diff --git a/app/entities.py b/app/entities.py index b7d04ee..cd2def0 100644 --- a/app/entities.py +++ b/app/entities.py @@ -82,6 +82,7 @@ class Vehicle(db.Model): services = db.relationship("Service", order_by="asc(Service.odometer)") regulars = db.relationship("RegularCost") consumables = db.relationship("Consumable", secondary=vehicles_consumables) + is_active = db.Column(db.Boolean(), default=True) # allow vehicle names to be duplicated between different owners but must still be uniq for each owner __table_args__ = (db.UniqueConstraint("owner_id", "name", name="_owner_name_uniq"),) diff --git a/app/forms/vehicle.py b/app/forms/vehicle.py index 4e42080..bad804e 100644 --- a/app/forms/vehicle.py +++ b/app/forms/vehicle.py @@ -1,20 +1,25 @@ from flask_wtf import FlaskForm -from wtforms import StringField, SubmitField, SelectField, SelectMultipleField +from wtforms import ( + StringField, + SubmitField, + SelectField, + SelectMultipleField, + BooleanField, +) from wtforms.validators import Length class SelectVehicleForm(FlaskForm): - vehicle = SelectField('Vehicle', coerce=int) - submit = SubmitField(label='Do it!') + vehicle = SelectField("Vehicle", coerce=int) + submit = SubmitField(label="Do it!") class EditVehicleForm(FlaskForm): - name = StringField('Name', validators=[Length(1, 255)]) - consumables = SelectMultipleField('Consumables', coerce=int,validators=[]) - submit = SubmitField(label='Do it!') + name = StringField("Name", validators=[Length(1, 255)]) + consumables = SelectMultipleField("Consumables", coerce=int, validators=[]) + is_active = BooleanField("Is active") + submit = SubmitField(label="Do it!") class DeleteVehicleForm(FlaskForm): - submit = SubmitField(label='Do it!') - - + submit = SubmitField(label="Do it!") diff --git a/app/routes/account.py b/app/routes/account.py index 89f812d..1bc4fdf 100644 --- a/app/routes/account.py +++ b/app/routes/account.py @@ -10,25 +10,27 @@ from ..tools import db_log_update, db_log_delete, db_log_add from .. import app, db, user_datastore -@app.route('/account', methods=['GET']) +@app.route("/account", methods=["GET"]) @login_required def get_account_page(): stations = [x.as_dict() for x in current_user.favourite_filling_stations] for station in stations: - station['state'] = 'favourite' - return render_template('account.html', - map_pos=(current_user.home_lat, current_user.home_long, current_user.home_zoom), - fs=json.dumps(stations)) + station["state"] = "favourite" + return render_template( + "account.html", + map_pos=(current_user.home_lat, current_user.home_long, current_user.home_zoom), + fs=json.dumps(stations), + ) -@app.route('/account/vehicle/edit/', methods=['GET', 'POST']) +@app.route("/account/vehicle/edit/", methods=["GET", "POST"]) @login_required def edit_vehicle(vid): vehicle = Vehicle.query.filter(Vehicle.id == vid).first() # prevent edit of foreign vehicles if vehicle not in current_user.vehicles: - return redirect(url_for('get_account_page')) + return redirect(url_for("get_account_page")) form = EditVehicleForm() form.consumables.choices = [(g.id, g.name) for g in Consumable.query.all()] @@ -39,8 +41,12 @@ def edit_vehicle(vid): if form.name.data is not None: form.name.default = form.name.data + if form.is_active.data is not None: + form.is_active.default = form.is_active.data + if form.validate_on_submit(): vehicle.name = form.name.data + vehicle.is_active = form.is_active.data # we cannot delete consumables where there are pitstops for => report error vehicle.consumables = [] for consumable_id in form.consumables.data: @@ -53,25 +59,26 @@ def edit_vehicle(vid): except IntegrityError: db.session.rollback() form.name.errors.append('"%s" is not unique.' % (form.name.data)) - return render_template('editVehicleForm.html', form=form) - return redirect(url_for('get_account_page')) + return render_template("editVehicleForm.html", form=form) + return redirect(url_for("get_account_page")) form.name.default = vehicle.name + form.is_active.default = vehicle.is_active form.process() - return render_template('editVehicleForm.html', form=form, vehicle=vehicle) + return render_template("editVehicleForm.html", form=form, vehicle=vehicle) -@app.route('/account/vehicle/delete/', methods=['GET', 'POST']) +@app.route("/account/vehicle/delete/", methods=["GET", "POST"]) @login_required def delete_vehicle(vid): vehicle = Vehicle.query.filter(Vehicle.id == vid).first() # prevent deletion of foreign vehicles if vehicle not in current_user.vehicles: - return redirect(url_for('get_account_page')) + return redirect(url_for("get_account_page")) if len(current_user.vehicles) == 1: - return redirect(url_for('get_account_page')) + return redirect(url_for("get_account_page")) form = DeleteVehicleForm() @@ -79,12 +86,12 @@ def delete_vehicle(vid): db.session.delete(vehicle) db.session.commit() db_log_delete(vehicle) - return redirect(url_for('get_account_page')) + return redirect(url_for("get_account_page")) - return render_template('deleteVehicleForm.html', form=form, vehicle=vehicle) + return render_template("deleteVehicleForm.html", form=form, vehicle=vehicle) -@app.route('/account/vehicle/create', methods=['GET', 'POST']) +@app.route("/account/vehicle/create", methods=["GET", "POST"]) @login_required def create_vehicle(): form = EditVehicleForm() @@ -100,8 +107,8 @@ def create_vehicle(): if form.validate_on_submit(): if len(form.consumables.data) == 0: - form.consumables.errors.append('At least one consumable must be selected.') - return render_template('createVehicleForm.html', form=form) + form.consumables.errors.append("At least one consumable must be selected.") + return render_template("createVehicleForm.html", form=form) vehicle_name = form.name.data new_vehicle = Vehicle(vehicle_name) @@ -117,13 +124,13 @@ def create_vehicle(): except IntegrityError: db.session.rollback() form.name.errors.append('"%s" is not unique.' % (form.name.data)) - return render_template('createVehicleForm.html', form=form) - return redirect(url_for('get_account_page')) + return render_template("createVehicleForm.html", form=form) + return redirect(url_for("get_account_page")) - return render_template('createVehicleForm.html', form=form) + return render_template("createVehicleForm.html", form=form) -@app.route('/account/delete', methods=['GET', 'POST']) +@app.route("/account/delete", methods=["GET", "POST"]) @login_required def delete_account(): form = DeleteAccountForm() @@ -131,25 +138,28 @@ def delete_account(): if form.validate_on_submit(): user_datastore.delete_user(current_user) db.session.commit() - return redirect(url_for('index')) + return redirect(url_for("index")) - return render_template('deleteAccountForm.html', form=form) + return render_template("deleteAccountForm.html", form=form) -@app.route('/account/home', methods=['GET']) +@app.route("/account/home", methods=["GET"]) @login_required def get_users_home(): return jsonify( - {'lat': float(current_user.home_lat), 'long': float(current_user.home_long), 'zoom': current_user.home_zoom}) + { + "lat": float(current_user.home_lat), + "long": float(current_user.home_long), + "zoom": current_user.home_zoom, + } + ) -@app.route('/account/home', methods=['POST']) +@app.route("/account/home", methods=["POST"]) @login_required def set_users_home(): - current_user.home_lat = request.json['lat'] - current_user.home_long = request.json['long'] - current_user.home_zoom = request.json['zoom'] + current_user.home_lat = request.json["lat"] + current_user.home_long = request.json["long"] + current_user.home_zoom = request.json["zoom"] db.session.commit() return jsonify({}) - - diff --git a/app/routes/misc.py b/app/routes/misc.py index 1aff06a..3b5141f 100644 --- a/app/routes/misc.py +++ b/app/routes/misc.py @@ -6,17 +6,17 @@ from ..tools import VehicleStats from .. import app -@app.route('/statistics', methods=['GET']) +@app.route("/statistics", methods=["GET"]) @login_required def get_statistics(): stats = [] - for vehicle in current_user.vehicles: + + def key(v): + return (not v.is_active, v.name) + + vehicles = sorted(current_user.vehicles, key=key) + for vehicle in vehicles: stats.append(VehicleStats(vehicle)) - return render_template('statistics.html', data=stats) + return render_template("statistics.html", data=stats) -@app.route('/manual', methods=['GET']) -@login_required -def get_manual(): - return render_template('manual.html') - diff --git a/app/routes/pitstop.py b/app/routes/pitstop.py index c9b8446..7113975 100644 --- a/app/routes/pitstop.py +++ b/app/routes/pitstop.py @@ -7,70 +7,101 @@ import types from ..entities import Vehicle, Consumable, Pitstop -from ..forms import SelectVehicleForm, SelectConsumableForm, \ - CreatePitstopForm, EditPitstopForm, DeletePitStopForm -from ..tools import db_log_update, db_log_delete, db_log_add, \ - pitstop_service_key, get_event_line_for_vehicle, \ - update_filling_station_prices, RegularCostInstance, \ - calculate_regular_cost_instances +from ..forms import ( + SelectVehicleForm, + SelectConsumableForm, + CreatePitstopForm, + EditPitstopForm, + DeletePitStopForm, +) +from ..tools import ( + db_log_update, + db_log_delete, + db_log_add, + pitstop_service_key, + get_event_line_for_vehicle, + update_filling_station_prices, + RegularCostInstance, + calculate_regular_cost_instances, + get_users_active_vehicle, +) from .. import app, db -@app.route('/pitstops/vehicle/select', methods=['GET', 'POST']) +@app.route("/pitstops/vehicle/select", methods=["GET", "POST"]) @login_required def select_vehicle_for_new_pitstop(): - if len(current_user.vehicles) == 1: - return redirect(url_for('select_consumable_for_new_pitstop', vid=current_user.vehicles[0].id)) + active_vehicles = get_users_active_vehicle(current_user) + if len(active_vehicles) == 1: + return redirect( + url_for( + "select_consumable_for_new_pitstop", vid=active_vehicles[0].id + ) + ) form = SelectVehicleForm() - form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] + form.vehicle.choices = [ + (g.id, g.name) for g in active_vehicles + ] if form.validate_on_submit(): - return redirect(url_for('select_consumable_for_new_pitstop', vid=form.vehicle.data)) + return redirect( + url_for("select_consumable_for_new_pitstop", vid=form.vehicle.data) + ) - return render_template('selectVehicle.html', form=form) + return render_template("selectVehicle.html", form=form) -@app.route('/pitstops/vehicle//consumable/select', methods=['GET', 'POST']) +@app.route("/pitstops/vehicle//consumable/select", methods=["GET", "POST"]) @login_required def select_consumable_for_new_pitstop(vid): vehicle = Vehicle.query.get(vid) if vehicle is None or vehicle not in current_user.vehicles: - return redirect(url_for('select_vehicle_for_new_pitstop')) + return redirect(url_for("select_vehicle_for_new_pitstop")) if len(vehicle.consumables) == 0: - flash('Please choose at least one consumable!', 'warning') - return redirect(url_for('edit_vehicle', vid=vid)) + flash("Please choose at least one consumable!", "warning") + return redirect(url_for("edit_vehicle", vid=vid)) if len(vehicle.consumables) == 1: - return redirect(url_for('create_pit_stop_form', vid=vid, cid=vehicle.consumables[0].id)) + return redirect( + url_for("create_pit_stop_form", vid=vid, cid=vehicle.consumables[0].id) + ) form = SelectConsumableForm() form.consumable.choices = [(g.id, g.name) for g in vehicle.consumables] if form.validate_on_submit(): - return redirect(url_for('create_pit_stop_form', vid=vid, cid=form.consumable.data)) + return redirect( + url_for("create_pit_stop_form", vid=vid, cid=form.consumable.data) + ) - return render_template('selectConsumableForVehicle.html', vehicle=vehicle, form=form) + return render_template( + "selectConsumableForVehicle.html", vehicle=vehicle, form=form + ) -@app.route('/pitstops/vehicle//consumable//create', methods=['GET', 'POST']) +@app.route( + "/pitstops/vehicle//consumable//create", methods=["GET", "POST"] +) @login_required def create_pit_stop_form(vid, cid): vehicle = Vehicle.query.get(vid) if vehicle is None or vehicle not in current_user.vehicles: - return redirect(url_for('select_vehicle_for_new_pitstop')) + return redirect(url_for("select_vehicle_for_new_pitstop")) consumable = Consumable.query.get(cid) if consumable not in vehicle.consumables: - return redirect(url_for('select_consumable_for_new_pitstop', vid=vid)) + return redirect(url_for("select_consumable_for_new_pitstop", vid=vid)) form = CreatePitstopForm() data = get_event_line_for_vehicle(vehicle) if len(data) > 0: form.set_pitstops(data) - form.same_odometer_allowed = (type(data[-1]) != Pitstop) or (data[-1].consumable.id != cid) + form.same_odometer_allowed = (type(data[-1]) != Pitstop) or ( + data[-1].consumable.id != cid + ) else: form.set_pitstops([]) form.same_odometer_allowed = True @@ -85,7 +116,9 @@ def create_pit_stop_form(vid, cid): # Validate should accept same odometer on different consumables # if form.validate_on_submit(): - new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data, form.costs.data, cid) + new_stop = Pitstop( + form.odometer.data, form.litres.data, form.date.data, form.costs.data, cid + ) db.session.add(new_stop) vehicle.pitstops.append(new_stop) try: @@ -93,44 +126,57 @@ def create_pit_stop_form(vid, cid): db_log_add(new_stop) except IntegrityError: db.session.rollback() - form.odometer.errors.append('Pitstop already present for %s at odometer %s km!' % (consumable.name, form.odometer.data)) - return render_template('createPitStopForm.html', form=form, vehicle=vehicle, messages=form.get_hint_messages()) - return redirect(url_for('get_pit_stops', _anchor= 'v' + str(vehicle.id))) + form.odometer.errors.append( + "Pitstop already present for %s at odometer %s km!" + % (consumable.name, form.odometer.data) + ) + return render_template( + "createPitStopForm.html", + form=form, + vehicle=vehicle, + messages=form.get_hint_messages(), + ) + return redirect(url_for("get_pit_stops", _anchor="v" + str(vehicle.id))) form.process() - return render_template('createPitStopForm.html', form=form, vehicle=vehicle, messages=form.get_hint_messages()) + return render_template( + "createPitStopForm.html", + form=form, + vehicle=vehicle, + messages=form.get_hint_messages(), + ) -@app.route('/pitstops/delete/', methods=['GET', 'POST']) +@app.route("/pitstops/delete/", methods=["GET", "POST"]) @login_required def delete_pit_stop_form(pid): pitstop = Pitstop.query.filter(Pitstop.id == pid).first() if pitstop is None: - return redirect(url_for('get_pit_stops')) + return redirect(url_for("get_pit_stops")) vehicle = Vehicle.query.filter(Vehicle.id == pitstop.vehicle_id).first() if vehicle not in current_user.vehicles: - return redirect(url_for('get_pit_stops')) + return redirect(url_for("get_pit_stops")) form = DeletePitStopForm() if form.validate_on_submit(): db.session.delete(pitstop) db.session.commit() db_log_delete(pitstop) - return redirect(url_for('get_pit_stops', _anchor='v' + str(vehicle.id))) + return redirect(url_for("get_pit_stops", _anchor="v" + str(vehicle.id))) - return render_template('deletePitstopForm.html', form=form, pitstop=pitstop ) + return render_template("deletePitstopForm.html", form=form, pitstop=pitstop) -@app.route('/pitstops/edit/', methods=['GET', 'POST']) +@app.route("/pitstops/edit/", methods=["GET", "POST"]) @login_required def edit_pit_stop_form(pid): edit_pitstop = Pitstop.query.get(pid) if edit_pitstop is None: - return redirect(url_for('get_pit_stops')) + return redirect(url_for("get_pit_stops")) vehicle = Vehicle.query.filter(Vehicle.id == edit_pitstop.vehicle_id).first() if vehicle not in current_user.vehicles: - return redirect(url_for('get_pit_stops')) + return redirect(url_for("get_pit_stops")) form = EditPitstopForm() data = get_event_line_for_vehicle(vehicle) @@ -148,19 +194,22 @@ def edit_pit_stop_form(pid): edit_pitstop.odometer = form.odometer.data db.session.commit() db_log_update(edit_pitstop) - return redirect(url_for('get_pit_stops', _anchor='v' + str(vehicle.id))) + return redirect(url_for("get_pit_stops", _anchor="v" + str(vehicle.id))) form.preinit_with_data() form.process() - return render_template('editPitStopForm.html', form=form, vehicle=vehicle, messages=form.get_hint_messages()) + return render_template( + "editPitStopForm.html", + form=form, + vehicle=vehicle, + messages=form.get_hint_messages(), + ) -@app.route('/pitstops', methods=['GET']) +@app.route("/pitstops", methods=["GET"]) @login_required def get_pit_stops(): - user = { - 'vehicles': [] - } + user = {"vehicles": []} for vehicle in current_user.vehicles: data = [] for pitstop in vehicle.pitstops: @@ -172,71 +221,94 @@ def get_pit_stops(): data.sort(key=pitstop_service_key) v = { - 'id': vehicle.id, - 'name': vehicle.name, - 'data': data, + "id": vehicle.id, + "name": vehicle.name, + "data": data, "regulars": vehicle.regulars, } - user['vehicles'].append(v) + user["vehicles"].append(v) - return render_template('pitstops.html', user=user) + return render_template("pitstops.html", user=user) -@app.route('/pitstops/plan/vehicle/select', methods=['GET', 'POST']) +@app.route("/pitstops/plan/vehicle/select", methods=["GET", "POST"]) @login_required def select_vehicle_for_plan_pitstop(): if len(current_user.vehicles) == 1: - return redirect(url_for('select_consumable_for_plan_pitstop', vid=current_user.vehicles[0].id)) + return redirect( + url_for( + "select_consumable_for_plan_pitstop", vid=current_user.vehicles[0].id + ) + ) form = SelectVehicleForm() form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] if form.validate_on_submit(): - return redirect(url_for('select_consumable_for_plan_pitstop', vid=form.vehicle.data)) + return redirect( + url_for("select_consumable_for_plan_pitstop", vid=form.vehicle.data) + ) - return render_template('selectVehicle.html', form=form) + return render_template("selectVehicle.html", form=form) -@app.route('/pitstops/plan/vehicle//consumable/select', methods=['GET', 'POST']) +@app.route( + "/pitstops/plan/vehicle//consumable/select", methods=["GET", "POST"] +) @login_required def select_consumable_for_plan_pitstop(vid): vehicle = Vehicle.query.get(vid) if vehicle is None or vehicle not in current_user.vehicles: - return redirect(url_for('select_consumable_for_plan_pitstop')) + return redirect(url_for("select_consumable_for_plan_pitstop")) if len(vehicle.consumables) == 0: - flash('Please choose at least one consumable!', 'warning') - return redirect(url_for('edit_vehicle', vid=vid)) + flash("Please choose at least one consumable!", "warning") + return redirect(url_for("edit_vehicle", vid=vid)) if len(vehicle.consumables) == 1: - return redirect(url_for('plan_pit_stop_form', vid=vid, cid=vehicle.consumables[0].id)) + return redirect( + url_for("plan_pit_stop_form", vid=vid, cid=vehicle.consumables[0].id) + ) form = SelectConsumableForm() form.consumable.choices = [(g.id, g.name) for g in vehicle.consumables] if form.validate_on_submit(): - return redirect(url_for('plan_pit_stop_form', vid=vid, cid=form.consumable.data)) + return redirect( + url_for("plan_pit_stop_form", vid=vid, cid=form.consumable.data) + ) - return render_template('selectConsumableForVehicle.html', vehicle=vehicle, form=form) + return render_template( + "selectConsumableForVehicle.html", vehicle=vehicle, form=form + ) -@app.route('/pitstops/plan/vehicle//consumable/', methods=['GET', 'POST']) +@app.route( + "/pitstops/plan/vehicle//consumable/", methods=["GET", "POST"] +) @login_required def plan_pit_stop_form(vid, cid): vehicle = Vehicle.query.get(vid) if vehicle is None or vehicle not in current_user.vehicles: - return redirect(url_for('select_vehicle_for_new_pitstop')) + return redirect(url_for("select_vehicle_for_new_pitstop")) consumable = Consumable.query.get(cid) if consumable not in vehicle.consumables: - return redirect(url_for('select_consumable_for_new_pitstop', vid=vid)) + return redirect(url_for("select_consumable_for_new_pitstop", vid=vid)) - update_filling_station_prices([x.id for x in current_user.favourite_filling_stations]) + update_filling_station_prices( + [x.id for x in current_user.favourite_filling_stations] + ) offers = [] for fs in current_user.favourite_filling_stations: - offers.append((fs, getattr(fs, consumable.ext_id),)) - - return render_template('planPitStopForm.html', vehicle=vehicle, consumable=consumable, offers=offers) - + offers.append( + ( + fs, + getattr(fs, consumable.ext_id), + ) + ) + return render_template( + "planPitStopForm.html", vehicle=vehicle, consumable=consumable, offers=offers + ) diff --git a/app/routes/regular_cost.py b/app/routes/regular_cost.py index e0d1b24..78a73ae 100644 --- a/app/routes/regular_cost.py +++ b/app/routes/regular_cost.py @@ -19,6 +19,7 @@ from ..tools import ( pitstop_service_key, get_event_line_for_vehicle, update_filling_station_prices, + get_users_active_vehicle, ) from .. import app, db @@ -48,13 +49,14 @@ def delete_regular_form(pid): @app.route("/regular_costs/vehicle/select", methods=["GET", "POST"]) @login_required def select_vehicle_for_new_regular_cost(): - if len(current_user.vehicles) == 1: + active_vehicles = get_users_active_vehicle(current_user) + if len(active_vehicles) == 1: return redirect( - url_for("create_regular_cost_for_vehicle", vid=current_user.vehicles[0].id) + url_for("create_regular_cost_for_vehicle", vid=active_vehicles[0].id) ) form = SelectVehicleForm() - form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] + form.vehicle.choices = [(g.id, g.name) for g in active_vehicles] if form.validate_on_submit(): return redirect( @@ -158,4 +160,3 @@ def end_regular_form(pid): form=form, vehicle=vehicle, ) - diff --git a/app/routes/service.py b/app/routes/service.py index 68b6800..16b65a3 100644 --- a/app/routes/service.py +++ b/app/routes/service.py @@ -3,17 +3,27 @@ from flask_security import login_required, current_user from datetime import date from ..entities import Vehicle, Service -from ..forms import CreateServiceForm, DeleteServiceForm, EditServiceForm, SelectVehicleForm -from ..tools import db_log_update, db_log_delete, get_event_line_for_vehicle, get_latest_pitstop_for_vehicle +from ..forms import ( + CreateServiceForm, + DeleteServiceForm, + EditServiceForm, + SelectVehicleForm, +) +from ..tools import ( + db_log_update, + db_log_delete, + get_event_line_for_vehicle, + get_latest_pitstop_for_vehicle, +) from .. import app, db -@app.route('/service/vehicle//create', methods=['GET', 'POST']) +@app.route("/service/vehicle//create", methods=["GET", "POST"]) @login_required def create_service_for_vehicle(vid): vehicle = Vehicle.query.get(vid) if vehicle is None or vehicle not in current_user.vehicles: - return redirect(url_for('get_account_page')) + return redirect(url_for("get_account_page")) form = CreateServiceForm() @@ -28,47 +38,55 @@ def create_service_for_vehicle(vid): form.preinit_with_data() if form.validate_on_submit(): - new_service = Service(form.date.data, form.odometer.data, vid, form.costs.data, form.description.data) + new_service = Service( + form.date.data, + form.odometer.data, + vid, + form.costs.data, + form.description.data, + ) db.session.add(new_service) vehicle.services.append(new_service) db.session.commit() - return redirect(url_for('get_pit_stops', _anchor='v' + str(vehicle.id))) + return redirect(url_for("get_pit_stops", _anchor="v" + str(vehicle.id))) form.process() - return render_template('createServiceForm.html', form=form, vehicle=vehicle, messages=[]) + return render_template( + "createServiceForm.html", form=form, vehicle=vehicle, messages=[] + ) -@app.route('/service/delete/', methods=['GET', 'POST']) +@app.route("/service/delete/", methods=["GET", "POST"]) @login_required def delete_service_form(sid): service = Service.query.filter(Service.id == sid).first() if service is None: - return redirect(url_for('get_pit_stops')) + return redirect(url_for("get_pit_stops")) vehicle = Vehicle.query.filter(Vehicle.id == service.vehicle_id).first() if vehicle not in current_user.vehicles: - return redirect(url_for('get_pit_stops')) + return redirect(url_for("get_pit_stops")) form = DeleteServiceForm() if form.validate_on_submit(): db.session.delete(service) db.session.commit() db_log_delete(service) - return redirect(url_for('get_pit_stops', _anchor='v' + str(vehicle.id))) + return redirect(url_for("get_pit_stops", _anchor="v" + str(vehicle.id))) - return render_template('deleteServiceForm.html', form=form, service=service ) + return render_template("deleteServiceForm.html", form=form, service=service) -@app.route('/service/edit/', methods=['GET', 'POST']) +@app.route("/service/edit/", methods=["GET", "POST"]) @login_required def edit_service_form(sid): edit_service = Service.query.get(sid) if edit_service is None: - return redirect(url_for('get_pit_stops')) + return redirect(url_for("get_pit_stops")) vehicle = Vehicle.query.filter(Vehicle.id == edit_service.vehicle_id).first() if vehicle not in current_user.vehicles: - return redirect(url_for('get_pit_stops')) + return redirect(url_for("get_pit_stops")) data = get_event_line_for_vehicle(vehicle) data = [x for x in data if x != edit_service] @@ -88,25 +106,31 @@ def edit_service_form(sid): edit_service.odometer = form.odometer.data db.session.commit() db_log_update(edit_service) - return redirect(url_for('get_pit_stops', _anchor='v' + str(vehicle.id))) + return redirect(url_for("get_pit_stops", _anchor="v" + str(vehicle.id))) form.preinit_with_data() form.process() - return render_template('editServiceForm.html', form=form, vehicle=vehicle, messages=form.get_hint_messages()) + return render_template( + "editServiceForm.html", + form=form, + vehicle=vehicle, + messages=form.get_hint_messages(), + ) -@app.route('/service/vehicle/select', methods=['GET', 'POST']) +@app.route("/service/vehicle/select", methods=["GET", "POST"]) @login_required def select_vehicle_for_new_service(): - if len(current_user.vehicles) == 1: - return redirect(url_for('create_service_for_vehicle', vid=current_user.vehicles[0].id)) + active_vehicles = get_users_active_vehicle(current_user) + if len(active_vehicles) == 1: + return redirect( + url_for("create_service_for_vehicle", vid=active_vehicles[0].id) + ) form = SelectVehicleForm() - form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles] + form.vehicle.choices = [(g.id, g.name) for g in active_vehicles] if form.validate_on_submit(): - return redirect(url_for('create_service_for_vehicle', vid=form.vehicle.data)) - - return render_template('selectVehicle.html', form=form) - + return redirect(url_for("create_service_for_vehicle", vid=form.vehicle.data)) + return render_template("selectVehicle.html", form=form) diff --git a/app/templates/account.html b/app/templates/account.html index 6549bf6..182865b 100644 --- a/app/templates/account.html +++ b/app/templates/account.html @@ -33,7 +33,10 @@ {% for vehicle in current_user.vehicles %} - {{ vehicle.name }} + {{ vehicle.name }}
+ {% if not vehicle.is_active %} + (inactive) + {% endif %} {{ vehicle.pitstops | length }} pitstops
diff --git a/app/templates/editVehicleForm.html b/app/templates/editVehicleForm.html index 78638c5..cd13f70 100644 --- a/app/templates/editVehicleForm.html +++ b/app/templates/editVehicleForm.html @@ -10,6 +10,7 @@ {{ form.hidden_tag() }} {{ render_field_with_errors(form.name) }} {{ render_field_with_errors(form.consumables) }} + {{ render_field_with_errors(form.is_active) }} {{ render_field_with_errors(form.submit) }} diff --git a/app/templates/layout.html b/app/templates/layout.html index dc6582e..663fbc4 100644 --- a/app/templates/layout.html +++ b/app/templates/layout.html @@ -1,11 +1,11 @@ {% macro navigation() -%} {% if current_user.email %} -
  • Plan Pitstop
  • Create Pitstop
  • Create Service
  • Create Regular Cost
  • Statistics
  • Account
  • +
  • Plan Pitstop
  • {% if current_user.has_role('admin') %}
  • Admin
  • {% endif %} @@ -45,7 +45,7 @@ {% endfor %} {% elif field.type == 'BooleanField' %} - + {% elif field.type == 'StringField' %} {% elif field.type == 'PasswordField' %} diff --git a/app/templates/manual.html b/app/templates/manual.html deleted file mode 100644 index a397e1a..0000000 --- a/app/templates/manual.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends "layout.html" %} - -{% macro line(header, cell) -%} - - - {{ header }} - - - {{ cell }} - - -{%- endmacro %} - -{% block body %} - -
    - - {{ line('Reifenluftdruck (vorne)', '1,75 - 2,0 Bar') }} - {{ line('Reifenluftdruck (hinten)', '2,0 - 2,25 Bar') }} - {{ line('Scheinwerfer', '12V 35/35W HS1-Halogen-Glühlampe') }} - {{ line('Standlicht', '12V 5W Glassockel 9mm') }} - {{ line('Blinker vorne', '12V 10W Stecksockel 15mm') }} - {{ line('Rück-/Bremslicht', '12V/21/5W Stecksockel 15mm') }} - {{ line('Blinker hinten', '12V 10W Stecksockel 15mm') }} - {{ line('Tankinhalt', 'ca. 6,0 L') }} - {{ line('Motoröl', 'SAE 15W40') }} - {{ line('Getriebeöl', 'SAE 80/90 (0,12/0,09)') }} -
    -
    - -{% endblock %} \ No newline at end of file diff --git a/app/tools.py b/app/tools.py index c7da89f..7936153 100644 --- a/app/tools.py +++ b/app/tools.py @@ -103,7 +103,9 @@ class VehicleStats: c.value = accumulated_costs if self.overall_distance > 0: - self.costs_per_distance = float(self.overall_costs) / (float(self.overall_distance) / 100) + self.costs_per_distance = float(self.overall_costs) / ( + float(self.overall_distance) / 100 + ) class StatsEvent: @@ -294,3 +296,14 @@ def calculate_regular_cost_instances(vehicle): ) data.append(r) return data + + +def get_users_active_vehicle(user): + def selector(vehicle): + if not vehicle.pitstops: + return date.today() + return vehicle.pitstops[-1].date + + active_vehicles = [g for g in user.vehicles if g.is_active] + active_vehicles.sort(key=selector, reverse=True) + return active_vehicles diff --git a/database_upgrades/upgrade_jody_to_kilian.sql b/database_upgrades/upgrade_jody_to_kilian.sql new file mode 100644 index 0000000..9311f2c --- /dev/null +++ b/database_upgrades/upgrade_jody_to_kilian.sql @@ -0,0 +1,2 @@ +ALTER TABLE `vehicle` ADD COLUMN `is_active` tinyint(1); +UPDATE `vehicle` SET `is_active` = 1;