Files
Suisa-Listen/Multi-Musikanteil.py
2026-02-05 15:26:50 +01:00

170 lines
5.3 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MehrmonatsAuswertung ACRCloud → PDFReport + Druck
———————————————————————————————————————————————
• TimestampSpalte : "Timestamp(UTC+02:00)"
• DauerSpalte : "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 QuerformatPDFReport mit Tabelle."""
c = canvas.Canvas(str(ausgabe), pagesize=landscape(A4))
w, h = landscape(A4)
# Überschrift
title = "Musikanteil pro Monat (ACRCloudAuswertung)"
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 WindowsStandarddrucker (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 ACRCloudExcelDateien wählen",
filetypes=[("ExcelDateien", "*.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]))
# PDFDatei speichern
save_path = filedialog.asksaveasfilename(
title="PDFReport speichern unter …",
defaultextension=".pdf",
filetypes=[("PDFDatei", "*.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()