Improved xlsx formatting
This commit is contained in:
@@ -5,6 +5,7 @@ from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
from ..models.courses import CourseTypes
|
||||
from ..models.missions import Training
|
||||
from ..models.weekpref import WeekPreference
|
||||
from ..models.hourbuildings import HourBuilding, HourBuildingLegFlight, HourBuildingLegStop, HourBuildingLegBase
|
||||
@@ -12,6 +13,19 @@ from ..models.hourbuildings import HourBuilding, HourBuildingLegFlight, HourBuil
|
||||
from datetime import date, datetime
|
||||
from typing import List
|
||||
|
||||
# Enable cell merging for equal mission
|
||||
MERGE: bool = False
|
||||
|
||||
PALETTE : List[str] = [
|
||||
"#E6F2FF", # azzurro chiarissimo
|
||||
"#E5FBF8", # verde acqua molto chiaro
|
||||
"#ECFBE1", # verde chiarissimo
|
||||
"#FFFBD1", # giallo molto chiaro
|
||||
"#FFF1D6", # giallo-arancio molto chiaro
|
||||
"#FFE3DD", # rosa pesca molto chiaro
|
||||
"#F3E6FA", # lilla chiarissimo
|
||||
]
|
||||
|
||||
def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) -> HttpResponse:
|
||||
|
||||
if not queryset.first():
|
||||
@@ -84,68 +98,72 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
|
||||
student_email: str = q.student.email
|
||||
student_course_type: str
|
||||
student_course_number: str
|
||||
student_course_ac: str
|
||||
student_course_ac: str = f"({'/'.join(t.type for t in q.student.aircrafts.distinct("type").all())})"
|
||||
if q.student.course:
|
||||
student_course_type = q.student.course.ctype
|
||||
student_course_number = str(q.student.course.cnumber)
|
||||
student_course_ac = " / ".join(t.type for t in q.student.aircrafts.distinct("type").all())
|
||||
student_data = [
|
||||
f"{q.student.surname} {q.student.name}\n{student_course_ac}",
|
||||
"\n".join([f"{q.student.surname} {q.student.name}", student_course_ac]),
|
||||
f"{student_course_type}-{student_course_number}"
|
||||
]
|
||||
else:
|
||||
student_data = [f"{q.student.surname} {q.student.name}", f"No Course Assigned"]
|
||||
|
||||
# Fill Training mission rows
|
||||
mission_name: str
|
||||
mission_name: List[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_notes = t.notes.strip().capitalize() if t.notes else "---"
|
||||
mission_name = [f"{t.mission.mtype}-{t.mission.mnum}"]
|
||||
if q.student.course and q.student.course.ctype == CourseTypes.PPL: # add course aircraft only for PPL students
|
||||
mission_name.append(student_course_ac)
|
||||
mission_name_joined: str = "\n".join(mission_name)
|
||||
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_name_joined if t.monday else "",
|
||||
mission_name_joined if t.tuesday else "",
|
||||
mission_name_joined if t.wednesday else "",
|
||||
mission_name_joined if t.thursday else "",
|
||||
mission_name_joined if t.friday else "",
|
||||
mission_name_joined if t.saturday else "",
|
||||
mission_name_joined if t.sunday else ""
|
||||
]
|
||||
mission_notes = t.notes.strip() if t.notes else "--"
|
||||
mission_data.append([str(q.week), *student_data, *mission_days, mission_notes, student_phone, student_email, ])
|
||||
|
||||
# Fill HourBuilding rows
|
||||
hb_name: str
|
||||
hb_name: List[str]
|
||||
hb_days: List[str]
|
||||
hb_data: List[List[str]] = []
|
||||
for h in HourBuilding.objects.filter(weekpref = q.id):
|
||||
hb_name = f"HB - {h.aircraft}\nVedi Note ->"
|
||||
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: List[str] = [f"{h.notes.strip()}", "---"] if h.notes else []
|
||||
hb_name = ["HB", f"({h.aircraft})"]
|
||||
hb_legs_all = HourBuildingLegBase.objects.filter(hb_id = h.id)
|
||||
for hh in hb_legs_all:
|
||||
time_str: str = ':'.join(str(hh.time).split(':')[:2]) # keep only hours and minutes
|
||||
if isinstance(hh, HourBuildingLegFlight):
|
||||
hb_notes.append(f"{hh.departure} -> {hh.destination} [{time_str}]{f' / PAX: {hh.pax.capitalize()}' if hh.pax else ''}")
|
||||
hb_pax: str | None = " ".join(x.capitalize() for x in hh.pax.split()) if hh.pax else None
|
||||
hb_name.append(f"{hh.departure} -> {hh.destination} [{time_str}]{f' / Pax: {hb_pax}' if hb_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])
|
||||
hb_name.append(f"STOP [{time_str}] {"Refuel" if hh.refuel else ""}" )
|
||||
hb_name_joined: str = "\n".join(hb_name)
|
||||
hb_days = [
|
||||
hb_name_joined if h.monday else "",
|
||||
hb_name_joined if h.tuesday else "",
|
||||
hb_name_joined if h.wednesday else "",
|
||||
hb_name_joined if h.thursday else "",
|
||||
hb_name_joined if h.friday else "",
|
||||
hb_name_joined if h.saturday else "",
|
||||
hb_name_joined if h.sunday else ""
|
||||
]
|
||||
hb_notes: str = h.notes.strip().capitalize() if h.notes else ""
|
||||
hb_data.append([str(q.week), *student_data, *hb_days, hb_notes, str(q.student.phone), q.student.email])
|
||||
|
||||
# Build rows for table
|
||||
all_data: List[List[str]] = mission_data + hb_data
|
||||
student_start: int = row + row_offset
|
||||
for row_content in all_data:
|
||||
for ri, row_content in enumerate(all_data):
|
||||
for c, cell_content in enumerate(row_content, start=1):
|
||||
cell = ws.cell(row = row + row_offset, column = c, value = cell_content)
|
||||
cell.alignment = center
|
||||
@@ -160,25 +178,25 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
|
||||
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 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_col_start: int = 1
|
||||
for c, cell_content in enumerate(row_content, start=1):
|
||||
# Merge cells in the row
|
||||
if cell_content == prev_cell_val and not merge_start:
|
||||
merge_start = True
|
||||
merge_col_start = c-1 # start merge from previous column
|
||||
elif cell_content != prev_cell_val and merge_start:
|
||||
merge_start = False
|
||||
ws.merge_cells(start_row=row+row_offset,
|
||||
end_row=row+row_offset,
|
||||
start_column=max(merge_col_start,1),
|
||||
end_column=max(c-1,1)) # end merge to previous column
|
||||
prev_cell_val = cell_content
|
||||
cell.fill = PatternFill('solid', fgColor=PALETTE[ri % len(PALETTE)].lstrip("#").lower())
|
||||
if MERGE:
|
||||
prev_cell_val: str = row_content[0]
|
||||
merge_start: bool = False
|
||||
merge_col_start: int = 1
|
||||
for c, cell_content in enumerate(row_content, start=1):
|
||||
# Merge cells in the row
|
||||
if cell_content == prev_cell_val and not merge_start:
|
||||
merge_start = True
|
||||
merge_col_start = c-1 # start merge from previous column
|
||||
elif cell_content != prev_cell_val and merge_start:
|
||||
merge_start = False
|
||||
ws.merge_cells(start_row=row+row_offset,
|
||||
end_row=row+row_offset,
|
||||
start_column=max(merge_col_start,1),
|
||||
end_column=max(c-1,1)) # end merge to previous column
|
||||
prev_cell_val = cell_content
|
||||
|
||||
# Incement row counter
|
||||
row_offset += 1
|
||||
@@ -208,6 +226,7 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
|
||||
|
||||
# Keep the largest column
|
||||
max_len: List[int] = []
|
||||
col_letter: str = "A"
|
||||
for column_cells in ws.columns:
|
||||
for cell in column_cells:
|
||||
cell_lines = str(cell.value).splitlines()
|
||||
@@ -215,11 +234,9 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
|
||||
continue
|
||||
max_len.append(max([len(ll) for ll in cell_lines]))
|
||||
length: int = max(max_len)
|
||||
col_letter: str = "A"
|
||||
if column_cells[0].column:
|
||||
col_letter = get_column_letter(column_cells[0].column)
|
||||
ws.column_dimensions[col_letter].width = length + 2
|
||||
|
||||
### End of Student Loop ###
|
||||
|
||||
# Save document in HttpResponse
|
||||
|
||||
6
note.txt
Normal file
6
note.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
OK leg delle hb all'interno del riquadro per i giorni
|
||||
OK aereo assegnato allo studente di fianco al numero della missione per PPL, invece per CPL e IR e HB il tipo va di fiaco al nome dello studente
|
||||
OK le missioni ripetute su piu' giorni hanno una cella per giorno (non unite)
|
||||
lo studente vede solo le missioni della sua fase PPL->PPL ATPL-> tutto
|
||||
OK ogni richiesta ha un colore diverso che cicla con delle tinte pastello
|
||||
|
||||
Reference in New Issue
Block a user