Skip to content

Commit

Permalink
feat: add support for multiple vehicles (#425)
Browse files Browse the repository at this point in the history
* add two new field to agreement

These fileds indicate whether an user has an agreement of a particular
type. This change is done to support the upcoming agreement change which
allow for a user to either use bocken, the electric bike *hornet* or
both.

* add validation logic to journalentry

A user which has an agreement type which does not match that of bocken
cannot create a journal entry, if not checked a person with only a
agreement that applies to hornet could add entries which would be
incorrect.

* translations

* wip: add new model+admin

made migration to initiliaze data and such but no real work in replacing
the logic in journal entries and such

* wip: "works"

need to test and further fix the front-end so that the automatically
filled trips are fixed

* wip: update template

Added code for fetching the latest status of all of the vehicles
dynamically so that it mimics previous behaviour with only a single
vehicle

* wip: add main group and vehicle to report form

Show the main group and the vehicle driven for the reports so that it is
"easier" to collect statistics.

* wip: remove debug print

* wip: docs and add todos

:shrug:

* wip: added validation logic to form and add tests

Updated tests for journalentryform to use vehicle, also added tests
which check if the user can or can not drive a particular vehicle etc.

* wip: missed a file :)

* wip: typo

* wip: translations

* wip: translations

* changed form autofill logic for vehicles

Now fetches the attribute based on the vehicle id which is stored in the
DOM, technically has support for unlimited vehicles but might not as
fool proof.

* add base to parseInt + docs

* fix: init meter with no entries

If there were no entries the code didn't fetch the meter stats from the
vehicle objects, now it does :)

* fix: add listener for meter start on dom load

* fix: lint

:smile_cat:
  • Loading branch information
sgronlund authored Oct 15, 2024
1 parent 1d70ed0 commit c385e99
Show file tree
Hide file tree
Showing 18 changed files with 648 additions and 188 deletions.
4 changes: 3 additions & 1 deletion src/bocken/admin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from bocken.models import (
Admin, Agreement, JournalEntry, Report, JournalEntryGroup
Admin, Agreement, JournalEntry, Report, JournalEntryGroup, Vehicle
)
from .agreement_admin import AgreementAdmin
from .journal_entry_admin import JournalEntryAdmin
from .journal_entry_group_admin import JournalEntryGroupAdmin
from .report_admin import ReportAdmin
from .vehicle_admin import VehicleAdmin
from .user_admin import UserAdmin
from django.utils.translation import gettext_lazy as _
from django.contrib import admin
Expand All @@ -13,6 +14,7 @@

admin.site.register(Admin, UserAdmin)
admin.site.register(Agreement, AgreementAdmin)
admin.site.register(Vehicle, VehicleAdmin)
admin.site.register(JournalEntry, JournalEntryAdmin)
admin.site.register(Report, ReportAdmin)
admin.site.register(JournalEntryGroup, JournalEntryGroupAdmin)
Expand Down
1 change: 1 addition & 0 deletions src/bocken/admin/agreement_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class AgreementAdmin(ImportExportModelAdmin):
form = AgreementForm
list_display = (
'name', 'personnummer', 'phonenumber',
'bike_agreement', 'car_agreement',
'email', 'expires_colored'
)
search_fields = ['name', 'personnummer']
4 changes: 2 additions & 2 deletions src/bocken/admin/journal_entry_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ class JournalEntryAdmin(ModelAdmin):
"""Custom class for the admin pages for journal entry."""

list_display = (
'agreement', 'created', 'group',
'agreement', 'created', 'group', 'vehicle',
'meter_start_gap_marker', 'meter_stop_gap_marker', 'get_total_distance'
)
ordering = ('-meter_stop', )
ordering = ('vehicle', '-meter_stop', )
search_fields = [
'agreement__name', 'agreement__personnummer', 'group__name'
]
Expand Down
15 changes: 15 additions & 0 deletions src/bocken/admin/vehicle_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

from django.contrib.admin import ModelAdmin


class VehicleAdmin(ModelAdmin):
"""Custom class for the admin pages for Vehicle."""

list_display = (
"vehicle_name",
"car",
"bike",
"vehicle_meter_start",
"vehicle_meter_stop"
)
list_filter = ['vehicle_name']
3 changes: 2 additions & 1 deletion src/bocken/forms/agreement_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ class Meta:
model = Agreement
fields = [
'name', 'personnummer', 'phonenumber',
'email', 'agreement_file', 'expires'
'email', 'bike_agreement', 'car_agreement',
'agreement_file', 'expires'
]
80 changes: 66 additions & 14 deletions src/bocken/forms/journal_entry_form.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from bocken.models.agreement import Agreement
from bocken.models.journal_entry import JournalEntry
from bocken.models.vehicle import Vehicle
from ..validators import validate_personnummer
from ..utils import format_personnummer
from ..widgets import TwoLevelSelect
Expand All @@ -26,7 +27,7 @@ class JournalEntryForm(ModelForm):

