#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Mehrmonats Auswertung ACRCloud PDF Report und Druck ——————————————————————————————————————————————— • Timestamp Spalten : "Timestamp(UTC+0x:00)" • Dauer Spalte : "Played Duration" (Sekunden) """ import calendar import os import subprocess import sys import tempfile from datetime import datetime from pathlib import Path from typing import List, Tuple import pandas as pd from reportlab.lib.pagesizes import A4, landscape from reportlab.lib.units import mm from reportlab.pdfgen import canvas import tkinter as tk from tkinter import filedialog, messagebox possible_cols = [ "Timestamp(UTC+01:00)", "Timestamp(UTC+02:00)", ] TS_COL = next(col for col in possible_cols if col in df.columns) DUR_COL = "Played Duration" def musikanteil(path: Path) -> Tuple[int, int, float]: """liefert (Jahr, Monat, Prozent) für genau eine Datei""" df = pd.read_excel(path) if TS_COL not in df.columns or DUR_COL not in df.columns: raise ValueError(f"{path.name}: Erforderliche Spalten nicht gefunden.") df[TS_COL] = pd.to_datetime(df[TS_COL], errors="raise") erstes = df[TS_COL].min() jahr, monat = erstes.year, erstes.month gesamt = df[DUR_COL].sum() sek_monat = calendar.monthrange(jahr, monat)[1] * 86_400 proz = gesamt / sek_monat * 100 return jahr, monat, proz def erstelle_pdf(daten: List[Tuple[int, int, float]], ausgabe: Path) -> None: """Erzeugt einen Querformat PDF Report mit Tabelle.""" c = canvas.Canvas(str(ausgabe), pagesize=landscape(A4)) w, h = landscape(A4) # Überschrift title = "Musikanteil pro Monat (ACRCloud Auswertung)" c.setFont("Helvetica-Bold", 16) c.drawCentredString(w / 2, h - 25 * mm, title) # Datum c.setFont("Helvetica", 9) c.drawString(15 * mm, h - 32 * mm, f"Erstellt am: {datetime.now():%d.%m.%Y %H:%M}") # Tabellenkopf ypos = h - 50 * mm col_widths = [50 * mm, 50 * mm, 50 * mm] headers = ["Monat/Jahr", "Musikanteil [%]", "Datei"] c.setFont("Helvetica-Bold", 11) for i, head in enumerate(headers): c.drawString(15 * mm + sum(col_widths[:i]), ypos, head) # Tabellenzeilen c.setFont("Helvetica", 11) ypos -= 8 * mm for jahr, monat, proz, dateiname in daten: c.drawString(15 * mm, ypos, f"{monat:02d}/{jahr}") c.drawRightString(15 * mm + col_widths[0] + col_widths[1] - 5 * mm, ypos, f"{proz:.2f}") c.drawString(15 * mm + col_widths[0] + col_widths[1], ypos, dateiname) ypos -= 7 * mm if ypos < 20 * mm: # neue Seite c.showPage() ypos = h - 25 * mm c.save() def drucke_pdf(pfad: Path) -> None: """Sendet das PDF an den Windows Standarddrucker (Acrobat bzw. Edge).""" try: # os.startfile mit "print" funktioniert auf Windows os.startfile(pfad, "print") except Exception as e: messagebox.showerror("Druckfehler", f"Drucken fehlgeschlagen:\n{e}") def main() -> None: root = tk.Tk() root.withdraw() dateien = filedialog.askopenfilenames( title="Mehrere ACRCloud Excel Dateien wählen", filetypes=[("Excel Dateien", "*.xlsx;*.xls")], ) if not dateien: return ergebnisse = [] fehler = [] for pfad in dateien: try: jahr, monat, proz = musikanteil(Path(pfad)) ergebnisse.append((jahr, monat, proz, Path(pfad).name)) except Exception as exc: fehler.append(f"{Path(pfad).name}: {exc}") if not ergebnisse: messagebox.showerror("Fehler", "\n".join(fehler) or "Keine gültigen Dateien.") return # chronologisch sortieren ergebnisse.sort(key=lambda x: (x[0], x[1])) # PDF Datei speichern save_path = filedialog.asksaveasfilename( title="PDF Report speichern unter …", defaultextension=".pdf", filetypes=[("PDF Datei", "*.pdf")], initialfile="Musikanteil_Report.pdf", ) if not save_path: return erstelle_pdf(ergebnisse, Path(save_path)) # Zusammenfassung anzeigen Druckoption text_lines = [f"{m:02d}/{j}: {p:.2f} %" for j, m, p, _ in ergebnisse] summary = "Erfolgreich erstellt:\n" + "\n".join(text_lines) def dialog(): win = tk.Toplevel() win.title("Auswertung fertig") win.geometry("320x240") tk.Label(win, text=summary, justify="left", pady=10).pack() frame = tk.Frame(win) frame.pack(pady=10) tk.Button(frame, text="PDF öffnen", command=lambda: [os.startfile(save_path), win.destroy()]).pack(side="left", padx=5) tk.Button(frame, text="Drucken", command=lambda: [drucke_pdf(Path(save_path)), win.destroy()]).pack(side="left", padx=5) tk.Button(frame, text="Schließen", command=win.destroy).pack(side="right", padx=5) win.mainloop() dialog() if fehler: messagebox.showwarning("Einige Dateien übersprungen", "Folgende Dateien konnten nicht verarbeitet werden:\n" + "\n".join(fehler)) if __name__ == "__main__": if sys.platform != "win32": print("Dieses Tool ist für Windows gedacht.") main()