Skip to content

Commit

Permalink
refactor(models): switch enums to django built-in type
Browse files Browse the repository at this point in the history
  • Loading branch information
zyv committed Nov 1, 2023
1 parent 73acbef commit 977fde4
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 73 deletions.
1 change: 1 addition & 0 deletions config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"logbook.context_processors.enums",
],
},
},
Expand Down
15 changes: 15 additions & 0 deletions logbook/context_processors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from .models import AircraftType, FunctionType, LaunchType, SpeedUnit
from .statistics.currency import CurrencyStatus


def enums(_):
return {
enum.__name__: enum
for enum in [
AircraftType,
FunctionType,
LaunchType,
SpeedUnit,
CurrencyStatus,
]
}
8 changes: 4 additions & 4 deletions logbook/management/commands/import_flightlog.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ def handle(self, *args, **options):

launch_type = (
{
"Self": models.LaunchType.SELF.name,
"Tow": models.LaunchType.TOW.name,
"Winch": models.LaunchType.WINCH.name,
"Self": models.LaunchType.SELF,
"Tow": models.LaunchType.TOW,
"Winch": models.LaunchType.WINCH,
}[remarks]
if aircraft.type == models.AircraftType.GLD.name
if aircraft.type == models.AircraftType.GLD
else ""
)

Expand Down
50 changes: 22 additions & 28 deletions logbook/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from datetime import UTC, datetime
from enum import Enum, StrEnum
from typing import Optional

from django.core.exceptions import ValidationError
Expand All @@ -10,32 +9,27 @@
from .statistics.currency import NinetyDaysCurrency, get_ninety_days_currency


class NameStrEnum(StrEnum):
def __str__(self) -> str:
return self.name
class AircraftType(models.TextChoices):
GLD = "GLD", "Glider"
TMG = "TMG", "Touring Motor Glider"
SEP = "SEP", "Single Engine Piston"


class AircraftType(Enum):
GLD = "Glider"
TMG = "Touring Motor Glider"
SEP = "Single Engine Piston"
class FunctionType(models.TextChoices):
PIC = "PIC", "Pilot-in-Command"
DUAL = "DUAL", "Dual instruction time"


class FunctionType(Enum):
PIC = "Pilot-in-Command"
DUAL = "Dual instruction time"
class LaunchType(models.TextChoices):
SELF = "SELF", "Self-launch"
WINCH = "WINCH", "Winch launch"
TOW = "TOW", "Aerotow"


class LaunchType(Enum):
SELF = "Self-launch"
WINCH = "Winch launch"
TOW = "Aerotow"


class SpeedUnit(NameStrEnum):
KMH = "km/h"
KT = "kt"
MPH = "mph"
class SpeedUnit(models.TextChoices):
KMH = "KMH", "km/h"
KT = "KT", "kt"
MPH = "MPH", "mph"


class Aerodrome(models.Model):
Expand All @@ -56,7 +50,7 @@ def __str__(self):


class Aircraft(models.Model):
type = models.CharField(max_length=3, choices=[(at.name, at.value) for at in AircraftType])
type = models.CharField(max_length=3, choices=AircraftType.choices)
maker = models.CharField(max_length=64)
model = models.CharField(max_length=64)

Expand All @@ -67,7 +61,7 @@ class Aircraft(models.Model):

currency_required = models.BooleanField(default=False)

speed_unit = models.CharField(max_length=3, choices=[(su.name, su.value) for su in SpeedUnit])
speed_unit = models.CharField(max_length=3, choices=SpeedUnit.choices)

