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.styles import Font, PatternFill, Alignment, Border, Side
|
||||||
from openpyxl.utils import get_column_letter
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
from ..models.courses import CourseTypes
|
||||||
from ..models.missions import Training
|
from ..models.missions import Training
|
||||||
from ..models.weekpref import WeekPreference
|
from ..models.weekpref import WeekPreference
|
||||||
from ..models.hourbuildings import HourBuilding, HourBuildingLegFlight, HourBuildingLegStop, HourBuildingLegBase
|
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 datetime import date, datetime
|
||||||
from typing import List
|
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:
|
def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) -> HttpResponse:
|
||||||
|
|
||||||
if not queryset.first():
|
if not queryset.first():
|
||||||
@@ -84,68 +98,72 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
|
|||||||
student_email: str = q.student.email
|
student_email: str = q.student.email
|
||||||
student_course_type: str
|
student_course_type: str
|
||||||
student_course_number: 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:
|
if q.student.course:
|
||||||
student_course_type = q.student.course.ctype
|
student_course_type = q.student.course.ctype
|
||||||
student_course_number = str(q.student.course.cnumber)
|
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 = [
|
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}"
|
f"{student_course_type}-{student_course_number}"
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
student_data = [f"{q.student.surname} {q.student.name}", f"No Course Assigned"]
|
student_data = [f"{q.student.surname} {q.student.name}", f"No Course Assigned"]
|
||||||
|
|
||||||
# Fill Training mission rows
|
# Fill Training mission rows
|
||||||
mission_name: str
|
mission_name: List[str]
|
||||||
mission_days: List[str]
|
mission_days: List[str]
|
||||||
mission_notes: str
|
mission_notes: str
|
||||||
mission_data: List[List[str]] = []
|
mission_data: List[List[str]] = []
|
||||||
for t in Training.objects.filter(weekpref = q.id):
|
for t in Training.objects.filter(weekpref = q.id):
|
||||||
if not t.mission:
|
if not t.mission:
|
||||||
raise Exception("No Training Mission Assigned")
|
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_days = [
|
||||||
mission_name if t.monday else "",
|
mission_name_joined if t.monday else "",
|
||||||
mission_name if t.tuesday else "",
|
mission_name_joined if t.tuesday else "",
|
||||||
mission_name if t.wednesday else "",
|
mission_name_joined if t.wednesday else "",
|
||||||
mission_name if t.thursday else "",
|
mission_name_joined if t.thursday else "",
|
||||||
mission_name if t.friday else "",
|
mission_name_joined if t.friday else "",
|
||||||
mission_name if t.saturday else "",
|
mission_name_joined if t.saturday else "",
|
||||||
mission_name if t.sunday 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, ])
|
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: List[str]
|
||||||
hb_days: List[str]
|
hb_days: List[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 = ["HB", f"({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: List[str] = [f"{h.notes.strip()}", "---"] if h.notes else []
|
|
||||||
hb_legs_all = HourBuildingLegBase.objects.filter(hb_id = h.id)
|
hb_legs_all = HourBuildingLegBase.objects.filter(hb_id = h.id)
|
||||||
for hh in hb_legs_all:
|
for hh in hb_legs_all:
|
||||||
time_str: str = ':'.join(str(hh.time).split(':')[:2]) # keep only hours and minutes
|
time_str: str = ':'.join(str(hh.time).split(':')[:2]) # keep only hours and minutes
|
||||||
if isinstance(hh, HourBuildingLegFlight):
|
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):
|
elif isinstance(hh, HourBuildingLegStop):
|
||||||
hb_notes.append(f"STOP [{time_str}] {"Refuel" if hh.refuel else ""}" )
|
hb_name.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_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
|
# 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 row_content in all_data:
|
for ri, row_content in enumerate(all_data):
|
||||||
for c, cell_content in enumerate(row_content, start=1):
|
for c, cell_content in enumerate(row_content, start=1):
|
||||||
cell = ws.cell(row = row + row_offset, column = c, value = cell_content)
|
cell = ws.cell(row = row + row_offset, column = c, value = cell_content)
|
||||||
cell.alignment = center
|
cell.alignment = center
|
||||||
@@ -160,25 +178,25 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
|
|||||||
elif c > course_index and c <= note_index:
|
elif c > course_index and c <= note_index:
|
||||||
cell.border = border_bottom_thin + border_right_thin
|
cell.border = border_bottom_thin + border_right_thin
|
||||||
# Fill mix cells if the cell is not empty
|
# 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):
|
if len(cell_content):
|
||||||
cell.fill = PatternFill('solid', fgColor="f0f0f0")
|
cell.fill = PatternFill('solid', fgColor=PALETTE[ri % len(PALETTE)].lstrip("#").lower())
|
||||||
|
if MERGE:
|
||||||
prev_cell_val: str = row_content[0]
|
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, cell_content in enumerate(row_content, start=1):
|
for c, cell_content in enumerate(row_content, start=1):
|
||||||
# Merge cells in the row
|
# Merge cells in the row
|
||||||
if cell_content == 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 cell_content != 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 = cell_content
|
prev_cell_val = cell_content
|
||||||
|
|
||||||
# Incement row counter
|
# Incement row counter
|
||||||
row_offset += 1
|
row_offset += 1
|
||||||
@@ -208,6 +226,7 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
|
|||||||
|
|
||||||
# Keep the largest column
|
# Keep the largest column
|
||||||
max_len: List[int] = []
|
max_len: List[int] = []
|
||||||
|
col_letter: str = "A"
|
||||||
for column_cells in ws.columns:
|
for column_cells in ws.columns:
|
||||||
for cell in column_cells:
|
for cell in column_cells:
|
||||||
cell_lines = str(cell.value).splitlines()
|
cell_lines = str(cell.value).splitlines()
|
||||||
@@ -215,11 +234,9 @@ def export_selected(request: HttpRequest, queryset: QuerySet[WeekPreference]) ->
|
|||||||
continue
|
continue
|
||||||
max_len.append(max([len(ll) for ll in cell_lines]))
|
max_len.append(max([len(ll) for ll in cell_lines]))
|
||||||
length: int = max(max_len)
|
length: int = max(max_len)
|
||||||
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)
|
||||||
ws.column_dimensions[col_letter].width = length + 2
|
ws.column_dimensions[col_letter].width = length + 2
|
||||||
|
|
||||||
### End of Student Loop ###
|
### End of Student Loop ###
|
||||||
|
|
||||||
# Save document in HttpResponse
|
# 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