from django.http import HttpRequest, HttpResponse from django.db.models.query import QuerySet from openpyxl import Workbook from openpyxl.styles import Font, PatternFill, Alignment from datetime import date, datetime, timedelta from typing import List import calendar from ..models.weekpref import WeekPreference from ..models.missions import Training from ..models.hourbuildings import HourBuilding def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) -> HttpResponse: if not queryset.first(): raise Exception("Empty queryset") # Init Variables year = date.today().year week = queryset.first().week if queryset.first() else date.today().isocalendar().week # Prepare export filename and http content filename = f"{year}-week{week}_export.xlsx" response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') response['Content-Disposition'] = f'attachment; filename="{filename}"' # Create workbook and sheet wb = Workbook() ws = wb.active if not ws: raise Exception("Export: cannot select active workbook") ws.title = f"{year} Week {week} Preferences" # 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)] headers = ["Week", "Student", "Course", *days, "Cell.", "Mail", "Notes"] # Header fields positions student_index: int = headers.index("Student") + 1 course_index: int = headers.index("Course") + 1 cell_index: int = headers.index("Cell.") + 1 mail_index: int = headers.index("Mail") + 1 # Stile header header_fill = PatternFill("solid", fgColor="0e005c") bold_white = Font(color="FFFFFF", bold=True) bold_black = Font(color="000000", bold=True) center = Alignment(horizontal="center", vertical="center") # Scrittura header for col, h in enumerate(headers, start=1): cell = ws.cell(row=1, column=col, value=h) cell.fill = header_fill cell.font = bold_white cell.alignment = center # Scrittura dati row: int = 2 row_offset: int = 0 # Fill worksheet with EVERY training and hb for every student for i, q in enumerate(queryset.order_by("student__surname", "student__name", "student__course"), start=1): student_data: List[str] student_phone: str = q.student.phone if q.student.phone else "" student_email: str = q.student.email if q.student.course: student_data = [f"{q.student.surname} {q.student.name}", f"{q.student.course.ctype}-{q.student.course.cnumber}"] else: student_data = [f"{q.student.surname} {q.student.name}", f"No Course Assigned"] # Fill Training mission rows mission_name: str mission_days: List[str] mission_notes: str mission_data: List[List[str]] = [] for t in Training.objects.filter(weekpref = q.id): if not t.mission: raise Exception("No Training Mission Assigned") mission_name = f"{t.mission.mtype}-{t.mission.mnum}" mission_days = [ mission_name if t.monday else "", mission_name if t.tuesday else "", mission_name if t.wednesday else "", mission_name if t.thursday else "", mission_name if t.friday else "", mission_name if t.saturday else "", mission_name if t.sunday else "" ] mission_notes = t.notes if t.notes else "--" mission_data.append([str(week), *student_data, *mission_days, student_phone, student_email, mission_notes]) # Fill HourBuilding rows hb_name: str hb_days: List[str] hb_notes: str hb_data: List[List[str]] = [] for h in HourBuilding.objects.filter(weekpref = q.id): hb_name = f"HB-{h.aircraft}" hb_days = [ hb_name if h.monday else "", hb_name if h.tuesday else "", hb_name if h.wednesday else "", hb_name if h.thursday else "", hb_name if h.friday else "", hb_name if h.saturday else "", hb_name if h.sunday else "" ] hb_notes = h.notes if h.notes else "--" hb_data.append([str(week), *student_data, *hb_days, str(q.student.phone), q.student.email, hb_notes]) # Build rows for table student_start: int = row + row_offset for r in mission_data + hb_data: prev_cell_val: str | None = None merge_start: bool = False merge_col_start: int = 1 for j, c in enumerate(r, start=1): if prev_cell_val is None: prev_cell_val = c cell = ws.cell(row = row + row_offset, column = j, value = c) cell.alignment = center # Format Student Name if j == student_index: cell.font = bold_black # Format Course Column if j == course_index and q.student.course: cell.fill = PatternFill("solid", fgColor=str(q.student.course.color).lstrip('#').lower()) # Merge cells in the row if c == prev_cell_val and not merge_start: merge_start = True merge_col_start = max(j-1, 1) elif c != prev_cell_val and merge_start: merge_start = False ws.merge_cells(start_row=row+row_offset, end_row=row+row_offset, start_column=merge_col_start, end_column=max(j-1,1)) # Incement row counter row_offset += 1 # End f preferences for this student student_end: int = row + row_offset -1 # Merge Name ws.merge_cells(start_row=student_start, end_row=student_end, start_column=student_index, end_column=student_index) # Merge Course ws.merge_cells(start_row=student_start, end_row=student_end, start_column=course_index, end_column=course_index) # Merge Cell ws.merge_cells(start_row=student_start, end_row=student_end, start_column=cell_index, end_column=cell_index) # Merge Mail ws.merge_cells(start_row=student_start, end_row=student_end, start_column=mail_index, end_column=mail_index) # Save document in HttpResponse wb.save(response) return response