Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# Supported languages
LANGUAGES = {
'en': 'English',
'cs': 'Čeština',
'de': 'Deutsch',
'es': 'Español',
'fr': 'Français',
Expand Down
3 changes: 2 additions & 1 deletion app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class User(UserMixin, db.Model):

# Menu preferences
start_page = db.Column(db.String(50), default='dashboard') # dashboard, vehicles, fuel, expenses, etc.
default_vehicle_id = db.Column(db.Integer, db.ForeignKey('vehicles.id'), nullable=True)
show_menu_vehicles = db.Column(db.Boolean, default=True)
show_menu_fuel = db.Column(db.Boolean, default=True)
show_menu_expenses = db.Column(db.Boolean, default=True)
Expand Down Expand Up @@ -1058,7 +1059,7 @@ class Trip(db.Model):

date = db.Column(db.Date, nullable=False, default=datetime.utcnow)
start_odometer = db.Column(db.Float, nullable=False)
end_odometer = db.Column(db.Float, nullable=False)
end_odometer = db.Column(db.Float, nullable=True)

purpose = db.Column(db.String(20), nullable=False) # business, personal, commute, etc.
description = db.Column(db.String(200))
Expand Down
12 changes: 7 additions & 5 deletions app/routes/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2389,7 +2389,9 @@ def import_clarkson():
# Parse CSV-like values (handling quoted strings)
values = parse_sql_values(values_str)
if len(values) >= 9:
clarkson_id = int(values[0]) if values[0] else None
# Clarkson may use integer IDs or UUIDs — store as string key
raw_id = clean_sql_string(values[0]) if values[0] else None
clarkson_id = raw_id
name = clean_sql_string(values[2])
registration = clean_sql_string(values[3])
make = clean_sql_string(values[4])
Expand Down Expand Up @@ -2419,12 +2421,12 @@ def import_clarkson():
except (ValueError, IndexError) as e:
continue

# Find Fuel INSERT statements
# Find Fuel INSERT statements — table may be named `Fuel` or `Fuelups`
# Clarkson format: INSERT INTO `Fuel` VALUES (id, vehicleId, fuelAmount, fuelUnitCost, totalCost, odometerReading, dateTime, fullTank, missedFillUp, userId, fuelUnit, location, lat, lng)
fuel_pattern = r"INSERT INTO [`']?Fuel[`']?\s+(?:VALUES\s*)?\(([^)]+)\)"
fuel_pattern = r"INSERT INTO [`']?(?:Fuel|Fuelups)[`']?\s+(?:VALUES\s*)?\(([^)]+)\)"
fuel_matches = re.findall(fuel_pattern, content, re.IGNORECASE)

fuel_multi_pattern = r"INSERT INTO [`']?Fuel[`']?[^;]*VALUES\s*((?:\([^)]+\)\s*,?\s*)+)"
fuel_multi_pattern = r"INSERT INTO [`']?(?:Fuel|Fuelups)[`']?[^;]*VALUES\s*((?:\([^)]+\)\s*,?\s*)+)"
fuel_multi_matches = re.findall(fuel_multi_pattern, content, re.IGNORECASE)

for match in fuel_multi_matches:
Expand All @@ -2435,7 +2437,7 @@ def import_clarkson():
try:
values = parse_sql_values(values_str)
if len(values) >= 10:
clarkson_vehicle_id = int(values[1]) if values[1] else None
clarkson_vehicle_id = clean_sql_string(values[1]) if values[1] else None
vehicle_id = vehicle_id_map.get(clarkson_vehicle_id)
if not vehicle_id:
continue
Expand Down
2 changes: 2 additions & 0 deletions app/routes/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ def notifications():
def menu_preferences():
"""Update user menu preferences"""
current_user.start_page = request.form.get('start_page', 'dashboard')
default_vehicle_id = request.form.get('default_vehicle_id')
current_user.default_vehicle_id = int(default_vehicle_id) if default_vehicle_id else None
current_user.show_menu_vehicles = request.form.get('show_menu_vehicles') == 'on'
current_user.show_menu_fuel = request.form.get('show_menu_fuel') == 'on'
current_user.show_menu_expenses = request.form.get('show_menu_expenses') == 'on'
Expand Down
2 changes: 1 addition & 1 deletion app/routes/expenses.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def new():
return redirect(url_for('vehicles.view', vehicle_id=vehicle_id))

# Pre-select vehicle if provided
selected_vehicle_id = request.args.get('vehicle_id', type=int)
selected_vehicle_id = request.args.get('vehicle_id', type=int) or current_user.default_vehicle_id

return render_template('expenses/form.html',
expense=None,
Expand Down
9 changes: 7 additions & 2 deletions app/routes/fuel.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ def new():
flash(_('Fuel log added successfully'), 'success')
return redirect(url_for('vehicles.view', vehicle_id=vehicle_id))

# Pre-select vehicle if provided
selected_vehicle_id = request.args.get('vehicle_id', type=int)
# Pre-select vehicle: explicit param > default vehicle preference
selected_vehicle_id = request.args.get('vehicle_id', type=int) or current_user.default_vehicle_id

# Get all fuel stations for dropdown (stations are system-wide)
stations = FuelStation.query.order_by(
Expand All @@ -161,6 +161,11 @@ def edit(log_id):
return redirect(url_for('fuel.index'))

if request.method == 'POST':
new_vehicle_id = request.form.get('vehicle_id', type=int)
if new_vehicle_id:
new_vehicle = Vehicle.query.get_or_404(new_vehicle_id)
if new_vehicle in vehicles:
log.vehicle_id = new_vehicle_id
date_str = request.form.get('date')
log.date = datetime.strptime(date_str, '%Y-%m-%d').date() if date_str else log.date
log.odometer = float(request.form.get('odometer'))
Expand Down
6 changes: 3 additions & 3 deletions app/routes/trips.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def new():
user_id=current_user.id,
date=date,
start_odometer=float(request.form.get('start_odometer')),
end_odometer=float(request.form.get('end_odometer')),
end_odometer=float(request.form.get('end_odometer')) if request.form.get('end_odometer') else None,
purpose=request.form.get('purpose'),
description=request.form.get('description'),
start_location=request.form.get('start_location'),
Expand All @@ -96,7 +96,7 @@ def new():
return redirect(url_for('trips.index'))

# Pre-select vehicle if provided
selected_vehicle_id = request.args.get('vehicle_id', type=int)
selected_vehicle_id = request.args.get('vehicle_id', type=int) or current_user.default_vehicle_id

# Get last odometer for selected vehicle
last_odometer = 0
Expand Down Expand Up @@ -130,7 +130,7 @@ def edit(trip_id):
date_str = request.form.get('date')
trip.date = datetime.strptime(date_str, '%Y-%m-%d').date() if date_str else trip.date
trip.start_odometer = float(request.form.get('start_odometer'))
trip.end_odometer = float(request.form.get('end_odometer'))
trip.end_odometer = float(request.form.get('end_odometer')) if request.form.get('end_odometer') else None
trip.purpose = request.form.get('purpose')
trip.description = request.form.get('description')
trip.start_location = request.form.get('start_location')
Expand Down
13 changes: 13 additions & 0 deletions app/templates/auth/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,19 @@ <h2 class="text-lg font-medium text-gray-900 dark:text-white">{{ _('Menu & Navig
</select>
</div>

<!-- Default Vehicle -->
<div class="border-t border-gray-200 dark:border-gray-700 pt-6">
<label for="default_vehicle_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('Default Vehicle') }}</label>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">{{ _('Pre-selected vehicle when adding fuel logs, expenses, trips, and charging sessions') }}</p>
<select name="default_vehicle_id" id="default_vehicle_id"
class="block w-full sm:w-64 rounded-md border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500">
<option value="">{{ _('No default') }}</option>
{% for vehicle in current_user.get_all_vehicles() %}
<option value="{{ vehicle.id }}" {% if current_user.default_vehicle_id == vehicle.id %}selected{% endif %}>{{ vehicle.name }}</option>
{% endfor %}
</select>
</div>

<!-- Quick Entry Button -->
<div class="flex items-center justify-between py-3 border-t border-gray-200 dark:border-gray-700 dark:border-gray-700">
<div>
Expand Down
2 changes: 1 addition & 1 deletion app/templates/fuel/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ <h1 class="text-2xl font-bold text-gray-900 dark:text-white mt-2">{% if log %}Ed

<div>
<label for="volume" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Volume ({{ current_user.volume_unit }})</label>
<input type="number" name="volume" id="volume" step="0.01"
<input type="number" name="volume" id="volume" step="0.001"
value="{{ log.volume if log else '' }}"
onchange="calculateTotal()"
class="mt-1 block w-full rounded-md border border-gray-300 dark:border-gray-600 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500">
Expand Down
2 changes: 1 addition & 1 deletion app/templates/fuel/quick.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ <h1 class="text-2xl font-bold text-gray-900 dark:text-white">{{ _('Quick Fuel En
<div>
<label for="volume" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('Volume') }}</label>
<div class="mt-1 relative">
<input type="number" name="volume" id="volume" step="0.01"
<input type="number" name="volume" id="volume" step="0.001"
class="block w-full rounded-lg border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white text-lg py-3 pr-8 focus:border-primary-500 focus:ring-primary-500">
<span class="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 text-sm">{{ current_user.volume_unit }}</span>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/templates/trips/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ <h1 class="text-2xl font-bold text-gray-900 dark:text-white mt-2">{% if trip %}{
</div>

<div>
<label for="end_odometer" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('End Odometer') }} ({{ current_user.distance_unit }}) *</label>
<input type="number" name="end_odometer" id="end_odometer" required step="0.1"
<label for="end_odometer" class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ _('End Odometer') }} ({{ current_user.distance_unit }})</label>
<input type="number" name="end_odometer" id="end_odometer" step="0.1"
value="{{ trip.end_odometer if trip else '' }}"
onchange="calculateDistance()"
class="mt-1 block w-full rounded-md border border-gray-300 dark:border-gray-600 px-3 py-2 bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500">
Expand Down
Loading
Loading