commit aedb654ef41014e4f2df86b9ade0335686294c33 Author: Charlie Root Date: Thu Nov 6 14:22:51 2025 +0100 scenar diff --git a/scenar.py b/scenar.py new file mode 100755 index 0000000..1f82c1c --- /dev/null +++ b/scenar.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 + +import cgi +import cgitb +import pandas as pd +from openpyxl import Workbook +from openpyxl.styles import Alignment, Border, Font, PatternFill, Side +from openpyxl.utils import get_column_letter +from datetime import datetime +from io import StringIO +import os +import shutil + +cgitb.enable() + +print("Content-Type: text/html") +print() + +form = cgi.FieldStorage() + +title = form.getvalue('title') +detail = form.getvalue('detail') +csv_content = form.getvalue('csv_content') +program_descriptions = { + "C": form.getvalue('desc_C', "Program naplnujici cile"), + "S": form.getvalue('desc_S', "Program pro podporu skupinove dynamiky"), + "K": form.getvalue('desc_K', "Kreativni program"), + "P": form.getvalue('desc_P', "Provozni blok"), + "O": form.getvalue('desc_O', "Vyplnove a ostatni programy"), +} +program_colors = { + "C": 'FF' + form.getvalue('color_C', "ADD8E6").lstrip('#'), + "S": 'FF' + form.getvalue('color_S', "90EE90").lstrip('#'), + "K": 'FF' + form.getvalue('color_K', "FFFF00").lstrip('#'), + "P": 'FF' + form.getvalue('color_P', "D3D3D3").lstrip('#'), + "O": 'FF' + form.getvalue('color_O', "FFB6C1").lstrip('#'), +} + +def read_csv_from_string(csv_string): + return pd.read_csv(StringIO(csv_string), sep=";", header=None, names=["Datum", "Zacatek", "Konec", "Program", "Typ", "Garant", "Poznamka"]) + +def calculate_row_height(cell_value, column_width): + if not cell_value: + return 15 + + max_line_length = column_width * 1.2 + lines = cell_value.split('\n') + line_count = 0 + for line in lines: + line_count += len(line) // max_line_length + 1 + + return line_count * 15 + +def create_timetable(data, title, detail, program_descriptions, program_colors): + wb = Workbook() + ws = wb.active + + thick_border = Border(left=Side(style='thick', color='000000'), + right=Side(style='thick', color='000000'), + top=Side(style='thick', color='000000'), + bottom=Side(style='thick', color='000000')) + + ws['A1'] = title + ws['A1'].alignment = Alignment(horizontal="center", vertical="center") + ws['A1'].font = Font(size=24, bold=True) + ws['A1'].border = thick_border + + ws['A2'] = detail + ws['A2'].alignment = Alignment(horizontal="center", vertical="center") + ws['A2'].font = Font(size=16, italic=True) + ws['A2'].border = thick_border + + # Nastavení výšky řádků pro nadpis a podnadpis + title_row_height = calculate_row_height(title, ws.column_dimensions[get_column_letter(1)].width) + detail_row_height = calculate_row_height(detail, ws.column_dimensions[get_column_letter(1)].width) + ws.row_dimensions[1].height = title_row_height + ws.row_dimensions[2].height = detail_row_height + + data["Datum"] = pd.to_datetime(data["Datum"], format="%d.%m.%Y") + data = data.sort_values(by=["Datum", "Zacatek"]) + + start_times = data["Zacatek"].apply(lambda x: datetime.strptime(str(x), '%H%M')) + end_times = data["Konec"].apply(lambda x: datetime.strptime(str(x), '%H%M')) + + min_time = start_times.min() + max_time = end_times.max() + + time_slots = pd.date_range(min_time, max_time, freq='15min').time + + total_columns = len(time_slots) + 1 + ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=total_columns) + ws.merge_cells(start_row=2, start_column=1, end_row=2, end_column=total_columns) + + row_offset = 3 + col_offset = 1 + cell = ws.cell(row=row_offset, column=col_offset, value="Datum") + cell.fill = PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid") + cell.alignment = Alignment(horizontal="center", vertical="center") + cell.font = Font(bold=True) + cell.border = thick_border + + for i, time_slot in enumerate(time_slots, start=col_offset + 1): + cell = ws.cell(row=row_offset, column=i, value=time_slot.strftime("%H:%M")) + cell.fill = PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid") + cell.alignment = Alignment(horizontal="center", vertical="center") + cell.font = Font(bold=True) + cell.border = thick_border + + current_row = row_offset + 1 + grouped_data = data.groupby(data['Datum']) + + for date, group in grouped_data: + day_name = date.strftime("%A") + date_str = date.strftime(f"%d.%m {day_name}") + + cell = ws.cell(row=current_row, column=col_offset, value=date_str) + cell.alignment = Alignment(horizontal="center", vertical="center") + cell.fill = PatternFill(start_color="D3D3D3", end_color="D3D3D3", fill_type="solid") + cell.font = Font(bold=True, size=14) + cell.border = thick_border + + for _, row in group.iterrows(): + start_time = datetime.strptime(str(row["Zacatek"]), '%H%M').time() + end_time = datetime.strptime(str(row["Konec"]), '%H%M').time() + start_index = list(time_slots).index(start_time) + col_offset + 1 + end_index = list(time_slots).index(end_time) + col_offset + 1 + + cell_value = f"{row['Program']}" + if pd.notna(row['Garant']): + cell_value += f"\n{row['Garant']}" + if pd.notna(row['Poznamka']): + cell_value += f"\n\n{row['Poznamka']}" + + ws.merge_cells(start_row=current_row, start_column=start_index, end_row=current_row, end_column=end_index - 1) + cell = ws.cell(row=current_row, column=start_index) + cell.value = cell_value + cell.alignment = Alignment(wrap_text=True, horizontal="center", vertical="center") + + lines = cell_value.split("\n") + for idx, line in enumerate(lines): + if idx == 0: + cell.font = Font(bold=True) + elif idx == 1: + cell.font = Font(bold=False) + elif idx > 1 and pd.notna(row['Poznamka']): + cell.font = Font(italic=True) + + cell.fill = PatternFill(start_color=program_colors[row["Typ"]], end_color=program_colors[row["Typ"]], fill_type="solid") + cell.border = thick_border + + current_row += 1 + + legend_row = current_row + 2 + legend_max_length = 0 + ws.cell(row=legend_row, column=1, value="Legenda:").font = Font(bold=True) + legend_row += 1 + for typ, desc in program_descriptions.items(): + legend_text = f"{desc} ({typ})" + legend_cell = ws.cell(row=legend_row, column=1, value=legend_text) + legend_cell.fill = PatternFill(start_color=program_colors[typ], fill_type="solid") + legend_max_length = max(legend_max_length, calculate_column_width(legend_text)) + legend_row += 1 + + ws.column_dimensions[get_column_letter(col_offset)].width = legend_max_length + + for col in range(2, total_columns + 1): + ws.column_dimensions[get_column_letter(col)].width = 15 + + for row in ws.iter_rows(min_row=1, max_row=current_row - 1, min_col=1, max_col=total_columns): + for cell in row: + cell.alignment = Alignment(wrap_text=True, horizontal="center", vertical="center") + cell.border = thick_border + + for row in ws.iter_rows(min_row=1, max_row=current_row - 1): + max_height = 0 + for cell in row: + if cell.value: + height = calculate_row_height(cell.value, ws.column_dimensions[get_column_letter(cell.column)].width) + if height > max_height: + max_height = height + ws.row_dimensions[row[0].row].height = max_height + + return wb + +def calculate_column_width(text): + max_length = max(len(line) for line in text.split('\n')) + return max_length * 1.2 + +data = read_csv_from_string(csv_content) +wb = create_timetable(data, title, detail, program_descriptions, program_colors) +filename = f"{title}.xlsx" +file_path = f"/var/www/htdocs/{filename}" +wb.save(file_path) + +print(f'Stahnout scenar pro {title} ZDE') +