5 Commits

10 changed files with 144 additions and 10 deletions

View File

@@ -31,6 +31,7 @@ def send_mail_password(request: HttpRequest, queryset: QuerySet[Student]) -> Non
img.add_header("Content-Disposition", "inline", filename="cantorair.png")
# build mail list filling template
queryset = queryset.filter(mail_sent=False)
mails: List[EmailMultiAlternatives] = []
for student in queryset:
if not student.user or not student.email: # skip student if has not an associated user
@@ -56,18 +57,25 @@ def send_mail_password(request: HttpRequest, queryset: QuerySet[Student]) -> Non
mail.attach(filename=img)
mail.attach_alternative(content=html_message, mimetype="text/html")
mails.append(mail)
student.mail_sent = True
student.save()
except Exception as e:
messages.error(request=request, message=f"General Error: {e}")
if len(mails) == 0:
messages.warning(request=request, message="No email will be sent")
return
# Open only one conenction and send mass email
try:
sent: int = 0
with get_connection() as conn:
conn.send_messages(mails)
sent = conn.send_messages(mails)
except SMTPException as e:
messages.error(request=request, message=f"Send Mail SMTP error: {e.strerror}")
except Exception as e:
messages.error(request=request, message=f"Send Mail General error: {e}")
else:
messages.success(request=request, message=f"Successfully sent {len(mails)} messages")
messages.success(request=request, message=f"Successfully sent {sent} messages")
return

View File

@@ -69,7 +69,7 @@ class ChangeAircraftForm(AdminActionForm):
class StudentAdmin(ImportMixin, AdminConfirmMixin, AdminActionFormsMixin, admin.ModelAdmin):
model = Student
list_display = ("surname", "name", "course", "course_color", "email", "phone", "username", "password", "active", )
list_display = ("surname", "name", "course", "course_color", "email", "phone", "username", "password", "active", "mail_sent")
list_filter = ("course", "active", )
search_fields = ("surname", "name", "phone", "email", )
actions = ("change_course", "deactivate_students", "change_aircraft", "send_mail", )

View File

@@ -19,14 +19,15 @@ from ..custom.colortag import course_color
from ..custom.student_permissions import has_edit_permission, has_week_add_permission
from ..actions.exportweek import export_selected
from datetime import date
from typing import Dict, List, Any
from datetime import date, datetime
from typing import Dict, List, Tuple, Any
class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin):
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", "inserted")
list_filter = ("week", "student__course", )
search_fields = ("student__surname","student__name", )
actions = ("export", )
readonly_fields = ("inserted", )
@admin.action(description="Export Selected Preferences")
def export(self, request: HttpRequest, queryset: QuerySet[WeekPreference]) -> HttpResponse | None:
@@ -47,6 +48,12 @@ class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin):
return SafeText("")
return course_color(obj.student.course.color)
# Hide brief mix count and inserted fields for students
def get_list_display(self, request: HttpRequest) -> Tuple:
if hasattr(request.user, "student"):
return ("week", "student__surname", "student__name", "student__course", "course_color", )
return ("week", "student__surname", "student__name", "student__course", "course_color", "student_brief_mix", "inserted")
# If a user is registered as student hide filters
def get_list_filter(self, request: HttpRequest) -> List[str]:
list_filter = super().get_list_filter(request)
@@ -90,7 +97,6 @@ class WeekPreferenceAdmin(nested_admin.NestedPolymorphicModelAdmin):
def get_form(self, request: HttpRequest, obj: WeekPreference | None = None, **kwargs: Dict[str, Any]) -> Form:
form: Form = super().get_form(request, obj, **kwargs)
current_week = date.today().isocalendar().week
# If form contains the week field
if "week" in form.base_fields:
# Set default value as current week

View File

@@ -0,0 +1,29 @@
# Generated by Django 5.2.8 on 2025-12-10 10:18
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('flightslot', '0029_alter_course_ctype'),
]
operations = [
migrations.AddField(
model_name='weekpreference',
name='inserted',
field=models.DateField(default=datetime.date(2025, 12, 10)),
),
migrations.AlterField(
model_name='availability',
name='week',
field=models.PositiveSmallIntegerField(auto_created=True, db_default=50, db_index=True, verbose_name='Week Number'),
),
migrations.AlterField(
model_name='weekpreference',
name='week',
field=models.PositiveSmallIntegerField(auto_created=True, db_default=50, db_index=True, verbose_name='Week Number'),
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 5.2.8 on 2025-12-10 10:28
import phonenumber_field.modelfields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('flightslot', '0030_weekpreference_inserted_alter_availability_week_and_more'),
]
operations = [
migrations.AddField(
model_name='student',
name='mail_sent',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='student',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(db_index=True, max_length=128, null=True, region=None, unique=True),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.8 on 2025-12-10 10:47
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('flightslot', '0031_student_mail_sent_alter_student_phone'),
]
operations = [
migrations.AlterField(
model_name='weekpreference',
name='inserted',
field=models.DateTimeField(default=datetime.datetime(2025, 12, 10, 10, 47, 2, 305637)),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.8 on 2025-12-10 11:02
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('flightslot', '0032_alter_weekpreference_inserted'),
]
operations = [
migrations.AlterField(
model_name='weekpreference',
name='inserted',
field=models.DateTimeField(default=datetime.datetime(2025, 12, 10, 11, 2, 58, 110972)),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.8 on 2025-12-10 11:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('flightslot', '0033_alter_weekpreference_inserted'),
]
operations = [
migrations.AlterField(
model_name='weekpreference',
name='inserted',
field=models.DateTimeField(auto_now_add=True),
),
]

View File

@@ -19,6 +19,7 @@ class Student(models.Model):
phone = modelfields.PhoneNumberField(
null=True,
db_index=True,
unique=True
)
@@ -54,6 +55,11 @@ class Student(models.Model):
Aircraft
)
mail_sent = models.BooleanField(
null=False,
default=False
)
def default_password(self) -> str: # Maximum 4 digits for passowrd
if self.pk:
return f"{self.name.lower()[0]}{self.surname.lower()}{self.id % 10000}"

View File

@@ -24,5 +24,10 @@ class WeekPreference(models.Model):
verbose_name="Student Selection"
)
inserted = models.DateTimeField(
null=False,
auto_now_add=True
)
def __str__(self):
return f"Week {self.week} - {self.student.surname} {self.student.name[0]}."