diff options
| author | matin <matin.kaufmann@gmail.com> | 2025-09-30 16:55:49 +0200 |
|---|---|---|
| committer | matin <matin.kaufmann@gmail.com> | 2025-09-30 16:55:49 +0200 |
| commit | ef3464569123d8f91a7300490a818d0ca299e398 (patch) | |
| tree | 0b8403f177c552f20b238aa9dc38c08f3fdb250e /prediction_report_service.py | |
| parent | 38129948c609e12202b8876ab2b87cde941f82bb (diff) | |
Berichfunktion hinzugefügt
Diffstat (limited to 'prediction_report_service.py')
| -rw-r--r-- | prediction_report_service.py | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/prediction_report_service.py b/prediction_report_service.py new file mode 100644 index 0000000..b7e3620 --- /dev/null +++ b/prediction_report_service.py @@ -0,0 +1,158 @@ +from datetime import datetime +from typing import List + +from PyQt5.QtGui import QTextDocument +from PyQt5.QtPrintSupport import QPrinter + +from calendar_manager import CalendarManager +from prediction_controller import PredictionController +from date_service import DateService + + +class PredictionReportService: + """Service to export the current prediction and entries into a PDF report.""" + + @staticmethod + def export_pdf(calendar_manager: CalendarManager, + prediction_controller: PredictionController, + out_path: str, + dateformat: str = "dd.MM.yyyy") -> bool: + try: + # Gather data + launch_dt = prediction_controller.get_launch_date() + duration = prediction_controller.get_duration() + prediction_dt = prediction_controller.get_prediction() + entries = calendar_manager.list_entries() + + # Format helpers + def fmt_date(dt): + return DateService.format_date_for_display(dt, dateformat) if dt else "" + + launch_str = fmt_date(launch_dt) + duration_str = f"{duration.years} Jahre" if duration and hasattr(duration, 'years') else "" + prediction_str = fmt_date(prediction_dt) + + # Sum time_periods (months and days separately) + total_months = 0 + total_days = 0 + for e in entries: + tp = getattr(e, 'time_period', None) + if not tp: + continue + try: + parts = tp.split() + if len(parts) >= 2: + value = int(parts[0]) + unit = parts[1].lower() + if value > 0: + if 'monat' in unit: + total_months += value + elif 'tag' in unit: + total_days += value + except Exception: + # Ignore unparsable time_periods + pass + + sum_parts = [] + if total_months > 0: + sum_parts.append(f"{total_months} Monate") + if total_days > 0: + sum_parts.append(f"{total_days} Tage") + sum_str = ", ".join(sum_parts) if sum_parts else "0" + + # Build HTML + created_stamp = datetime.now().strftime('%d.%m.%Y %H:%M:%S') + html = [] + html.append("<html><head><meta charset='utf-8'>") + html.append(""" + <style> + body { font-family: Arial, sans-serif; color: #222; } + h1 { font-size: 20px; margin-bottom: 6px; } + .meta { margin-bottom: 12px; } + .meta div { margin: 2px 0; } + table { border-collapse: collapse; width: 100%; } + th, td { border: 1px solid #e1e4ee; padding: 4px 6px; vertical-align: top; font-size: 150px; line-height: 1.3; } + th { background: #f5f7fb; text-align: left; } + tfoot td { font-weight: bold; } + .stamp { margin-top: 16px; font-size: 10px; color: #666; } + </style> + """) + html.append("</head><body>") + html.append("<h1>Ausfallzeitenrechner — Bericht</h1>") + html.append("<div class='meta'>") + html.append(f"<div><strong>Promotionsdatum:</strong> {launch_str}</div>") + html.append(f"<div><strong>Bewerbungszeitraum:</strong> {duration_str}</div>") + html.append(f"<div><strong>Bewerbungsfrist:</strong> {prediction_str}</div>") + html.append("</div>") + + # Table header + html.append("<table>") + html.append( + "<thead><tr>" + "<th>Beginn</th>" + "<th>Ende</th>" + "<th>Art</th>" + "<th>Kommentar</th>" + "<th>Beginn Anrechnung</th>" + "<th>Ende Anrechnung</th>" + "<th>Anrechnungszeitraum</th>" + "</tr></thead><tbody>" + ) + + # Rows + for e in entries: + start_text = fmt_date(e.start_date) + end_text = fmt_date(e.end_date) + keyword_text = e.keyword + commentary_text = getattr(e, 'commentary', '') or '' + c_start_text = fmt_date(getattr(e, 'corrected_start_date', None)) + c_end_text = fmt_date(getattr(e, 'corrected_end_date', None)) + time_text = getattr(e, 'time_period', '') or '' + + # Escape + def esc(s: str) -> str: + return (s.replace('&', '&') + .replace('<', '<') + .replace('>', '>')) + + html.append( + f"<tr><td>{esc(start_text)}</td>" + f"<td>{esc(end_text)}</td>" + f"<td>{esc(keyword_text)}</td>" + f"<td>{esc(commentary_text)}</td>" + f"<td>{esc(c_start_text)}</td>" + f"<td>{esc(c_end_text)}</td>" + f"<td>{esc(time_text)}</td></tr>" + ) + + html.append("</tbody>") + html.append(f"<tfoot><tr><td colspan='7'>Summe Anrechnungszeitraum: {sum_str}</td></tr></tfoot>") + html.append("</table>") + + html.append(f"<div class='stamp'>Erstellt am: {created_stamp}</div>") + html.append("</body></html>") + + # Ensure extension + if not out_path.lower().endswith('.pdf'): + out_path = out_path + '.pdf' + + # Render HTML to PDF + printer = QPrinter(QPrinter.HighResolution) + printer.setOutputFormat(QPrinter.PdfFormat) + printer.setOutputFileName(out_path) + # Try to set margins (not all bindings support Millimeter overload) + try: + printer.setPageMargins(12, 12, 12, 12, QPrinter.Millimeter) + except Exception: + pass + + doc = QTextDocument() + doc.setHtml("".join(html)) + doc.print_(printer) + + return True + except Exception as e: + print(f"Error exporting PDF: {e}") + return False + + |
