2 Commits

2 changed files with 125 additions and 21 deletions

View File

@@ -2,7 +2,7 @@ from django.http import HttpRequest, HttpResponse
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from openpyxl import Workbook from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter from openpyxl.utils import get_column_letter
from datetime import date, datetime from datetime import date, datetime
@@ -34,22 +34,33 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
if not ws: if not ws:
raise Exception("Export: cannot select active workbook") raise Exception("Export: cannot select active workbook")
ws.title = f"Week Preferences" ws.title = f"Week Preferences"
ws.page_setup.orientation = ws.ORIENTATION_LANDSCAPE
ws.page_setup.paperSize = ws.PAPERSIZE_A3
ws.page_setup.fitToHeight = 0
ws.page_setup.fitToWidth = 1
# 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, "Cell.", "Mail", "Notes"]
# Header fields positions # Header fields positions
week_index: int = headers.index("Week") + 1
student_index: int = headers.index("Student") + 1 student_index: int = headers.index("Student") + 1
course_index: int = headers.index("Course") + 1 course_index: int = headers.index("Course") + 1
cell_index: int = headers.index("Cell.") + 1 cell_index: int = headers.index("Cell.") + 1
mail_index: int = headers.index("Mail") + 1 mail_index: int = headers.index("Mail") + 1
note_index: int = headers.index("Notes") + 1
# Stile header # Stile header
header_fill = PatternFill("solid", fgColor="0e005c") header_fill = PatternFill("solid", fgColor="0e005c")
bold_white = Font(color="FFFFFF", bold=True) bold_white = Font(color="FFFFFF", bold=True)
bold_black = Font(color="000000", bold=True) bold_black = Font(color="000000", bold=True)
center = Alignment(horizontal="center", vertical="center") center = Alignment(horizontal="center", vertical="center", wrapText=True)
# Cell styles
border_thick: Side = Side(style='thick', color='000000')
border_bottom: Border = Border(bottom=border_thick)
border_all: Border = Border(bottom=border_thick, top=border_thick, left=border_thick, right=None)
# Scrittura header # Scrittura header
for col, h in enumerate(headers, start=1): for col, h in enumerate(headers, start=1):
@@ -58,7 +69,9 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
cell.font = bold_white cell.font = bold_white
cell.alignment = center cell.alignment = center
### Start of Student Loop ###
# Fill worksheet with EVERY training and hb for every student # Fill worksheet with EVERY training and hb for every student
# Each of this iterations fills the table for a student
row: int = 2 row: int = 2
row_offset: int = 0 row_offset: int = 0
for i, q in enumerate(queryset.order_by("student__surname", "student__name", "student__course"), start=1): for i, q in enumerate(queryset.order_by("student__surname", "student__name", "student__course"), start=1):
@@ -111,7 +124,7 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
hb_legs = HourBuildingLeg.objects.filter(hb_id = h.id) hb_legs = HourBuildingLeg.objects.filter(hb_id = h.id)
for hh in hb_legs: for hh in hb_legs:
hb_notes += f"{hh.departure} -> {hh.destination} [{hh.time}]\n" if not hh.stop else f"STOP at {hh.departure} [{hh.time}]\n" hb_notes += f"{hh.departure} -> {hh.destination} [{hh.time}]\n" if not hh.stop else f"STOP at {hh.departure} [{hh.time}]\n"
hb_notes.strip('\r') hb_notes.strip('\n')
hb_data.append([str(q.week), *student_data, *hb_days, str(q.student.phone), q.student.email, hb_notes]) hb_data.append([str(q.week), *student_data, *hb_days, str(q.student.phone), q.student.email, hb_notes])
# Build rows for table # Build rows for table
@@ -126,6 +139,7 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
cell.font = bold_black cell.font = bold_black
# Format Course Column # Format Course Column
if j == course_index and q.student.course: if j == course_index and q.student.course:
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())
prev_cell_val: str = r[0] prev_cell_val: str = r[0]
@@ -149,7 +163,12 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
# 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
# Merge Name
# Merge Week, thick border
# 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)
# Merge Name, thick border
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)
# Merge Course # Merge Course
ws.merge_cells(start_row=student_start, end_row=student_end, start_column=course_index, end_column=course_index) ws.merge_cells(start_row=student_start, end_row=student_end, start_column=course_index, end_column=course_index)
@@ -158,13 +177,20 @@ 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
for column_cells in ws.columns: for column_cells in ws.columns:
length: int = max(len(str(cell.value)) for cell in column_cells) length: int = max(len(str(cell.value)) for cell in column_cells)
col_letter: str = "A" col_letter: str = "A"
if column_cells[0].column: if column_cells[0].column:
get_column_letter(column_cells[0].column) col_letter = get_column_letter(column_cells[0].column)
ws.column_dimensions[col_letter].width = length ws.column_dimensions[col_letter].width = length + 2
### End of Student Loop ###
# Save document in HttpResponse # Save document in HttpResponse
wb.save(response) wb.save(response)

