7 Commits

12 changed files with 280 additions and 96 deletions

View File

@@ -43,6 +43,7 @@ INSTALLED_APPS = [
'colorfield', 'colorfield',
'import_export', 'import_export',
'django_admin_action_forms', 'django_admin_action_forms',
'polymorphic'
] ]
# Import Export plugin settings # Import Export plugin settings
@@ -52,7 +53,7 @@ IMPORT_EXPORT_SKIP_ADMIN_LOG = True
IMPORT_FORMATS = [CSV] IMPORT_FORMATS = [CSV]
MIDDLEWARE = [ MIDDLEWARE = [
'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware', 'flightslot.middleware.RedirectNonSuperuserFromAdminMiddleware', # custom middleware to show "user" page to non superuser
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',

View File

@@ -9,7 +9,3 @@ urlpatterns = [
path('user/', flightslot_user.urls), path('user/', flightslot_user.urls),
path("", lambda r: redirect("/user/")), # la root porta gli utenti nella pagina giusta path("", lambda r: redirect("/user/")), # la root porta gli utenti nella pagina giusta
] ]
admin.site.site_header = "Flight Scheduler 🛫"
admin.site.site_title = "Flight Scheduler 🛫"
admin.site.index_title = "Welcome to CantorAir Flight Scheduler Portal"

View File

@@ -10,7 +10,7 @@ from typing import List
from ..models.weekpref import WeekPreference from ..models.weekpref import WeekPreference
from ..models.missions import Training from ..models.missions import Training
from ..models.hourbuildings import HourBuilding,HourBuildingLeg from ..models.hourbuildings import HourBuilding, HourBuildingLegFlight, HourBuildingLegStop, HourBuildingLegBase
def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) -> HttpResponse: def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) -> HttpResponse:
@@ -41,7 +41,7 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
# Header titles # Header titles
days = [f"{datetime.strptime(f"{year} {week} {x}", "%G %V %u").strftime("%A")} {datetime.strptime(f"{year} {week} {x}", "%G %V %u").day}" for x in range(1,8)] days = [f"{datetime.strptime(f"{year} {week} {x}", "%G %V %u").strftime("%A")} {datetime.strptime(f"{year} {week} {x}", "%G %V %u").day}" for x in range(1,8)]
headers = ["Week", "Student", "Course", *days, "Cell.", "Mail", "Notes"] headers = ["Week", "Student", "Course", *days, "Notes", "Cell.", "Mail"]
# Header fields positions # Header fields positions
week_index: int = headers.index("Week") + 1 week_index: int = headers.index("Week") + 1
@@ -59,7 +59,12 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
# Cell styles # Cell styles
border_thick: Side = Side(style='thick', color='000000') border_thick: Side = Side(style='thick', color='000000')
border_thin: Side = Side(style='thin', color='000000', border_style='dashed')
border_bottom: Border = Border(bottom=border_thick) border_bottom: Border = Border(bottom=border_thick)
border_bottom_thin: Border = Border(bottom=border_thin)
border_left: Border = Border(left=border_thick)
border_right: Border = Border(right=border_thick)
border_right_thin: Border = Border(right=border_thin)
border_all: Border = Border(bottom=border_thick, top=border_thick, left=border_thick, right=None) border_all: Border = Border(bottom=border_thick, top=border_thick, left=border_thick, right=None)
# Scrittura header # Scrittura header
@@ -102,15 +107,14 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
mission_name if t.sunday else "" mission_name if t.sunday else ""
] ]
mission_notes = t.notes if t.notes else "--" mission_notes = t.notes if t.notes else "--"
mission_data.append([str(q.week), *student_data, *mission_days, student_phone, student_email, mission_notes]) mission_data.append([str(q.week), *student_data, *mission_days, mission_notes, student_phone, student_email, ])
# Fill HourBuilding rows # Fill HourBuilding rows
hb_name: str hb_name: str
hb_days: List[str] hb_days: List[str]
hb_notes: str
hb_data: List[List[str]] = [] hb_data: List[List[str]] = []
for h in HourBuilding.objects.filter(weekpref = q.id): for h in HourBuilding.objects.filter(weekpref = q.id):
hb_name = f"HB-{h.aircraft}\nVedi Note ->" hb_name = f"HB - {h.aircraft}\nVedi Note ->"
hb_days = [ hb_days = [
hb_name if h.monday else "", hb_name if h.monday else "",
hb_name if h.tuesday else "", hb_name if h.tuesday else "",
@@ -120,53 +124,71 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
hb_name if h.saturday else "", hb_name if h.saturday else "",
hb_name if h.sunday else "" hb_name if h.sunday else ""
] ]
hb_notes = f"{h.notes}\n----\n" if h.notes else "" hb_notes: List[str] = [f"{h.notes}", "---"] if h.notes else []
hb_legs = HourBuildingLeg.objects.filter(hb_id = h.id) hb_legs_all = HourBuildingLegBase.objects.filter(hb_id = h.id)
for hh in hb_legs: for hh in hb_legs_all:
hb_notes += f"{hh.departure} -> {hh.destination} [{hh.time}]\n" if not hh.stop else f"STOP at {hh.departure} [{hh.time}]\n" time_str: str = ':'.join(str(hh.time).split(':')[:2]) # keep only hours and minutes
hb_notes.strip('\n') if isinstance(hh, HourBuildingLegFlight):
hb_data.append([str(q.week), *student_data, *hb_days, str(q.student.phone), q.student.email, hb_notes]) hb_notes.append(f"{hh.departure} -> {hh.destination} [{time_str}]{f' / PAX: {hh.pax.capitalize()}' if hh.pax else ''}")
elif isinstance(hh, HourBuildingLegStop):
hb_notes.append(f"STOP [{time_str}] {"Refuel" if hh.refuel else ""}" )
hb_data.append([str(q.week), *student_data, *hb_days, "\n".join(hb_notes), str(q.student.phone), q.student.email])
# Build rows for table # Build rows for table
all_data: List[List[str]] = mission_data + hb_data all_data: List[List[str]] = mission_data + hb_data
student_start: int = row + row_offset student_start: int = row + row_offset
for r in all_data: for row_content in all_data:
for j, c in enumerate(r, start=1): for c, cell_content in enumerate(row_content, start=1):
cell = ws.cell(row = row + row_offset, column = j, value = c) cell = ws.cell(row = row + row_offset, column = c, value = cell_content)
cell.alignment = center cell.alignment = center
# Format Student Name # Format Student Name
if j == student_index: if c == student_index:
cell.font = bold_black cell.font = bold_black
# Format Course Column # Format Course Column with color
if j == course_index and q.student.course: elif c == course_index and q.student.course:
cell.font = bold_black cell.font = bold_black
cell.fill = PatternFill("solid", fgColor=str(q.student.course.color).lstrip('#').lower()) cell.fill = PatternFill("solid", fgColor=str(q.student.course.color).lstrip('#').lower())
# Add internal borders between mix cells and notes
prev_cell_val: str = r[0] elif c > course_index and c <= note_index:
cell.border = border_bottom_thin + border_right_thin
# Fill mix cells if the cell is not empty
if c > course_index and c < note_index:
if len(cell_content):
cell.fill = PatternFill('solid', fgColor="f0f0f0")
prev_cell_val: str = row_content[0]
merge_start: bool = False merge_start: bool = False
merge_col_start: int = 1 merge_col_start: int = 1
for c, v in enumerate(r, start=1): for c, cell_content in enumerate(row_content, start=1):
# Merge cells in the row # Merge cells in the row
if v == prev_cell_val and not merge_start: if cell_content == prev_cell_val and not merge_start:
merge_start = True merge_start = True
merge_col_start = c-1 # start merge from previous column merge_col_start = c-1 # start merge from previous column
elif v != prev_cell_val and merge_start: elif cell_content != prev_cell_val and merge_start:
merge_start = False merge_start = False
ws.merge_cells(start_row=row+row_offset, ws.merge_cells(start_row=row+row_offset,
end_row=row+row_offset, end_row=row+row_offset,
start_column=max(merge_col_start,1), start_column=max(merge_col_start,1),
end_column=max(c-1,1)) # end merge to previous column end_column=max(c-1,1)) # end merge to previous column
prev_cell_val = v prev_cell_val = cell_content
# Incement row counter # Incement row counter
row_offset += 1 row_offset += 1
# End week preferences for this student # End week preferences for this student
student_end: int = row + row_offset -1 student_end: int = row + row_offset - 1
# Add thick border to the last cell row of this student
for c in range(course_index, mail_index + 1):
ws.cell(row=student_end, column=c).border = Border(bottom=border_thick, right=border_thin)
# And for last column also a vertical border all student high
if c == mail_index:
for row_content in range(student_start, student_end + 1):
ws.cell(row=row_content, column=c).border += border_right
# Merge Week, thick border # Merge Week, thick border
# ws.cell(row=student_start, column=week_index).border = border_all ws.cell(row=student_start, column=week_index).border = border_all
# ws.merge_cells(start_row=student_start, end_row=student_end, start_column=week_index, end_column=week_index) ws.merge_cells(start_row=student_start, end_row=student_end, start_column=week_index, end_column=week_index)
# Merge Name, thick border # Merge Name, thick border
ws.cell(row=student_start, column=student_index).border = border_all ws.cell(row=student_start, column=student_index).border = border_all
ws.merge_cells(start_row=student_start, end_row=student_end, start_column=student_index, end_column=student_index) ws.merge_cells(start_row=student_start, end_row=student_end, start_column=student_index, end_column=student_index)
@@ -177,13 +199,15 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
# Merge Mail # Merge Mail
ws.merge_cells(start_row=student_start, end_row=student_end, start_column=mail_index, end_column=mail_index) ws.merge_cells(start_row=student_start, end_row=student_end, start_column=mail_index, end_column=mail_index)
# Add thick border to the last cell row of this student
for i in range(course_index, len(all_data[0])+1):
ws.cell(row=student_end, column=i).border = border_bottom
# Keep the largest column # Keep the largest column
max_len: List[int] = []
for column_cells in ws.columns: for column_cells in ws.columns:
length: int = max(len(str(cell.value)) for cell in column_cells) for cell in column_cells:
cell_lines = str(cell.value).splitlines()
if len(cell_lines) == 0:
continue
max_len.append(max([len(ll) for ll in cell_lines]))
length: int = max(max_len)
col_letter: str = "A" col_letter: str = "A"
if column_cells[0].column: if column_cells[0].column:
col_letter = get_column_letter(column_cells[0].column) col_letter = get_column_letter(column_cells[0].column)
@@ -191,8 +215,7 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
### End of Student Loop ### ### End of Student Loop ###
# Save document in HttpResponse # Save document in HttpResponse
wb.save(response) wb.save(response)
return response return response

View File

@@ -10,6 +10,7 @@ from .admins.course_adm import CourseAdmin
from .admins.student_adm import StudentAdmin from .admins.student_adm import StudentAdmin
from .admins.mission_adm import MissionProfileAdmin from .admins.mission_adm import MissionProfileAdmin
from .admins.weekpred_adm import WeekPreferenceAdmin from .admins.weekpred_adm import WeekPreferenceAdmin
#from .admins.hourbuilding_adm import HourBuilding, HourBuildingInLine
from django.contrib.admin import AdminSite from django.contrib.admin import AdminSite
@@ -31,6 +32,10 @@ flightslot_user = FlightSlotUserSite(name="user_site")
# registra SOLO i modelli autorizzati # registra SOLO i modelli autorizzati
flightslot_user.register(WeekPreference, WeekPreferenceAdmin) flightslot_user.register(WeekPreference, WeekPreferenceAdmin)
admin.site.site_header = "Flight Scheduler Admin 🛫"
admin.site.site_title = "Flight Scheduler Admin 🛫"
admin.site.index_title = "Welcome to CantorAir Flight Scheduler Administrator Portal"
admin.site.register(Course, CourseAdmin) admin.site.register(Course, CourseAdmin)
admin.site.register(MissionProfile, MissionProfileAdmin) admin.site.register(MissionProfile, MissionProfileAdmin)
admin.site.register(Student, StudentAdmin) admin.site.register(Student, StudentAdmin)

View File

@@ -7,36 +7,63 @@ from django.http import HttpRequest
from durationwidget.widgets import TimeDurationWidget from durationwidget.widgets import TimeDurationWidget
from ..models.hourbuildings import HourBuilding, HourBuildingLeg from ..models.hourbuildings import HourBuilding, HourBuildingLegBase, HourBuildingLegFlight, HourBuildingLegStop
from ..models.weekpref import WeekPreference from ..models.weekpref import WeekPreference
from datetime import date from datetime import date
class HourBuildingLegForm(forms.ModelForm): class HourBuildingLegFlightForm(forms.ModelForm):
class Meta: class Meta:
model = HourBuildingLeg model = HourBuildingLegFlight
fields = '__all__' fields = "__all__"
widgets = { widgets = {
'time': TimeDurationWidget(show_days=False, "time": TimeDurationWidget(show_days=False,
show_seconds=False show_seconds=False,
attrs={
"style": (
"margin-right:5px; margin-left:5px; width:40px; min:0; max:5"
) )
})
}
class HourBuildingLegStopForm(forms.ModelForm):
class Meta:
model = HourBuildingLegStop
fields = "__all__"
widgets = {
"time": TimeDurationWidget(show_days=False,
show_seconds=False,
attrs={
"style": (
"margin-right:5px; margin-left:5px; width:40px;"
)
})
} }
# Register your models here. # Register your models here.
class HourBuildingLegInline(nested_admin.NestedTabularInline): class HourBuildingLegBaseInLine(nested_admin.NestedStackedPolymorphicInline):
model = HourBuildingLeg model = HourBuildingLegBase
form = HourBuildingLegForm fk_name = "hb"
extra = 0 verbose_name_plural = "Hour Building Legs"
fk_name = 'hb'
max_num = 5 class HourBuildingLegFlightInLine(nested_admin.NestedStackedPolymorphicInline.Child):
formfield_overrides = { model = HourBuildingLegFlight
models.CharField: {'widget': TextInput(attrs={'size':'20'})}, form = HourBuildingLegFlightForm
models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':35})}, fk_name = "hourbuildinglegbase_ptr"
} fields = ("departure", "time", "destination", "pax", )
hide_title = True
class HourBuildingLegStopInLine(nested_admin.NestedStackedPolymorphicInline.Child):
model = HourBuildingLegStop
form = HourBuildingLegFlightForm
fk_name = "hourbuildinglegbase_ptr"
fields = ("time", "refuel", )
child_inlines = (HourBuildingLegFlightInLine, HourBuildingLegStopInLine, )
# If user is a student deny edit permission for week past the current one # If user is a student deny edit permission for week past the current one
def has_change_permission(self, request: HttpRequest, obj: HourBuilding | None = None): def has_change_permission(self, request: HttpRequest, obj: HourBuilding | None = None):
if hasattr(request.user, 'student') and obj: if hasattr(request.user, "student") and obj:
current_week = date.today().isocalendar().week current_week = date.today().isocalendar().week
if not obj.DoesNotExist and current_week > obj.weekpref.week: if not obj.DoesNotExist and current_week > obj.weekpref.week:
return False return False
@@ -45,26 +72,16 @@ class HourBuildingLegInline(nested_admin.NestedTabularInline):
def has_delete_permission(self, request: HttpRequest, obj: HourBuilding | None = None): def has_delete_permission(self, request: HttpRequest, obj: HourBuilding | None = None):
return self.has_change_permission(request=request, obj=obj) return self.has_change_permission(request=request, obj=obj)
class HourBuildingInLine(nested_admin.NestedTabularInline): class HourBuildingInLine(nested_admin.NestedTabularInline):
model = HourBuilding model = HourBuilding
inlines = (HourBuildingLegBaseInLine,)
extra = 0 extra = 0
inlines = [HourBuildingLegInline]
fk_name = 'weekpref'
verbose_name_plural = "Hour Building"
max_num = 7 max_num = 7
fk_name = "weekpref"
verbose_name_plural = "Hour Buildings"
formfield_overrides = { formfield_overrides = {
models.CharField: {'widget': TextInput(attrs={'size':'20'})}, models.CharField: {"widget": TextInput(attrs={"size":"20"})},
models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':35})}, models.TextField: {"widget": Textarea(attrs={"rows":4, "cols":35})},
} }
# If user is a student deny edit permission for week past the current one
def has_change_permission(self, request: HttpRequest, obj: WeekPreference | None = None):
if hasattr(request.user, 'student') and obj:
current_week = date.today().isocalendar().week
if current_week > obj.week:
return False
return True
def has_delete_permission(self, request: HttpRequest, obj: WeekPreference | None = None):
return self.has_change_permission(request=request, obj=obj)

View File

@@ -18,8 +18,8 @@ from ..actions.exportweek import export_selected
from datetime import date from datetime import date
class WeekPreferenceAdmin(nested_admin.NestedModelAdmin): class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin):
inlines = (TrainingInLIne, HourBuildingInLine,) inlines = (TrainingInLIne, HourBuildingInLine, )
list_display = ("week", "student__surname","student__name", "student__course", "course_color", "student_brief_mix",) list_display = ("week", "student__surname","student__name", "student__course", "course_color", "student_brief_mix",)
list_filter = ("week", "student__course", "student",) list_filter = ("week", "student__course", "student",)
actions = ("export",) actions = ("export",)