v_r = models.PositiveSmallIntegerField(verbose_name="Vr", help_text="Rotation speed", blank=True, null=True)
v_y = models.PositiveSmallIntegerField(
Expand Down Expand Up @@ -118,12 +112,12 @@ class LogEntry(models.Model):

landings = models.PositiveSmallIntegerField(default=1)

time_function = models.CharField(max_length=5, choices=[(ft.name, ft.value) for ft in FunctionType])
time_function = models.CharField(max_length=5, choices=FunctionType.choices)

pilot = models.ForeignKey(Pilot, on_delete=models.PROTECT, related_name="pilot_set")
copilot = models.ForeignKey(Pilot, on_delete=models.PROTECT, related_name="copilot_set", blank=True, null=True)

launch_type = models.CharField(max_length=5, blank=True, choices=[(lt.name, lt.value) for lt in LaunchType])
launch_type = models.CharField(max_length=5, blank=True, choices=LaunchType.choices)

remarks = models.CharField(max_length=255, blank=True)

Expand All @@ -138,8 +132,8 @@ class Meta:
CheckConstraint(check=~Q(copilot=F("pilot")), name="copilot_not_pilot"),
CheckConstraint(
check=(
Q(time_function=FunctionType.PIC.name) # PIC time may be XC or not XC
| ~Q(time_function=FunctionType.PIC.name) & Q(cross_country=False) # non-PIC time must be non-XC
Q(time_function=FunctionType.PIC) # PIC time may be XC or not XC
| ~Q(time_function=FunctionType.PIC) & Q(cross_country=False) # non-PIC time must be non-XC
),
name="no_pic_no_xc",
),
Expand Down Expand Up @@ -167,7 +161,7 @@ def clean(self):
# Check constraints can't reference other tables; it's possible via UDFs, but not universally supported by RDBs
if (
self.aircraft_id is not None # checks if foreign key is set to avoid `RelatedObjectDoesNotExist` exception!
and self.aircraft.type == AircraftType.GLD.name
and self.aircraft.type == AircraftType.GLD
and not self.launch_type
):
raise ValidationError("Launch type is required for gliders!")
Expand Down
4 changes: 2 additions & 2 deletions logbook/statistics/currency.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from dataclasses import dataclass
from datetime import UTC, date, datetime, timedelta
from enum import StrEnum
from typing import TYPE_CHECKING

from django.db import models
from django.db.models import OuterRef, QuerySet, Subquery, Sum, Value

if TYPE_CHECKING:
from ..models import LogEntry


class CurrencyStatus(StrEnum):
class CurrencyStatus(models.TextChoices):
CURRENT = "🟢"
EXPIRING = "🟡"
NOT_CURRENT = "🔴"
Expand Down
8 changes: 4 additions & 4 deletions logbook/templates/logbook/logentry_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ <h1>Log entries</h1>
<th>Type<br>Registration</th>
<th>From<br>To</th>
<th>Departure<br>Arrival</th>
{% for aircraft_type in aircraft_types reversed %}
{% for aircraft_type in AircraftType reversed %}
<th class="text-center">{{ aircraft_type.name }}</th>
{% endfor %}
<th class="text-center">Landings</th>
<th>Name</th>
{% for function_type in function_types %}
{% for function_type in FunctionType %}
<th class="text-center">{{ function_type.name }}</th>
{% endfor %}
<th>Remarks</th>
Expand All @@ -34,7 +34,7 @@ <h1>Log entries</h1>
<td>{{ obj.aircraft.icao_designator }}<br>{{ obj.aircraft.registration }}</td>
<td>{{ obj.from_aerodrome.icao_code }}<br>{{ obj.to_aerodrome.icao_code }}</td>
<td>{{ obj.departure_time | time:"H:i" }}<br>{{ obj.arrival_time | time:"H:i" }}</td>
{% for aircraft_type in aircraft_types reversed %}
{% for aircraft_type in AircraftType reversed %}
<td class="text-center">
{% if obj.aircraft.type == aircraft_type.name %}
{{ obj.arrival_time | subtract:obj.departure_time | duration:"%h:%M" }}
Expand All @@ -45,7 +45,7 @@ <h1>Log entries</h1>
{% endfor %}
<td class="text-center">{{ obj.landings }}</td>
<td>{% if not obj.pilot.self %}{{ obj.pilot.first_name.0 }}. {{ obj.pilot.last_name }}{% else %}Self{% endif %}</td>
{% for function_type in function_types %}
{% for function_type in FunctionType %}
<td class="text-center">
{% if obj.time_function == function_type.name %}
{{ obj.arrival_time | subtract:obj.departure_time | duration:"%h:%M" }}
Expand Down
2 changes: 0 additions & 2 deletions logbook/views/aircraft.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from ..models import Aircraft
from ..statistics.currency import CurrencyStatus
from .utils import AuthenticatedListView


Expand All @@ -8,6 +7,5 @@ class AircraftIndexView(AuthenticatedListView):

def get_context_data(self, *, object_list=None, **kwargs):
return super().get_context_data(**kwargs) | {
"CurrencyStatus": CurrencyStatus.__members__,
"aircraft_fields": {field.name: field for field in Aircraft._meta.get_fields()},
}
24 changes: 10 additions & 14 deletions logbook/views/dashboard.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.db.models import QuerySet

from ..models import Aircraft, AircraftType, FunctionType, LogEntry
from ..statistics.currency import CURRENCY_REQUIRED_LANDINGS_NIGHT, CurrencyStatus, get_ninety_days_currency
from ..statistics.currency import CURRENCY_REQUIRED_LANDINGS_NIGHT, get_ninety_days_currency
from ..statistics.experience import compute_totals
from .utils import (
AuthenticatedListView,
Expand All @@ -14,25 +14,21 @@ class DashboardView(AuthenticatedListView):

def get_context_data(self, *args, **kwargs):
def totals_per_function(log_entries: QuerySet[LogEntry]):
return {
function.name: compute_totals(log_entries.filter(time_function=function.name))
for function in FunctionType
}
return {function: compute_totals(log_entries.filter(time_function=function)) for function in FunctionType}

return super().get_context_data(*args, **kwargs) | {
"CurrencyStatus": CurrencyStatus.__members__,
"passenger_currency": {
"sep": {
"day": get_ninety_days_currency(
LogEntry.objects.filter(
aircraft__type=AircraftType.SEP.name,
time_function=FunctionType.PIC.name,
aircraft__type=AircraftType.SEP,
time_function=FunctionType.PIC,
),
),
"night": get_ninety_days_currency(
LogEntry.objects.filter(
aircraft__type=AircraftType.SEP.name,
time_function=FunctionType.PIC.name,
aircraft__type=AircraftType.SEP,
time_function=FunctionType.PIC,
night=True,
),
required_landings=CURRENCY_REQUIRED_LANDINGS_NIGHT,
Expand All @@ -41,14 +37,14 @@ def totals_per_function(log_entries: QuerySet[LogEntry]):
"tmg": {
"day": get_ninety_days_currency(
LogEntry.objects.filter(
aircraft__type=AircraftType.TMG.name,
time_function=FunctionType.PIC.name,
aircraft__type=AircraftType.TMG,
time_function=FunctionType.PIC,
),
),
"night": get_ninety_days_currency(
LogEntry.objects.filter(
aircraft__type=AircraftType.TMG.name,
time_function=FunctionType.PIC.name,
aircraft__type=AircraftType.TMG,
time_function=FunctionType.PIC,
night=True,
),
required_landings=CURRENCY_REQUIRED_LANDINGS_NIGHT,
Expand Down
8 changes: 2 additions & 6 deletions logbook/views/entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from vereinsflieger.vereinsflieger import VereinsfliegerSession

from ..models import Aerodrome, Aircraft, AircraftType, FunctionType, LogEntry, Pilot
from ..models import Aerodrome, Aircraft, FunctionType, LogEntry, Pilot
from .utils import AuthenticatedListView


Expand Down Expand Up @@ -86,11 +86,7 @@ class EntryIndexView(AuthenticatedListView, FormView):
success_url = reverse_lazy("logbook:entries")

def get_context_data(self, *args, **kwargs):
return super().get_context_data(*args, **kwargs) | {
"aircraft_types": list(AircraftType),
"function_types": list(FunctionType),
"form": self.get_form(),
}
return super().get_context_data(*args, **kwargs) | {"form": self.get_form()}

def paginate_queryset(self, queryset, page_size):
entries = tuple(chain.from_iterable(([entry] + [None] * (entry.slots - 1)) for entry in queryset))
Expand Down
26 changes: 13 additions & 13 deletions logbook/views/experience.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def get_context_data(self, **kwargs):

def get_sep_revalidation_experience(log_entries: QuerySet[LogEntry]) -> ExperienceRequirements:
eligible_entries = log_entries.filter(
aircraft__type__in={AircraftType.SEP.name, AircraftType.TMG.name},
aircraft__type__in={AircraftType.SEP, AircraftType.TMG},
departure_time__gte=make_aware(
datetime.combine(
Certificate.objects.get(name__contains="SEP").valid_until - relativedelta(months=12),
Expand All @@ -52,15 +52,15 @@ def get_sep_revalidation_experience(log_entries: QuerySet[LogEntry]) -> Experien
),
"6 hours as PIC": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=6), landings=0),
accrued=compute_totals(eligible_entries.filter(time_function=FunctionType.PIC.name)),
accrued=compute_totals(eligible_entries.filter(time_function=FunctionType.PIC)),
),
"12 take-offs and 12 landings": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=0), landings=12),
accrued=compute_totals(eligible_entries),
),
"Refresher training with FI or CRI": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=1), landings=0),
accrued=compute_totals(eligible_entries.filter(time_function=FunctionType.DUAL.name)),
accrued=compute_totals(eligible_entries.filter(time_function=FunctionType.DUAL)),
),
},
details="""
Expand All @@ -75,15 +75,15 @@ def get_ppl_experience(log_entries: QuerySet[LogEntry]) -> ExperienceRequirement
experience={
"Dual instruction": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=25), landings=0),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.DUAL.name)),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.DUAL)),
),
"Supervised solo": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=10), landings=0),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC.name)),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC)),
),
"Cross-country solo": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=5), landings=0),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC.name, cross_country=True)),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC, cross_country=True)),
),
"Total hours": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=45), landings=0),
Expand All @@ -102,15 +102,15 @@ def get_night_experience(log_entries: QuerySet[LogEntry]) -> ExperienceRequireme
experience={
"Solo full-stop landings": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=0), landings=5),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC.name), full_stop=True),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC), full_stop=True),
),
"Dual instruction": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=3), landings=0),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.DUAL.name)),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.DUAL)),
),
"Dual cross-country (>27 NM)": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=1), landings=0),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.DUAL.name, cross_country=True)),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.DUAL, cross_country=True)),
),
"Total hours": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=5), landings=0),
Expand All @@ -125,7 +125,7 @@ def get_ir_experience(log_entries: QuerySet[LogEntry]) -> ExperienceRequirements
experience={
"Cross-country PIC": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=50), landings=0),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC.name, cross_country=True)),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC, cross_country=True)),
),
},
)
Expand All @@ -136,16 +136,16 @@ def get_cpl_experience(log_entries: QuerySet[LogEntry]) -> ExperienceRequirement
experience={ # TODO: add dual
"PIC": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=100), landings=0),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC.name)),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC)),
),
"Cross-country PIC": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=20), landings=0),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC.name, cross_country=True)),
accrued=compute_totals(log_entries.filter(time_function=FunctionType.PIC, cross_country=True)),
),
"Visual dual instruction": ExperienceRecord(
required=TotalsRecord(time=timedelta(hours=15), landings=0),
accrued=compute_totals(
log_entries.filter(time_function=FunctionType.DUAL.name, departure_time__gte=CPL_START_DATE),
log_entries.filter(time_function=FunctionType.DUAL, departure_time__gte=CPL_START_DATE),
),
),
"Total hours": ExperienceRecord(
Expand Down

0 comments on commit 977fde4

Please sign in to comment.