confirm = BooleanField(
required=True,
label=_("I confirm that Bocken is clean and in good shape"),
label=_("I confirm that the vehicle is clean and in good shape"),
widget=CheckboxInput(attrs={'class': 'h-8 w-8'})
)

Expand All @@ -35,7 +36,7 @@ class JournalEntryForm(ModelForm):
class Meta:
model = JournalEntry
fields = [
'personnummer', 'group', 'meter_start', 'meter_stop'
'personnummer', 'vehicle', 'group', 'meter_start', 'meter_stop'
]
widgets = {
"meter_start": TextInput(
Expand All @@ -57,6 +58,7 @@ class Meta:
'group': 'users',
'meter_start': 'play-circle',
'meter_stop': 'stop-circle',
'vehicle': 'truck'
}
help_texts = {
'group': _(
Expand All @@ -68,20 +70,43 @@ class Meta:
"latest entry. If the number is not correct, enter the value "
"that the meter had when you started driving. Also inform the "
"head of the pub crew about this."
),
'vehicle': _(
"Choose the type of vehicle you have driven."
)
}

def __init__(self, *args, **kwargs):
super(JournalEntryForm, self).__init__(*args, **kwargs)
# Set the initial value for the meter start to the stop value of the
# last entry since it most likely is the value of the meter when a
# person starts driving.
latest_entry = JournalEntry.get_latest_entry()
if latest_entry:
# last entry based on the current vehicle choice since it most
# likely is the value of the meter when a person starts driving.
all_vehicles = Vehicle.objects.all()
latest_entries = [
JournalEntry.get_latest_entry(x)
for x in all_vehicles
if JournalEntry.get_latest_entry(x) is not None
]
if latest_entries:
latest_entry = latest_entries[0]
self.initial = {
'meter_start': latest_entry.meter_stop
'meter_start': latest_entry.meter_stop,
}

# This stores all of the latest registered trips for vehicle.
# By doing this we can hence "support" any amount of vehicle
# and fetch their latest trip to automatically set as
# a value for when a user is registering a new journal entry
for item in latest_entries:
self.initial[
f'meter_start_{str(item.vehicle.id)}'
] = item.meter_stop
else:
# if there is not a latest entry, then fetch from vehicle objects
# ideally it should always be fetched from here but..?
for vehicle in all_vehicles:
self.initial[
f'meter_start_{str(vehicle.id)}'
] = vehicle.vehicle_meter_stop
# If there is data from the previous form (a.k.a. invalid data
# was passed) we need to add some of that data to the TwoLevelSelect
# widget so that it can automatically choose a default option.
Expand All @@ -103,7 +128,8 @@ def clean_personnummer(self):

def clean_meter_start(self):
"""Meter start must be larger than the meter stop in the last entry."""
latest_entry = JournalEntry.get_latest_entry()
form_vehicle = self.cleaned_data['vehicle']
latest_entry = JournalEntry.get_latest_entry(form_vehicle)
if latest_entry:
if latest_entry.meter_stop > self.cleaned_data['meter_start']:
raise ValidationError(_(
Expand All @@ -121,26 +147,52 @@ def clean(self): # noqa
# we don't need to add an error message that a user does not
# have a written agreement.
person_nummer = self.cleaned_data.get('personnummer')
veh = self.cleaned_data.get('vehicle')
if person_nummer:
try:
agreement = Agreement.objects.get(
personnummer=person_nummer
)
can_use_car = agreement.car_agreement
can_use_bike = agreement.bike_agreement
if veh.car:
if not can_use_car:
self.add_error('vehicle', _(
"You don't have a written agreement which you "
"must have to drive a car. Contact the head of "
"the pub crew and send a copy of the details "
"you wrote inte the fields below."
))
else:
if not can_use_bike:
self.add_error('vehicle', _(
"You don't have a written agreement which you "
"must have to drive a bike. Contact the head of "
"the pub crew and send a copy of the details "
"you wrote inte the fields below."
))

self.instance.agreement = agreement
except Agreement.DoesNotExist:
self.add_error('personnummer', _(
"You don't have a written agreement which you must have "
"to drive bocken. Contact the head of the pub crew and "
"send a copy of the details you wrote into the fields "
"below."
"You don't have a written agreement which you "
"must have to drive a vehicle. Contact the head of "
"the pub crew and send a copy of the details "
"you wrote inte the fields below."
))

# Make sure meter stop is larger than meter start
meter_start = cleaned_data.get('meter_start', 0)
meter_stop = cleaned_data['meter_stop']
if cleaned_data['meter_stop'] <= meter_start:
self.add_error('meter_stop', _(
"Trip meter at stop must be larger than the trip meter at "
"start"
))

else:
if veh:
Vehicle.objects.filter(id=veh.id).update(
vehicle_meter_start=meter_start,
vehicle_meter_stop=meter_stop
)
return cleaned_data
Loading

0 comments on commit c385e99

Please sign in to comment.