From 424a53a9111d60fc94b7639a1853e5c1280be4d2 Mon Sep 17 00:00:00 2001 From: "Yury V. Zaytsev" Date: Tue, 26 Dec 2023 12:33:56 +0100 Subject: [PATCH] refactor(models): split models file in separate files for better overview --- logbook/admin.py | 5 +- logbook/context_processors.py | 3 +- logbook/fixtures/import_aerodromes.py | 2 +- .../management/commands/import_flightlog.py | 37 +-- logbook/models.py | 235 ------------------ logbook/models/__init__.py | 0 logbook/models/aerodrome.py | 19 ++ logbook/models/aircraft.py | 71 ++++++ logbook/models/log_entry.py | 84 +++++++ logbook/models/pilot.py | 73 ++++++ logbook/statistics/currency.py | 2 +- logbook/statistics/experience.py | 2 +- logbook/templatetags/logbook_utils.py | 2 +- logbook/views/aircraft.py | 2 +- logbook/views/certificates.py | 2 +- logbook/views/dashboard.py | 3 +- logbook/views/entries.py | 5 +- logbook/views/experience.py | 4 +- logbook/views/utils.py | 2 +- manage.py | 2 +- tests/test_models.py | 2 +- tests/test_statistics.py | 5 +- tests/test_templatetags.py | 2 +- vereinsflieger/models.py | 2 +- vereinsflieger/test_vereinsflieger_api.py | 2 +- 25 files changed, 301 insertions(+), 267 deletions(-) delete mode 100644 logbook/models.py create mode 100644 logbook/models/__init__.py create mode 100644 logbook/models/aerodrome.py create mode 100644 logbook/models/aircraft.py create mode 100644 logbook/models/log_entry.py create mode 100644 logbook/models/pilot.py diff --git a/logbook/admin.py b/logbook/admin.py index 2c1aca0..792aacf 100644 --- a/logbook/admin.py +++ b/logbook/admin.py @@ -1,6 +1,9 @@ from django.contrib import admin -from .models import Aerodrome, Aircraft, Certificate, LogEntry, Pilot +from .models.aerodrome import Aerodrome +from .models.aircraft import Aircraft +from .models.log_entry import LogEntry +from .models.pilot import Certificate, Pilot from .templatetags.logbook_utils import duration diff --git a/logbook/context_processors.py b/logbook/context_processors.py index 1ca1d5d..98a6bf5 100644 --- a/logbook/context_processors.py +++ b/logbook/context_processors.py @@ -1,4 +1,5 @@ -from .models import AircraftType, FunctionType, LaunchType, SpeedUnit +from .models.aircraft import AircraftType, SpeedUnit +from .models.log_entry import FunctionType, LaunchType from .statistics.currency import CurrencyStatus diff --git a/logbook/fixtures/import_aerodromes.py b/logbook/fixtures/import_aerodromes.py index 9ae193e..bb015de 100644 --- a/logbook/fixtures/import_aerodromes.py +++ b/logbook/fixtures/import_aerodromes.py @@ -6,7 +6,7 @@ from django_countries import countries -from logbook.models import Aerodrome +from logbook.models.aerodrome import Aerodrome CHECK_ICAO_CODE = False diff --git a/logbook/management/commands/import_flightlog.py b/logbook/management/commands/import_flightlog.py index d9d1a09..5028ba2 100644 --- a/logbook/management/commands/import_flightlog.py +++ b/logbook/management/commands/import_flightlog.py @@ -5,7 +5,10 @@ from django.core.management.base import BaseCommand -from logbook import models +import logbook.models.aerodrome +import logbook.models.aircraft +import logbook.models.log_entry +import logbook.models.pilot class Fields(enum.StrEnum): @@ -40,7 +43,7 @@ def add_arguments(self, parser): def handle(self, *args, **options): if options["init"]: self.stdout.write(self.style.WARNING("Removing old database records...")) - models.LogEntry.objects.all().delete() + logbook.models.log_entry.LogEntry.objects.all().delete() self.stdout.write(self.style.SUCCESS("Importing FlightLog records...")) @@ -65,30 +68,36 @@ def handle(self, *args, **options): assert (pic_time + dual_time) == duration_recorded, f"{duration_recorded}, {pic_time}, {dual_time}" assert bool(pic_time) ^ bool(dual_time) - time_function = models.FunctionType.PIC if pic_time else models.FunctionType.DUAL + time_function = ( + logbook.models.log_entry.FunctionType.PIC + if pic_time + else logbook.models.log_entry.FunctionType.DUAL + ) registration = row[Fields.REGISTRATION] - aircraft = models.Aircraft.objects.get(registration=registration) + aircraft = logbook.models.aircraft.Aircraft.objects.get(registration=registration) - me = models.Pilot.objects.get(last_name="Zaytsev") - recorded_copilot = models.Pilot.objects.get(last_name=row[Fields.COPILOT].strip()) + me = logbook.models.pilot.Pilot.objects.get(last_name="Zaytsev") + recorded_copilot = logbook.models.pilot.Pilot.objects.get(last_name=row[Fields.COPILOT].strip()) pilot, copilot = ( - (me, recorded_copilot) if time_function is models.FunctionType.PIC else (recorded_copilot, me) + (me, recorded_copilot) + if time_function is logbook.models.log_entry.FunctionType.PIC + else (recorded_copilot, me) ) - from_aerodrome = models.Aerodrome.objects.get(icao_code=row[Fields.FROM].strip()) - to_aerodrome = models.Aerodrome.objects.get(icao_code=row[Fields.TO].strip()) + from_aerodrome = logbook.models.aerodrome.Aerodrome.objects.get(icao_code=row[Fields.FROM].strip()) + to_aerodrome = logbook.models.aerodrome.Aerodrome.objects.get(icao_code=row[Fields.TO].strip()) remarks = row[Fields.NOTE].strip() launch_type = ( { - "Self": models.LaunchType.SELF, - "Tow": models.LaunchType.TOW, - "Winch": models.LaunchType.WINCH, + "Self": logbook.models.log_entry.LaunchType.SELF, + "Tow": logbook.models.log_entry.LaunchType.TOW, + "Winch": logbook.models.log_entry.LaunchType.WINCH, }[remarks] - if aircraft.type == models.AircraftType.GLD + if aircraft.type == logbook.models.aircraft.AircraftType.GLD else "" ) @@ -117,7 +126,7 @@ def handle(self, *args, **options): "remarks": remarks, } - entry, created = models.LogEntry.objects.update_or_create( + entry, created = logbook.models.log_entry.LogEntry.objects.update_or_create( departure_time=departure_time, from_aerodrome=from_aerodrome, defaults=defaults, diff --git a/logbook/models.py b/logbook/models.py deleted file mode 100644 index 02b32af..0000000 --- a/logbook/models.py +++ /dev/null @@ -1,235 +0,0 @@ -from datetime import UTC, datetime -from typing import Optional - -from django.core.exceptions import ValidationError -from django.db import models -from django.db.models import CheckConstraint, F, Q, UniqueConstraint -from django_countries.fields import CountryField - -from .statistics.currency import NinetyDaysCurrency, get_ninety_days_currency - - -class AircraftType(models.TextChoices): - GLD = "GLD", "Glider" - TMG = "TMG", "Touring Motor Glider" - SEP = "SEP", "Single Engine Piston" - - -class FunctionType(models.TextChoices): - PIC = "PIC", "Pilot-in-Command" - DUAL = "DUAL", "Dual instruction time" - - -class LaunchType(models.TextChoices): - SELF = "SELF", "Self-launch" - WINCH = "WINCH", "Winch launch" - TOW = "TOW", "Aerotow" - - -class SpeedUnit(models.TextChoices): - KMH = "KMH", "km/h" - KT = "KT", "kt" - MPH = "MPH", "mph" - - -class Aerodrome(models.Model): - name = models.CharField(max_length=75) - city = models.CharField(max_length=75) - country = CountryField() - icao_code = models.CharField(max_length=4, unique=True) - latitude = models.DecimalField(max_digits=9, decimal_places=6) - longitude = models.DecimalField(max_digits=9, decimal_places=6) - elevation = models.IntegerField() - priority = models.IntegerField() - - class Meta: - ordering = ("priority",) - - def __str__(self): - return f"{self.icao_code} ({self.name})" - - -class Aircraft(models.Model): - type = models.CharField(max_length=3, choices=AircraftType.choices) - maker = models.CharField(max_length=64) - model = models.CharField(max_length=64) - - # https://www.icao.int/publications/doc8643/pages/search.aspx - icao_designator = models.CharField(max_length=4) - - registration = models.CharField(max_length=9, unique=True) - - currency_required = models.BooleanField(default=False) - - 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( - verbose_name="Vy", - help_text="Best rate of climb speed", - blank=True, - null=True, - ) - v_bg = models.PositiveSmallIntegerField(verbose_name="Vbg", help_text="Best glide speed", blank=True, null=True) - v_app = models.PositiveSmallIntegerField(verbose_name="Vapp", help_text="Approach speed", blank=True, null=True) - v_ref = models.PositiveSmallIntegerField( - verbose_name="Vref", - help_text="Uncorrected final approach speed", - blank=True, - null=True, - ) - v_s = models.PositiveSmallIntegerField(verbose_name="Vs", help_text="Stall speed", blank=True, null=True) - v_c = models.PositiveSmallIntegerField(verbose_name="Vc", help_text="Cruise speed", blank=True, null=True) - - demonstrated_crosswind = models.PositiveSmallIntegerField( - help_text="Demonstrated crosswind in KT", - blank=True, - null=True, - ) - - remarks = models.TextField(blank=True) - - class Meta: - ordering = ("registration",) - verbose_name_plural = "aircraft" - - def __str__(self): - return f"{self.registration} ({self.maker} {self.model})" - - @property - def currency_status(self) -> Optional[NinetyDaysCurrency]: - return get_ninety_days_currency(LogEntry.objects.filter(aircraft=self)) if self.currency_required else None - - -class Pilot(models.Model): - first_name = models.CharField(max_length=30) - last_name = models.CharField(max_length=150) - - me = models.BooleanField(default=False) - - class Meta: - constraints = ( - UniqueConstraint( - fields=["me"], - condition=Q(me=True), - name="only_one_me", - ), - ) - ordering = ("last_name",) - unique_together = ("first_name", "last_name") - - def __str__(self): - return f"{self.first_name} {self.last_name}" - - -class LogEntry(models.Model): - aircraft = models.ForeignKey(Aircraft, on_delete=models.PROTECT) - - from_aerodrome = models.ForeignKey(Aerodrome, on_delete=models.PROTECT, related_name="from_aerodrome_set") - to_aerodrome = models.ForeignKey(Aerodrome, on_delete=models.PROTECT, related_name="to_aerodrome_set") - - departure_time = models.DateTimeField(unique=True) - arrival_time = models.DateTimeField(unique=True) - - landings = models.PositiveSmallIntegerField(default=1) - - 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=LaunchType.choices) - - remarks = models.CharField(max_length=255, blank=True) - - cross_country = models.BooleanField(default=False) - night = models.BooleanField(default=False) - - slots = models.PositiveSmallIntegerField(default=1, help_text="Number of logbook slots for this entry.") - - class Meta: - constraints = ( - CheckConstraint(check=Q(arrival_time__gt=F("departure_time")), name="arrival_after_departure"), - CheckConstraint(check=~Q(copilot=F("pilot")), name="copilot_not_pilot"), - CheckConstraint( - check=( - 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", - ), - ) - ordering = ("-arrival_time",) - verbose_name_plural = "Log entries" - - def __str__(self): - duration = (self.arrival_time - self.departure_time).total_seconds() - duration_hours = int(duration // 3600) - duration_minutes = int((duration - duration_hours * 3600) // 60) - remarks = f"({self.launch_type})" if self.launch_type else "" - return ( - f"{self.departure_time.strftime('%Y-%m-%d %H:%M')} - {self.arrival_time.strftime('%H:%M')} " - f"({duration_hours:02}:{duration_minutes:02}) " - f"{self.aircraft.registration} ({self.aircraft.type}) " - f"{self.from_aerodrome.icao_code} -> {self.to_aerodrome.icao_code} " - f"{self.pilot.last_name}{' / ' + self.copilot.last_name if self.copilot is not None else ''} " - f"{'[XC] ' if self.cross_country else ''}" - f"{'[N] ' if self.night else ''}" - f"{remarks}".strip() - ) - - 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 - and not self.launch_type - ): - raise ValidationError("Launch type is required for gliders!") - - -class Certificate(models.Model): - name = models.CharField(max_length=255) - number = models.CharField(max_length=255, blank=True) - issue_date = models.DateField() - valid_until = models.DateField(blank=True, null=True) - authority = models.CharField(max_length=255) - remarks = models.CharField(max_length=255, blank=True) - supersedes = models.ForeignKey( - "self", - on_delete=models.PROTECT, - blank=True, - null=True, - related_name="supersedes_set", - ) - - class Meta: - constraints = ( - CheckConstraint( - check=Q(valid_until__isnull=True) | Q(valid_until__gt=F("issue_date")), - name="validity_after_issue", - ), - CheckConstraint( - check=~Q(id=F("supersedes")), - name="supersedes_self", - ), - UniqueConstraint( - fields=["supersedes"], - condition=Q(supersedes__isnull=False), - name="supersedes_unique", - ), - ) - ordering = ("name", F("supersedes").desc(nulls_last=True), "-valid_until") - - def __str__(self): - return f"{self.name}{' / {}'.format(self.number) if self.number else ''} ({self.issue_date})" - - @property - def valid(self) -> bool: - return ( - self.valid_until is None or self.valid_until >= datetime.now(tz=UTC).date() - ) and not self.supersedes_set.count() - - @property - def superseded_by(self) -> Optional["Certificate"]: - return self.supersedes_set.first() diff --git a/logbook/models/__init__.py b/logbook/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/logbook/models/aerodrome.py b/logbook/models/aerodrome.py new file mode 100644 index 0000000..efe9a7f --- /dev/null +++ b/logbook/models/aerodrome.py @@ -0,0 +1,19 @@ +from django.db import models +from django_countries.fields import CountryField + + +class Aerodrome(models.Model): + name = models.CharField(max_length=75) + city = models.CharField(max_length=75) + country = CountryField() + icao_code = models.CharField(max_length=4, unique=True) + latitude = models.DecimalField(max_digits=9, decimal_places=6) + longitude = models.DecimalField(max_digits=9, decimal_places=6) + elevation = models.IntegerField() + priority = models.IntegerField() + + class Meta: + ordering = ("priority",) + + def __str__(self): + return f"{self.icao_code} ({self.name})" diff --git a/logbook/models/aircraft.py b/logbook/models/aircraft.py new file mode 100644 index 0000000..8a53e1b --- /dev/null +++ b/logbook/models/aircraft.py @@ -0,0 +1,71 @@ +from typing import Optional + +from django.db import models + +from logbook.statistics.currency import NinetyDaysCurrency, get_ninety_days_currency + + +class AircraftType(models.TextChoices): + GLD = "GLD", "Glider" + TMG = "TMG", "Touring Motor Glider" + SEP = "SEP", "Single Engine Piston" + + +class SpeedUnit(models.TextChoices): + KMH = "KMH", "km/h" + KT = "KT", "kt" + MPH = "MPH", "mph" + + +class Aircraft(models.Model): + type = models.CharField(max_length=3, choices=AircraftType.choices) + maker = models.CharField(max_length=64) + model = models.CharField(max_length=64) + + # https://www.icao.int/publications/doc8643/pages/search.aspx + icao_designator = models.CharField(max_length=4) + + registration = models.CharField(max_length=9, unique=True) + + currency_required = models.BooleanField(default=False) + + 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( + verbose_name="Vy", + help_text="Best rate of climb speed", + blank=True, + null=True, + ) + v_bg = models.PositiveSmallIntegerField(verbose_name="Vbg", help_text="Best glide speed", blank=True, null=True) + v_app = models.PositiveSmallIntegerField(verbose_name="Vapp", help_text="Approach speed", blank=True, null=True) + v_ref = models.PositiveSmallIntegerField( + verbose_name="Vref", + help_text="Uncorrected final approach speed", + blank=True, + null=True, + ) + v_s = models.PositiveSmallIntegerField(verbose_name="Vs", help_text="Stall speed", blank=True, null=True) + v_c = models.PositiveSmallIntegerField(verbose_name="Vc", help_text="Cruise speed", blank=True, null=True) + + demonstrated_crosswind = models.PositiveSmallIntegerField( + help_text="Demonstrated crosswind in KT", + blank=True, + null=True, + ) + + remarks = models.TextField(blank=True) + + class Meta: + ordering = ("registration",) + verbose_name_plural = "aircraft" + + def __str__(self): + return f"{self.registration} ({self.maker} {self.model})" + + @property + def currency_status(self) -> Optional[NinetyDaysCurrency]: + from .log_entry import LogEntry + + return get_ninety_days_currency(LogEntry.objects.filter(aircraft=self)) if self.currency_required else None diff --git a/logbook/models/log_entry.py b/logbook/models/log_entry.py new file mode 100644 index 0000000..9bf4b79 --- /dev/null +++ b/logbook/models/log_entry.py @@ -0,0 +1,84 @@ +from django.core.exceptions import ValidationError +from django.db import models +from django.db.models import CheckConstraint, F, Q + +from .aerodrome import Aerodrome +from .aircraft import Aircraft, AircraftType +from .pilot import Pilot + + +class FunctionType(models.TextChoices): + PIC = "PIC", "Pilot-in-Command" + DUAL = "DUAL", "Dual instruction time" + + +class LaunchType(models.TextChoices): + SELF = "SELF", "Self-launch" + WINCH = "WINCH", "Winch launch" + TOW = "TOW", "Aerotow" + + +class LogEntry(models.Model): + aircraft = models.ForeignKey(Aircraft, on_delete=models.PROTECT) + + from_aerodrome = models.ForeignKey(Aerodrome, on_delete=models.PROTECT, related_name="from_aerodrome_set") + to_aerodrome = models.ForeignKey(Aerodrome, on_delete=models.PROTECT, related_name="to_aerodrome_set") + + departure_time = models.DateTimeField(unique=True) + arrival_time = models.DateTimeField(unique=True) + + landings = models.PositiveSmallIntegerField(default=1) + + 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=LaunchType.choices) + + remarks = models.CharField(max_length=255, blank=True) + + cross_country = models.BooleanField(default=False) + night = models.BooleanField(default=False) + + slots = models.PositiveSmallIntegerField(default=1, help_text="Number of logbook slots for this entry.") + + class Meta: + constraints = ( + CheckConstraint(check=Q(arrival_time__gt=F("departure_time")), name="arrival_after_departure"), + CheckConstraint(check=~Q(copilot=F("pilot")), name="copilot_not_pilot"), + CheckConstraint( + check=( + 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", + ), + ) + ordering = ("-arrival_time",) + verbose_name_plural = "Log entries" + + def __str__(self): + duration = (self.arrival_time - self.departure_time).total_seconds() + duration_hours = int(duration // 3600) + duration_minutes = int((duration - duration_hours * 3600) // 60) + remarks = f"({self.launch_type})" if self.launch_type else "" + return ( + f"{self.departure_time.strftime('%Y-%m-%d %H:%M')} - {self.arrival_time.strftime('%H:%M')} " + f"({duration_hours:02}:{duration_minutes:02}) " + f"{self.aircraft.registration} ({self.aircraft.type}) " + f"{self.from_aerodrome.icao_code} -> {self.to_aerodrome.icao_code} " + f"{self.pilot.last_name}{' / ' + self.copilot.last_name if self.copilot is not None else ''} " + f"{'[XC] ' if self.cross_country else ''}" + f"{'[N] ' if self.night else ''}" + f"{remarks}".strip() + ) + + 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 + and not self.launch_type + ): + raise ValidationError("Launch type is required for gliders!") diff --git a/logbook/models/pilot.py b/logbook/models/pilot.py new file mode 100644 index 0000000..bd9da82 --- /dev/null +++ b/logbook/models/pilot.py @@ -0,0 +1,73 @@ +from datetime import UTC, datetime +from typing import Optional + +from django.db import models +from django.db.models import CheckConstraint, F, Q, UniqueConstraint + + +class Pilot(models.Model): + first_name = models.CharField(max_length=30) + last_name = models.CharField(max_length=150) + + me = models.BooleanField(default=False) + + class Meta: + constraints = ( + UniqueConstraint( + fields=["me"], + condition=Q(me=True), + name="only_one_me", + ), + ) + ordering = ("last_name",) + unique_together = ("first_name", "last_name") + + def __str__(self): + return f"{self.first_name} {self.last_name}" + + +class Certificate(models.Model): + name = models.CharField(max_length=255) + number = models.CharField(max_length=255, blank=True) + issue_date = models.DateField() + valid_until = models.DateField(blank=True, null=True) + authority = models.CharField(max_length=255) + remarks = models.CharField(max_length=255, blank=True) + supersedes = models.ForeignKey( + "self", + on_delete=models.PROTECT, + blank=True, + null=True, + related_name="supersedes_set", + ) + + class Meta: + constraints = ( + CheckConstraint( + check=Q(valid_until__isnull=True) | Q(valid_until__gt=F("issue_date")), + name="validity_after_issue", + ), + CheckConstraint( + check=~Q(id=F("supersedes")), + name="supersedes_self", + ), + UniqueConstraint( + fields=["supersedes"], + condition=Q(supersedes__isnull=False), + name="supersedes_unique", + ), + ) + ordering = ("name", F("supersedes").desc(nulls_last=True), "-valid_until") + + def __str__(self): + return f"{self.name}{' / {}'.format(self.number) if self.number else ''} ({self.issue_date})" + + @property + def valid(self) -> bool: + return ( + self.valid_until is None or self.valid_until >= datetime.now(tz=UTC).date() + ) and not self.supersedes_set.count() + + @property + def superseded_by(self) -> Optional["Certificate"]: + return self.supersedes_set.first() diff --git a/logbook/statistics/currency.py b/logbook/statistics/currency.py index ae3bc00..746e9d8 100644 --- a/logbook/statistics/currency.py +++ b/logbook/statistics/currency.py @@ -6,7 +6,7 @@ from django.db.models import OuterRef, QuerySet, Subquery, Sum, Value if TYPE_CHECKING: - from ..models import LogEntry + from ..models.log_entry import LogEntry class CurrencyStatus(models.TextChoices): diff --git a/logbook/statistics/experience.py b/logbook/statistics/experience.py index db82c8c..2fcd91f 100644 --- a/logbook/statistics/experience.py +++ b/logbook/statistics/experience.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Iterable, Optional if TYPE_CHECKING: - from ..models import LogEntry + from ..models.log_entry import LogEntry @dataclass(frozen=True, kw_only=True) diff --git a/logbook/templatetags/logbook_utils.py b/logbook/templatetags/logbook_utils.py index 244f0d4..5247d8e 100644 --- a/logbook/templatetags/logbook_utils.py +++ b/logbook/templatetags/logbook_utils.py @@ -6,7 +6,7 @@ from django.template import TemplateSyntaxError from django.utils.safestring import mark_safe -from ..models import SpeedUnit +from ..models.aircraft import SpeedUnit from ..statistics.experience import ExperienceRecord, TotalsRecord register = template.Library() diff --git a/logbook/views/aircraft.py b/logbook/views/aircraft.py index 2b01bd5..f752e0d 100644 --- a/logbook/views/aircraft.py +++ b/logbook/views/aircraft.py @@ -1,4 +1,4 @@ -from ..models import Aircraft +from ..models.aircraft import Aircraft from .utils import AuthenticatedListView diff --git a/logbook/views/certificates.py b/logbook/views/certificates.py index 05db151..56ea157 100644 --- a/logbook/views/certificates.py +++ b/logbook/views/certificates.py @@ -1,4 +1,4 @@ -from ..models import Certificate +from ..models.pilot import Certificate from .utils import AuthenticatedListView, check_certificates_expiry diff --git a/logbook/views/dashboard.py b/logbook/views/dashboard.py index 112a78c..e875b3e 100644 --- a/logbook/views/dashboard.py +++ b/logbook/views/dashboard.py @@ -2,7 +2,8 @@ from django.db.models import QuerySet -from ..models import Aircraft, AircraftType, FunctionType, LogEntry +from ..models.aircraft import Aircraft, AircraftType +from ..models.log_entry import FunctionType, LogEntry from ..statistics.currency import CURRENCY_REQUIRED_LANDINGS_NIGHT, get_ninety_days_currency from ..statistics.experience import compute_totals from .utils import ( diff --git a/logbook/views/entries.py b/logbook/views/entries.py index 605c8fa..7181003 100644 --- a/logbook/views/entries.py +++ b/logbook/views/entries.py @@ -8,7 +8,10 @@ from vereinsflieger.vereinsflieger import import_from_vereinsflieger -from ..models import Aerodrome, Aircraft, LogEntry, Pilot +from ..models.aerodrome import Aerodrome +from ..models.aircraft import Aircraft +from ..models.log_entry import LogEntry +from ..models.pilot import Pilot from .utils import AuthenticatedListView diff --git a/logbook/views/experience.py b/logbook/views/experience.py index d0fce5c..2e09b97 100644 --- a/logbook/views/experience.py +++ b/logbook/views/experience.py @@ -5,7 +5,9 @@ from django.db.models import QuerySet from django.utils.timezone import make_aware -from ..models import AircraftType, Certificate, FunctionType, LogEntry +from ..models.aircraft import AircraftType +from ..models.log_entry import FunctionType, LogEntry +from ..models.pilot import Certificate from ..statistics.experience import ( ExperienceRecord, ExperienceRequirements, diff --git a/logbook/views/utils.py b/logbook/views/utils.py index 9c6b7ad..10840ae 100644 --- a/logbook/views/utils.py +++ b/logbook/views/utils.py @@ -8,7 +8,7 @@ from django.utils.safestring import mark_safe from django.views.generic import ListView, TemplateView -from ..models import Certificate +from ..models.pilot import Certificate EXPIRY_WARNING_THRESHOLD = relativedelta(months=3) diff --git a/manage.py b/manage.py index b365015..33ac0fb 100755 --- a/manage.py +++ b/manage.py @@ -8,7 +8,7 @@ from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( - "Couldnst import Django. Are you sure it's installed and " + "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?", ) from exc diff --git a/tests/test_models.py b/tests/test_models.py index 30f0dc1..1c19ea8 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -2,7 +2,7 @@ from django.test import TestCase -from logbook.models import Certificate +from logbook.models.pilot import Certificate class ModelsTest(TestCase): diff --git a/tests/test_statistics.py b/tests/test_statistics.py index 11bec1d..2a8e957 100644 --- a/tests/test_statistics.py +++ b/tests/test_statistics.py @@ -2,7 +2,10 @@ from django.test import TestCase -from logbook.models import Aerodrome, Aircraft, AircraftType, LogEntry, Pilot +from logbook.models.aerodrome import Aerodrome +from logbook.models.aircraft import Aircraft, AircraftType +from logbook.models.log_entry import LogEntry +from logbook.models.pilot import Pilot from logbook.statistics.currency import CurrencyStatus, get_ninety_days_currency diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index 239a681..076c906 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -4,7 +4,7 @@ from django.template import TemplateSyntaxError from django.utils.safestring import SafeString -from logbook.models import SpeedUnit +from logbook.models.aircraft import SpeedUnit from logbook.statistics.experience import ExperienceRecord, TotalsRecord from logbook.templatetags.logbook_utils import duration, replace, represent, subtract, to_kt diff --git a/vereinsflieger/models.py b/vereinsflieger/models.py index 8d5ecee..c0cb25c 100644 --- a/vereinsflieger/models.py +++ b/vereinsflieger/models.py @@ -5,7 +5,7 @@ from playwright.async_api import Page -from logbook.models import FunctionType +from logbook.models.log_entry import FunctionType @dataclass(frozen=True, kw_only=True) diff --git a/vereinsflieger/test_vereinsflieger_api.py b/vereinsflieger/test_vereinsflieger_api.py index 5251e0f..f9d97ce 100644 --- a/vereinsflieger/test_vereinsflieger_api.py +++ b/vereinsflieger/test_vereinsflieger_api.py @@ -5,7 +5,7 @@ import requests import requests_mock -from logbook.models import FunctionType +from logbook.models.log_entry import FunctionType from vereinsflieger.models import Flight, Person from vereinsflieger.vereinsflieger_api import VereinsfliegerApiSession