View File

@@ -42,6 +42,17 @@ class HourBuildingLegInline(nested_admin.NestedTabularInline):
fk_name = 'hb' fk_name = 'hb'
max_num = 5 max_num = 5
# 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):
if hasattr(request.user, 'student') and obj:
current_week = date.today().isocalendar().week
if not obj.DoesNotExist and current_week > obj.weekpref.week:
return False
return True
def has_delete_permission(self, request: HttpRequest, obj: HourBuilding | None = None):
return self.has_change_permission(request=request, obj=obj)
class HourBuildingInLine(nested_admin.NestedTabularInline): class HourBuildingInLine(nested_admin.NestedTabularInline):
model = HourBuilding model = HourBuilding
extra = 0 extra = 0
@@ -50,6 +61,17 @@ class HourBuildingInLine(nested_admin.NestedTabularInline):
verbose_name_plural = "Hour Building" verbose_name_plural = "Hour Building"
max_num = 7 max_num = 7
# 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)
class TrainingInLIne(nested_admin.NestedTabularInline): class TrainingInLIne(nested_admin.NestedTabularInline):
model = Training model = Training
form = TrainingForm form = TrainingForm
@@ -58,9 +80,20 @@ class TrainingInLIne(nested_admin.NestedTabularInline):
verbose_name_plural = "Training Missions" verbose_name_plural = "Training Missions"
max_num = 7 max_num = 7
# 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)
class WeekPreferenceAdmin(nested_admin.NestedModelAdmin): class WeekPreferenceAdmin(nested_admin.NestedModelAdmin):
inlines = (TrainingInLIne, HourBuildingInLine,) inlines = (TrainingInLIne, HourBuildingInLine,)
list_display = ("week", "student__name", "student__surname", "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",)
@@ -83,14 +116,23 @@ class WeekPreferenceAdmin(nested_admin.NestedModelAdmin):
return SafeText("") return SafeText("")
return course_color(obj.student.course.color) return course_color(obj.student.course.color)
def has_module_permission(self, request): # If a user is registered as student hide filters
def get_list_filter(self, request):
list_filter = super().get_list_filter(request)
if hasattr(request.user, 'student'): if hasattr(request.user, 'student'):
return False return []
return True return list_filter
# If a user is registered as student do not show actions
def get_actions(self, request):
actions = super().get_actions(request)
if hasattr(request.user, 'student'):
return []
return actions
# If a user is registered as student show only their preferences
def get_queryset(self, request): def get_queryset(self, request):
qs = super().get_queryset(request) qs = super().get_queryset(request)
# If a user is registered as student show only their preferences
if hasattr(request.user, 'student'): if hasattr(request.user, 'student'):
return qs.filter(student=request.user.student) return qs.filter(student=request.user.student)
# If admin show everything # If admin show everything
@@ -98,6 +140,12 @@ class WeekPreferenceAdmin(nested_admin.NestedModelAdmin):
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):
form: forms.Form = super().get_form(request, obj, **kwargs) form: forms.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
form.base_fields['week'].initial = current_week
# If student is current user making request # If student is current user making request
if hasattr(request.user, 'student'): if hasattr(request.user, 'student'):
@@ -105,15 +153,45 @@ class WeekPreferenceAdmin(nested_admin.NestedModelAdmin):
if 'student' in form.base_fields: if 'student' in form.base_fields:
form.base_fields['student'].initial = student form.base_fields['student'].initial = student
form.base_fields['student'].disabled = True form.base_fields['student'].disabled = True
form.base_fields['week'].disabled = True form.base_fields['week'].disabled = True # student cannot change week
# If form contains the week field
if 'week' in form.base_fields:
# Set default value as current week
current_week = date.today().isocalendar().week
form.base_fields['week'].initial = current_week
return form return form
# If user is a student deny edit permission for week past the current one
def has_change_permission(self, request, 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
# If user is a student deny edit permission for week past the current one
def has_add_permission(self, request, 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
# If user is a student deny edit permission for week past the current one
def has_delete_permission(self, request, 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 changeform_view(self, request: HttpRequest, object_id: int | None = None, form_url: str = '', extra_context=None):
extra_context = extra_context or {}
if hasattr(request.user, 'student') and object_id:
current_week = date.today().isocalendar().week
weekpref = WeekPreference.objects.get(id=object_id)
if current_week > weekpref.week:
extra_context['show_save'] = False
extra_context['show_save_and_continue'] = False
extra_context['show_save_and_add_another'] = False
extra_context['show_delete'] = False
return super().changeform_view(request, object_id, form_url, extra_context)
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
# Imposta automaticamente lo studente se non è già valorizzato # Imposta automaticamente lo studente se non è già valorizzato
if hasattr(request.user, 'student') and not obj.student_id: if hasattr(request.user, 'student') and not obj.student_id: