diff --git a/techdb/flightslot/admin.py b/techdb/flightslot/admin.py index e4e1043..cfcb9f7 100644 --- a/techdb/flightslot/admin.py +++ b/techdb/flightslot/admin.py @@ -1,10 +1,17 @@ -from django.core.exceptions import ValidationError -from django.contrib import admin from django import forms -from .models import * -from datetime import date +from django.contrib import admin from durationwidget.widgets import TimeDurationWidget +from datetime import date import nested_admin + +from .models.courses import Course +from .models.hourbuildings import HourBuilding, HourBuildingLeg +from .models.missions import Training, MissionProfile +from .models.students import Student +from .models.weekpref import WeekPreference + +class TrainingForm(forms.ModelForm): + model=Training class HourBuildingLegForm(forms.ModelForm): class Meta: @@ -16,9 +23,6 @@ class HourBuildingLegForm(forms.ModelForm): ) } -class TrainingForm(forms.ModelForm): - model=Training - # Register your models here. class HourBuildingLegInline(nested_admin.NestedTabularInline): model = HourBuildingLeg @@ -88,8 +92,17 @@ class StudentAdmin(admin.ModelAdmin): list_display = ("surname", "name", "course", "email","active") list_filter = ["course", "active"] +class CourseAdminForm(forms.ModelForm): + class Meta: + model = Course + class CourseAdmin(admin.ModelAdmin): + list_display = ["ctype", "cnumber", "year", "color"] list_filter = ["ctype", "year"] + form=CourseAdminForm + +class MissionProfileAdmin(admin.ModelAdmin): + list_display = ("mtype", "mnum") admin.site.register(Course, CourseAdmin) admin.site.register(MissionProfile) diff --git a/techdb/flightslot/apps.py b/techdb/flightslot/apps.py index f7998f4..a8acc38 100644 --- a/techdb/flightslot/apps.py +++ b/techdb/flightslot/apps.py @@ -5,4 +5,5 @@ class FlightslotConfig(AppConfig): name = 'flightslot' def ready(self): + # Import only when application is ready otherwise signals will not be called from . import signals \ No newline at end of file diff --git a/techdb/flightslot/migrations/0012_alter_course_cnumber_alter_course_year_and_more.py b/techdb/flightslot/migrations/0012_alter_course_cnumber_alter_course_year_and_more.py new file mode 100644 index 0000000..ef6bd5c --- /dev/null +++ b/techdb/flightslot/migrations/0012_alter_course_cnumber_alter_course_year_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.8 on 2025-11-12 09:47 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('flightslot', '0011_alter_hourbuildingleg_time'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='cnumber', + field=models.PositiveSmallIntegerField(default=2025), + ), + migrations.AlterField( + model_name='course', + name='year', + field=models.PositiveSmallIntegerField(db_default=2025, editable=False), + ), + migrations.AlterField( + model_name='missionprofile', + name='duration', + field=models.DurationField(default=datetime.timedelta(seconds=3600)), + ), + migrations.AlterField( + model_name='weekpreference', + name='week', + field=models.PositiveSmallIntegerField(auto_created=True, db_default=46, db_index=True, verbose_name='Week Number'), + ), + ] diff --git a/techdb/flightslot/migrations/0013_course_color_alter_course_ctype_alter_course_year.py b/techdb/flightslot/migrations/0013_course_color_alter_course_ctype_alter_course_year.py new file mode 100644 index 0000000..1822cd4 --- /dev/null +++ b/techdb/flightslot/migrations/0013_course_color_alter_course_ctype_alter_course_year.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.8 on 2025-11-12 10:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('flightslot', '0012_alter_course_cnumber_alter_course_year_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='course', + name='color', + field=models.CharField(choices=[('#ffffff', 'WHITE'), ('#ff0000', 'RED'), ('#00ff00', 'GREEN'), ('#0000ff', 'BLUE')], default='#ffffff'), + ), + migrations.AlterField( + model_name='course', + name='ctype', + field=models.CharField(choices=[('FI', 'FI'), ('PPL', 'PPL'), ('ATPL', 'ATPL'), ('DL', 'DISTANCE'), ('OTHER', 'OTHER')]), + ), + migrations.AlterField( + model_name='course', + name='year', + field=models.PositiveSmallIntegerField(default=2025), + ), + ] diff --git a/techdb/flightslot/migrations/0014_student_phone.py b/techdb/flightslot/migrations/0014_student_phone.py new file mode 100644 index 0000000..8c5a47e --- /dev/null +++ b/techdb/flightslot/migrations/0014_student_phone.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.8 on 2025-11-12 10:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('flightslot', '0013_course_color_alter_course_ctype_alter_course_year'), + ] + + operations = [ + migrations.AddField( + model_name='student', + name='phone', + field=models.CharField(max_length=16, null=True), + ), + ] diff --git a/techdb/flightslot/migrations/0015_alter_course_color.py b/techdb/flightslot/migrations/0015_alter_course_color.py new file mode 100644 index 0000000..9904fe9 --- /dev/null +++ b/techdb/flightslot/migrations/0015_alter_course_color.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.8 on 2025-11-12 10:31 + +import colorfield.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('flightslot', '0014_student_phone'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='color', + field=colorfield.fields.ColorField(default='#FFFFFF', image_field=None, max_length=25, samples=[('#ffffff', 'WHITE'), ('#ff0000', 'RED'), ('#00ff00', 'GREEN'), ('#0000ff', 'BLUE')]), + ), + ] diff --git a/techdb/flightslot/models.py b/techdb/flightslot/models.py deleted file mode 100644 index 26035b0..0000000 --- a/techdb/flightslot/models.py +++ /dev/null @@ -1,308 +0,0 @@ -from django.db import models -from django.contrib import admin -from django.contrib.auth.models import User -from django.db.models.functions import Now, ExtractWeek, ExtractYear -from django.utils.translation import gettext_lazy as _ -from datetime import timedelta, date - -# Create your models here. - -class CourseTypes(models.TextChoices): - PPL = "PPL", _("PPL") - ATPL = "ATPL", _("ATPL") - -class Course(models.Model): - id = models.AutoField( - primary_key=True - ) - - ctype = models.CharField( - null=False, - choices=CourseTypes - ) - - cnumber = models.PositiveSmallIntegerField( - null=False, - default=date.today().year - ) - - year = models.PositiveSmallIntegerField( - editable=False, - db_default=date.today().year - ) - - def __str__(self): - return f"{self.ctype}-{self.cnumber}" - -class Student(models.Model): - id = models.AutoField( - primary_key=True - ) - - email = models.EmailField( - null=False, - db_index=True - ) - - name = models.CharField( - null=False, - max_length=32 - ) - - surname = models.CharField( - null=False, - max_length=32 - ) - - course = models.ForeignKey( - Course, - on_delete=models.DO_NOTHING, - null=True - ) - - active = models.BooleanField( - null=False, - default=True - ) - - user = models.OneToOneField( - User, - on_delete=models.CASCADE, - null=True, - blank=True - ) - - def __str__(self): - return f"{self.surname} {self.name[0]}. => {self.course}" - -class WeekPreference(models.Model): - id = models.BigAutoField( - primary_key=True - ) - - week = models.PositiveSmallIntegerField( - null=False, - db_index=True, - db_default=date.today().isocalendar().week, - auto_created=True, - verbose_name="Week Number" - ) - - student = models.ForeignKey( - Student, - null=False, - db_index=True, - on_delete=models.DO_NOTHING, - verbose_name="Student Selection" - ) - - def __str__(self): - return f"Week {self.week} - {self.student.surname} {self.student.name[0]}." - -class MissionType(models.TextChoices): - OTHER = "OTHER", _("OTHER") - PPL = "PPL", _("PPL") - IR = "IR", _("IR") - CPL = "CPL", _("CPL") - FI = "FI", _("FI") - PC = "PC", _("PC") - CHK = "CHK", _("CHK_6M") - -class MissionProfile(models.Model): - id = models.AutoField( - primary_key=True - ) - - mtype = models.CharField( - null=False, - default=MissionType.PPL, - choices=MissionType, - verbose_name="Mission Type" - ) - - mnum = models.PositiveSmallIntegerField( - null=True, - default=0, - verbose_name="Mission Number" - ) - - duration = models.DurationField( - null=False, - default=timedelta(hours=1) - ) - - notes = models.TextField( - max_length=140, - null=True, - blank=True - ) - - def __str__(self): - return f"{self.mtype} {self.mnum}" - -class MissionProfileAdmin(admin.ModelAdmin): - list_display = ("mtype", "mnum") - -class AircraftTypes(models.TextChoices): - C152 = "C152", _("Cessna 152") - P208 = "P208", _("Tecnam P2008") - PA28 = "PA28", _("Piper PA28R") - C182 = "C182", _("Cessna 182Q") - P210 = "P210", _("Tecnam P2010") - -class HourBuilding(models.Model): - id = models.BigAutoField( - primary_key=True - ) - - weekpref = models.ForeignKey( - WeekPreference, - null=False, - on_delete=models.CASCADE - ) - - aircraft = models.CharField( - null=False, - choices=AircraftTypes - ) - - monday = models.BooleanField( - default=True, - null=False - ) - - tuesday = models.BooleanField( - default=True, - null=False - ) - - wednesday = models.BooleanField( - default=True, - null=False - ) - - thursday = models.BooleanField( - default=True, - null=False - ) - - friday = models.BooleanField( - default=True, - null=False - ) - - saturday = models.BooleanField( - default=True, - null=False - ) - - sunday = models.BooleanField( - default=True, - null=False - ) - - notes = models.TextField( - max_length=140, - null=True, - blank=True - ) - -class HourBuildingLeg(models.Model): - id = models.BigAutoField( - primary_key=True - ) - - hb = models.ForeignKey( - HourBuilding, - on_delete=models.CASCADE - ) - - departure = models.CharField( - null=False, - blank=False, - default="LILV", - max_length=4 - ) - - destination = models.CharField( - null=False, - blank=False, - default="LILV", - max_length=4 - ) - - time = models.DurationField( - null=False, - default = timedelta(hours=1) - ) - - stop = models.BooleanField( - default=False - ) - - def __str__(self): - if self.stop: - return "Refuelling Stop" - else: - return f"Flight Leg: {self.departure} -> {self.destination}" - -class Training(models.Model): - id = models.BigAutoField( - primary_key=True - ) - - weekpref = models.ForeignKey( - WeekPreference, - null=False, - on_delete=models.CASCADE - ) - - mission = models.ForeignKey( - MissionProfile, - null=True, - on_delete=models.CASCADE - ) - - monday = models.BooleanField( - default=True, - null=False - ) - - tuesday = models.BooleanField( - default=True, - null=False - ) - - wednesday = models.BooleanField( - default=True, - null=False - ) - - thursday = models.BooleanField( - default=True, - null=False - ) - - friday = models.BooleanField( - default=True, - null=False - ) - - saturday = models.BooleanField( - default=True, - null=False - ) - - sunday = models.BooleanField( - default=True, - null=False - ) - - notes = models.TextField( - max_length=140, - null=True, - blank=True - ) - - def __str__(self): - return f"{self.mission}" diff --git a/techdb/flightslot/models/aircrafts.py b/techdb/flightslot/models/aircrafts.py new file mode 100644 index 0000000..c452a18 --- /dev/null +++ b/techdb/flightslot/models/aircrafts.py @@ -0,0 +1,9 @@ +from django.utils.translation import gettext_lazy as _ +from django.db import models + +class AircraftTypes(models.TextChoices): + C152 = "C152", _("Cessna 152") + P208 = "P208", _("Tecnam P2008") + PA28 = "PA28", _("Piper PA28R") + C182 = "C182", _("Cessna 182Q") + P210 = "P210", _("Tecnam P2010") diff --git a/techdb/flightslot/models/courses.py b/techdb/flightslot/models/courses.py new file mode 100644 index 0000000..d88c047 --- /dev/null +++ b/techdb/flightslot/models/courses.py @@ -0,0 +1,52 @@ +from django.utils.translation import gettext_lazy as _ +from django.db import models +from datetime import date +from colorfield.fields import ColorField + +class CourseTypes(models.TextChoices): + FI = "FI", _("FI") + PPL = "PPL", _("PPL") + ATPL = "ATPL", _("ATPL") + DISTANCE = "DL", _("DISTANCE") + OTHER = "OTHER",_("OTHER") + + +class Course(models.Model): + # Add colors according to table from Alessia + COLOR_PALETTE = [ + ("#ffffff","WHITE"), + ("#ff0000", "RED"), + ("#00ff00", "GREEN"), + ("#0000ff", "BLUE") + ] + + id = models.AutoField( + primary_key=True + ) + + ctype = models.CharField( + null=False, + choices=CourseTypes, + verbose_name=_("Course Type") + ) + + cnumber = models.PositiveSmallIntegerField( + null=False, + default=date.today().year, + verbose_name=_("Course Number") + ) + + year = models.PositiveSmallIntegerField( + null=False, + default=date.today().year, + verbose_name=_("Year") + ) + + color = ColorField ( + samples=COLOR_PALETTE, + verbose_name=_("Binder Color") + ) + + def __str__(self): + return f"{self.ctype}-{self.cnumber}" + \ No newline at end of file diff --git a/techdb/flightslot/models/hourbuildings.py b/techdb/flightslot/models/hourbuildings.py new file mode 100644 index 0000000..5f36661 --- /dev/null +++ b/techdb/flightslot/models/hourbuildings.py @@ -0,0 +1,103 @@ +from django.utils.translation import gettext_lazy as _ +from django.db import models +from datetime import timedelta + +from ..models.weekpref import WeekPreference +from ..models.aircrafts import AircraftTypes + + +class HourBuilding(models.Model): + id = models.BigAutoField( + primary_key=True + ) + + weekpref = models.ForeignKey( + WeekPreference, + null=False, + on_delete=models.CASCADE + ) + + aircraft = models.CharField( + null=False, + choices=AircraftTypes + ) + + monday = models.BooleanField( + default=True, + null=False + ) + + tuesday = models.BooleanField( + default=True, + null=False + ) + + wednesday = models.BooleanField( + default=True, + null=False + ) + + thursday = models.BooleanField( + default=True, + null=False + ) + + friday = models.BooleanField( + default=True, + null=False + ) + + saturday = models.BooleanField( + default=True, + null=False + ) + + sunday = models.BooleanField( + default=True, + null=False + ) + + notes = models.TextField( + max_length=140, + null=True, + blank=True + ) + +class HourBuildingLeg(models.Model): + id = models.BigAutoField( + primary_key=True + ) + + hb = models.ForeignKey( + HourBuilding, + on_delete=models.CASCADE + ) + + departure = models.CharField( + null=False, + blank=False, + default="LILV", + max_length=4 + ) + + destination = models.CharField( + null=False, + blank=False, + default="LILV", + max_length=4 + ) + + time = models.DurationField( + null=False, + default = timedelta(hours=1) + ) + + stop = models.BooleanField( + default=False + ) + + def __str__(self): + if self.stop: + return "Refuelling Stop" + else: + return f"Flight Leg: {self.departure} -> {self.destination}" \ No newline at end of file diff --git a/techdb/flightslot/models/missions.py b/techdb/flightslot/models/missions.py new file mode 100644 index 0000000..4c79b07 --- /dev/null +++ b/techdb/flightslot/models/missions.py @@ -0,0 +1,108 @@ +from django.utils.translation import gettext_lazy as _ +from django.db import models +from datetime import timedelta + +from ..models.weekpref import WeekPreference + +class MissionType(models.TextChoices): + OTHER = "OTHER", _("OTHER") + PPL = "PPL", _("PPL") + IR = "IR", _("IR") + CPL = "CPL", _("CPL") + FI = "FI", _("FI") + PC = "PC", _("PC") + CHK = "CHK", _("CHK_6M") + +class MissionProfile(models.Model): + id = models.AutoField( + primary_key=True + ) + + mtype = models.CharField( + null=False, + default=MissionType.PPL, + choices=MissionType, + verbose_name="Mission Type" + ) + + mnum = models.PositiveSmallIntegerField( + null=True, + default=0, + verbose_name="Mission Number" + ) + + duration = models.DurationField( + null=False, + default=timedelta(hours=1) + ) + + notes = models.TextField( + max_length=140, + null=True, + blank=True + ) + + def __str__(self): + return f"{self.mtype} {self.mnum}" + +class Training(models.Model): + id = models.BigAutoField( + primary_key=True + ) + + weekpref = models.ForeignKey( + WeekPreference, + null=False, + on_delete=models.CASCADE + ) + + mission = models.ForeignKey( + MissionProfile, + null=True, + on_delete=models.CASCADE + ) + + monday = models.BooleanField( + default=True, + null=False + ) + + tuesday = models.BooleanField( + default=True, + null=False + ) + + wednesday = models.BooleanField( + default=True, + null=False + ) + + thursday = models.BooleanField( + default=True, + null=False + ) + + friday = models.BooleanField( + default=True, + null=False + ) + + saturday = models.BooleanField( + default=True, + null=False + ) + + sunday = models.BooleanField( + default=True, + null=False + ) + + notes = models.TextField( + max_length=140, + null=True, + blank=True + ) + + def __str__(self): + return f"{self.mission}" + \ No newline at end of file diff --git a/techdb/flightslot/models/students.py b/techdb/flightslot/models/students.py new file mode 100644 index 0000000..14dc2f0 --- /dev/null +++ b/techdb/flightslot/models/students.py @@ -0,0 +1,50 @@ +from django.db import models +from django.contrib.auth.models import User + +from ..models.courses import Course + +class Student(models.Model): + id = models.AutoField( + primary_key=True + ) + + email = models.EmailField( + null=False, + db_index=True + ) + + phone = models.CharField( + null=True, + max_length=16 + ) + + name = models.CharField( + null=False, + max_length=32 + ) + + surname = models.CharField( + null=False, + max_length=32 + ) + + course = models.ForeignKey( + Course, + on_delete=models.DO_NOTHING, + null=True + ) + + active = models.BooleanField( + null=False, + default=True + ) + + user = models.OneToOneField( + User, + on_delete=models.CASCADE, + null=True, + blank=True + ) + + def __str__(self): + return f"{self.surname} {self.name[0]}. => {self.course}" diff --git a/techdb/flightslot/models/weekpref.py b/techdb/flightslot/models/weekpref.py new file mode 100644 index 0000000..ac7fad5 --- /dev/null +++ b/techdb/flightslot/models/weekpref.py @@ -0,0 +1,28 @@ +from django.db import models +from datetime import date + +from ..models.students import Student + +class WeekPreference(models.Model): + id = models.BigAutoField( + primary_key=True + ) + + week = models.PositiveSmallIntegerField( + null=False, + db_index=True, + db_default=date.today().isocalendar().week, + auto_created=True, + verbose_name="Week Number" + ) + + student = models.ForeignKey( + Student, + null=False, + db_index=True, + on_delete=models.DO_NOTHING, + verbose_name="Student Selection" + ) + + def __str__(self): + return f"Week {self.week} - {self.student.surname} {self.student.name[0]}." diff --git a/techdb/flightslot/signals.py b/techdb/flightslot/signals.py index c65d604..46cc6b5 100644 --- a/techdb/flightslot/signals.py +++ b/techdb/flightslot/signals.py @@ -2,8 +2,10 @@ from django.db.models.signals import post_save from django.dispatch import receiver from django.contrib.auth.models import User from django.contrib.auth.models import Group -from .models import Student +from .models.students import Student + +# Create a Django user every time a new student is created @receiver(post_save, sender=Student) def create_user_for_student(sender: Student, instance: Student, created, **kwargs): if created and not instance.user: diff --git a/techdb/poetry.lock b/techdb/poetry.lock index 8c1821d..d1f695f 100644 --- a/techdb/poetry.lock +++ b/techdb/poetry.lock @@ -51,6 +51,21 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""} argon2 = ["argon2-cffi (>=19.1.0)"] bcrypt = ["bcrypt"] +[[package]] +name = "django-colorfield" +version = "0.14.0" +description = "color field for django models with a nice color-picker in the admin." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "django_colorfield-0.14.0-py3-none-any.whl", hash = "sha256:f169c4e7ad8f336e51d4ea81f866346e7d4f336f3766e54e144cd16ea7d84a0e"}, + {file = "django_colorfield-0.14.0.tar.gz", hash = "sha256:478dbd3975a88f2ea2a9afc325faaca05c54ebf04ec985ce130f6dea39dfb899"}, +] + +[package.dependencies] +Pillow = ">=9.0.0" + [[package]] name = "django-durationwidget" version = "1.0.5" @@ -113,6 +128,115 @@ python-monkey-business = ">=1.0.0" dev = ["Pillow", "black", "dj-database-url", "django-selenosis", "flake8", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "selenium"] test = ["Pillow", "dj-database-url", "django-selenosis", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "selenium"] +[[package]] +name = "pillow" +version = "12.0.0" +description = "Python Imaging Library (fork)" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "pillow-12.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:3adfb466bbc544b926d50fe8f4a4e6abd8c6bffd28a26177594e6e9b2b76572b"}, + {file = "pillow-12.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ac11e8ea4f611c3c0147424eae514028b5e9077dd99ab91e1bd7bc33ff145e1"}, + {file = "pillow-12.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d49e2314c373f4c2b39446fb1a45ed333c850e09d0c59ac79b72eb3b95397363"}, + {file = "pillow-12.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c7b2a63fd6d5246349f3d3f37b14430d73ee7e8173154461785e43036ffa96ca"}, + {file = "pillow-12.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d64317d2587c70324b79861babb9c09f71fbb780bad212018874b2c013d8600e"}, + {file = "pillow-12.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d77153e14b709fd8b8af6f66a3afbb9ed6e9fc5ccf0b6b7e1ced7b036a228782"}, + {file = "pillow-12.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32ed80ea8a90ee3e6fa08c21e2e091bba6eda8eccc83dbc34c95169507a91f10"}, + {file = "pillow-12.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c828a1ae702fc712978bda0320ba1b9893d99be0badf2647f693cc01cf0f04fa"}, + {file = "pillow-12.0.0-cp310-cp310-win32.whl", hash = "sha256:bd87e140e45399c818fac4247880b9ce719e4783d767e030a883a970be632275"}, + {file = "pillow-12.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:455247ac8a4cfb7b9bc45b7e432d10421aea9fc2e74d285ba4072688a74c2e9d"}, + {file = "pillow-12.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:6ace95230bfb7cd79ef66caa064bbe2f2a1e63d93471c3a2e1f1348d9f22d6b7"}, + {file = "pillow-12.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0fd00cac9c03256c8b2ff58f162ebcd2587ad3e1f2e397eab718c47e24d231cc"}, + {file = "pillow-12.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3475b96f5908b3b16c47533daaa87380c491357d197564e0ba34ae75c0f3257"}, + {file = "pillow-12.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:110486b79f2d112cf6add83b28b627e369219388f64ef2f960fef9ebaf54c642"}, + {file = "pillow-12.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5269cc1caeedb67e6f7269a42014f381f45e2e7cd42d834ede3c703a1d915fe3"}, + {file = "pillow-12.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa5129de4e174daccbc59d0a3b6d20eaf24417d59851c07ebb37aeb02947987c"}, + {file = "pillow-12.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bee2a6db3a7242ea309aa7ee8e2780726fed67ff4e5b40169f2c940e7eb09227"}, + {file = "pillow-12.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:90387104ee8400a7b4598253b4c406f8958f59fcf983a6cea2b50d59f7d63d0b"}, + {file = "pillow-12.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc91a56697869546d1b8f0a3ff35224557ae7f881050e99f615e0119bf934b4e"}, + {file = "pillow-12.0.0-cp311-cp311-win32.whl", hash = "sha256:27f95b12453d165099c84f8a8bfdfd46b9e4bda9e0e4b65f0635430027f55739"}, + {file = "pillow-12.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b583dc9070312190192631373c6c8ed277254aa6e6084b74bdd0a6d3b221608e"}, + {file = "pillow-12.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:759de84a33be3b178a64c8ba28ad5c135900359e85fb662bc6e403ad4407791d"}, + {file = "pillow-12.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53561a4ddc36facb432fae7a9d8afbfaf94795414f5cdc5fc52f28c1dca90371"}, + {file = "pillow-12.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71db6b4c1653045dacc1585c1b0d184004f0d7e694c7b34ac165ca70c0838082"}, + {file = "pillow-12.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2fa5f0b6716fc88f11380b88b31fe591a06c6315e955c096c35715788b339e3f"}, + {file = "pillow-12.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82240051c6ca513c616f7f9da06e871f61bfd7805f566275841af15015b8f98d"}, + {file = "pillow-12.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55f818bd74fe2f11d4d7cbc65880a843c4075e0ac7226bc1a23261dbea531953"}, + {file = "pillow-12.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b87843e225e74576437fd5b6a4c2205d422754f84a06942cfaf1dc32243e45a8"}, + {file = "pillow-12.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c607c90ba67533e1b2355b821fef6764d1dd2cbe26b8c1005ae84f7aea25ff79"}, + {file = "pillow-12.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21f241bdd5080a15bc86d3466a9f6074a9c2c2b314100dd896ac81ee6db2f1ba"}, + {file = "pillow-12.0.0-cp312-cp312-win32.whl", hash = "sha256:dd333073e0cacdc3089525c7df7d39b211bcdf31fc2824e49d01c6b6187b07d0"}, + {file = "pillow-12.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe611163f6303d1619bbcb653540a4d60f9e55e622d60a3108be0d5b441017a"}, + {file = "pillow-12.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:7dfb439562f234f7d57b1ac6bc8fe7f838a4bd49c79230e0f6a1da93e82f1fad"}, + {file = "pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643"}, + {file = "pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4"}, + {file = "pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399"}, + {file = "pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5"}, + {file = "pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b"}, + {file = "pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3"}, + {file = "pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07"}, + {file = "pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e"}, + {file = "pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344"}, + {file = "pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27"}, + {file = "pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79"}, + {file = "pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098"}, + {file = "pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905"}, + {file = "pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a"}, + {file = "pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3"}, + {file = "pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced"}, + {file = "pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b"}, + {file = "pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d"}, + {file = "pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a"}, + {file = "pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe"}, + {file = "pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee"}, + {file = "pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef"}, + {file = "pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9"}, + {file = "pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b"}, + {file = "pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47"}, + {file = "pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9"}, + {file = "pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2"}, + {file = "pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a"}, + {file = "pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b"}, + {file = "pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad"}, + {file = "pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01"}, + {file = "pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c"}, + {file = "pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e"}, + {file = "pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e"}, + {file = "pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9"}, + {file = "pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab"}, + {file = "pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b"}, + {file = "pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b"}, + {file = "pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0"}, + {file = "pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6"}, + {file = "pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6"}, + {file = "pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1"}, + {file = "pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e"}, + {file = "pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca"}, + {file = "pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925"}, + {file = "pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8"}, + {file = "pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4"}, + {file = "pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52"}, + {file = "pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a"}, + {file = "pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7"}, + {file = "pillow-12.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b22bd8c974942477156be55a768f7aa37c46904c175be4e158b6a86e3a6b7ca8"}, + {file = "pillow-12.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:805ebf596939e48dbb2e4922a1d3852cfc25c38160751ce02da93058b48d252a"}, + {file = "pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae81479f77420d217def5f54b5b9d279804d17e982e0f2fa19b1d1e14ab5197"}, + {file = "pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeaefa96c768fc66818730b952a862235d68825c178f1b3ffd4efd7ad2edcb7c"}, + {file = "pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09f2d0abef9e4e2f349305a4f8cc784a8a6c2f58a8c4892eea13b10a943bd26e"}, + {file = "pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bdee52571a343d721fb2eb3b090a82d959ff37fc631e3f70422e0c2e029f3e76"}, + {file = "pillow-12.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b290fd8aa38422444d4b50d579de197557f182ef1068b75f5aa8558638b8d0a5"}, + {file = "pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"] +tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] +xmp = ["defusedxml"] + [[package]] name = "psycopg2-binary" version = "2.9.11" @@ -255,4 +379,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "91722fc2a809ab1409253b5c1e7aa817cd0f98914360a28772e4c2439f4a6b72" +content-hash = "8d8926bda0e1b7cdb8a1e57168dd3acc0f3240e34d4e2faf613b844d989dad30" diff --git a/techdb/pyproject.toml b/techdb/pyproject.toml index 67c2772..a3fd22f 100644 --- a/techdb/pyproject.toml +++ b/techdb/pyproject.toml @@ -12,6 +12,7 @@ psycopg2-binary = "^2.9.10" django-nested-admin = "^4.1.1" django-durationwidget = "^1.0.5" django-import-export = "^4.3.13" +django-colorfield = "^0.14.0" [build-system] diff --git a/techdb/techdb/settings.py b/techdb/techdb/settings.py index e4928bb..3257511 100644 --- a/techdb/techdb/settings.py +++ b/techdb/techdb/settings.py @@ -40,7 +40,8 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'nested_admin', 'flightslot', - 'durationwidget' + 'durationwidget', + 'colorfield' ] MIDDLEWARE = [