View File

@@ -0,0 +1,58 @@
# Generated by Django 5.2.8 on 2025-11-21 11:20
import datetime
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('flightslot', '0017_alter_missionprofile_mtype_alter_weekpreference_week'),
]
operations = [
migrations.CreateModel(
name='HourBuildingLegBase',
fields=[
('id', models.BigAutoField(primary_key=True, serialize=False)),
('time', models.DurationField(default=datetime.timedelta(seconds=3600))),
('hb', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='flightslot.hourbuilding')),
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
),
migrations.CreateModel(
name='HourBuildingLegFlight',
fields=[
('hourbuildinglegbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='flightslot.hourbuildinglegbase')),
('departure', models.CharField(default='LILV', max_length=4)),
('destination', models.CharField(default='LILV', max_length=4)),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('flightslot.hourbuildinglegbase',),
),
migrations.CreateModel(
name='HourBuildingLegStop',
fields=[
('hourbuildinglegbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='flightslot.hourbuildinglegbase')),
('location', models.CharField(default='LILV', max_length=4)),
('refuel', models.BooleanField(default=False)),
],
options={
'abstract': False,
'base_manager_name': 'objects',
},
bases=('flightslot.hourbuildinglegbase',),
),
migrations.DeleteModel(
name='HourBuildingLeg',
),
]

View File

@@ -0,0 +1,37 @@
# Generated by Django 5.2.8 on 2025-11-21 16:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('flightslot', '0018_hourbuildinglegbase_hourbuildinglegflight_and_more'),
]
operations = [
migrations.RemoveField(
model_name='hourbuildinglegstop',
name='location',
),
migrations.AddField(
model_name='hourbuildinglegflight',
name='pax',
field=models.CharField(max_length=16, null=True),
),
migrations.AlterField(
model_name='hourbuildinglegbase',
name='time',
field=models.DurationField(),
),
migrations.AlterField(
model_name='hourbuildinglegflight',
name='departure',
field=models.CharField(max_length=4),
),
migrations.AlterField(
model_name='hourbuildinglegflight',
name='destination',
field=models.CharField(max_length=4),
),
]

View File

@@ -1,11 +1,11 @@
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.db import models from django.db import models
from datetime import timedelta
from polymorphic.models import PolymorphicModel
from ..models.weekpref import WeekPreference from ..models.weekpref import WeekPreference
from ..models.aircrafts import AircraftTypes from ..models.aircrafts import AircraftTypes
class HourBuilding(models.Model): class HourBuilding(models.Model):
id = models.BigAutoField( id = models.BigAutoField(
primary_key=True primary_key=True
@@ -66,7 +66,7 @@ class HourBuilding(models.Model):
def __str__(self): def __str__(self):
return f"Hour Building: {self.aircraft}" return f"Hour Building: {self.aircraft}"
class HourBuildingLeg(models.Model): class HourBuildingLegBase(PolymorphicModel):
id = models.BigAutoField( id = models.BigAutoField(
primary_key=True primary_key=True
) )
@@ -76,32 +76,63 @@ class HourBuildingLeg(models.Model):
on_delete=models.CASCADE on_delete=models.CASCADE
) )
time = models.DurationField(
null=False,
blank=False
)
# Change displayed name in the inline form
class Meta(PolymorphicModel.Meta):
verbose_name = "Flight Leg or Stop"
verbose_name_plural = "Flight Legs or Stops"
def __str__(self):
return f"Hour Building Leg"
class HourBuildingLegFlight(HourBuildingLegBase):
departure = models.CharField( departure = models.CharField(
null=False, null=False,
blank=False, blank=False,
default="LILV",
max_length=4 max_length=4
) )
destination = models.CharField( destination = models.CharField(
null=False, null=False,
blank=False, blank=False,
default="LILV",
max_length=4 max_length=4
) )
time = models.DurationField( pax = models.CharField(
null=False, null=True,
default = timedelta(hours=1) blank=True,
max_length=16,
verbose_name="Pax (optional)"
) )
stop = models.BooleanField( # Change displayed name in the inline form
class Meta(HourBuildingLegBase.Meta):
verbose_name = "Flight leg"
verbose_name_plural = "Flight legs"
def save(self, *args, **kwargs):
self.departure = self.departure.upper().strip()
self.destination = self.destination.upper().strip()
super().save(*args, **kwargs)
def __str__(self):
return f"{self.departure} -> {self.destination}"
class HourBuildingLegStop(HourBuildingLegBase):
refuel = models.BooleanField(
default=False default=False
) )
# Change displayed name in the inline form
class Meta(HourBuildingLegBase.Meta):
verbose_name = "Stop"
verbose_name_plural = "Stops"
def __str__(self): def __str__(self):
if self.stop: return f"Refuel" if self.refuel else f"No Refuel"
return "Refuelling Stop"
else:
return f"Flight Leg: {self.departure} -> {self.destination}"

View File

@@ -51,24 +51,24 @@ class Student(models.Model):
# Override save method to add user for login upon Student creation # Override save method to add user for login upon Student creation
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
creating = self.pk is None creating: bool = self.pk is None
super().save(*args, **kwargs) super().save(*args, **kwargs)
if creating and not self.user: if creating and not self.user:
username = f"{self.name.lower()}.{self.surname.lower()}" username: str = f"{self.name.lower()}.{self.surname.lower()}"
# Avoid username conflict with progressive number # Avoid username conflict with progressive number
base_username = username base_username = username
counter = 1 counter: int = 1
while User.objects.filter(username=username).exists(): while User.objects.filter(username=username).exists():
username = f"{base_username}{counter}" username = f"{base_username}{counter}"
counter += 1 counter += 1
# Create user # Create user
user = User.objects.create_user( user: User = User.objects.create_user(
first_name=self.name, first_name=self.name,
last_name=self.surname, last_name=self.surname,
username=username, username=username,
email=self.email, email=self.email,
password=self.default_password(), password=self.default_password(),
is_staff=True is_staff=True # allows access to admin page
) )
student_group, _ = Group.objects.get_or_create(name="StudentGroup") student_group, _ = Group.objects.get_or_create(name="StudentGroup")

23
cntmanage/poetry.lock generated
View File

@@ -2,14 +2,14 @@
[[package]] [[package]]
name = "asgiref" name = "asgiref"
version = "3.10.0" version = "3.11.0"
description = "ASGI specs, helper code, and adapters" description = "ASGI specs, helper code, and adapters"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "asgiref-3.10.0-py3-none-any.whl", hash = "sha256:aef8a81283a34d0ab31630c9b7dfe70c812c95eba78171367ca8745e88124734"}, {file = "asgiref-3.11.0-py3-none-any.whl", hash = "sha256:1db9021efadb0d9512ce8ffaf72fcef601c7b73a8807a1bb2ef143dc6b14846d"},
{file = "asgiref-3.10.0.tar.gz", hash = "sha256:d89f2d8cd8b56dada7d52fa7dc8075baa08fb836560710d38c292a7a3f78c04e"}, {file = "asgiref-3.11.0.tar.gz", hash = "sha256:13acff32519542a1736223fb79a715acdebe24286d98e8b164a73085f40da2c4"},
] ]
[package.extras] [package.extras]
@@ -143,6 +143,21 @@ python-monkey-business = ">=1.0.0"
dev = ["Pillow", "black", "dj-database-url", "django-selenosis", "flake8", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "selenium"] 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"] test = ["Pillow", "dj-database-url", "django-selenosis", "pytest", "pytest-cov", "pytest-django", "pytest-xdist", "selenium"]
[[package]]
name = "django-polymorphic"
version = "4.1.0"
description = "Seamless polymorphic inheritance for Django models"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "django_polymorphic-4.1.0-py3-none-any.whl", hash = "sha256:0ce3984999e103a0d1a434a5c5617f2c7f990dc3d5fb3585ce0fadadf9ff90ea"},
{file = "django_polymorphic-4.1.0.tar.gz", hash = "sha256:4438d95a0aef6c4307cd6c83ead387e1142ce80b65188a931ec2f0dbdd9bfc51"},
]
[package.dependencies]
Django = ">=3.2"
[[package]] [[package]]
name = "et-xmlfile" name = "et-xmlfile"
version = "2.0.0" version = "2.0.0"
@@ -421,4 +436,4 @@ files = [
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = "^3.12" python-versions = "^3.12"
content-hash = "6bf43236f441d8b6bf8d1928910d169d3b29cfa499bb7d09d97ea227f8115658" content-hash = "e932d0af75c888d83fecefaaad1d018c508881a3bfde2ea640a82790e3567855"

View File

@@ -17,6 +17,7 @@ django-import-export = "^4.3.13"
django-colorfield = "^0.14.0" django-colorfield = "^0.14.0"
openpyxl = "^3.1.5" openpyxl = "^3.1.5"
django-admin-action-forms = "^2.2.1" django-admin-action-forms = "^2.2.1"
django-polymorphic = "^4.1.0"
[build-system] [build-system]