mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-07-01 11:14:52 +02:00
feat: add OpenDocument PDF templates
This commit is contained in:
+92
-14
@@ -1,10 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import tkinter as tk
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from tkinter import messagebox, ttk
|
||||
|
||||
from ccma.domain.contributions import CLAIM_STATUS_LABELS, claim_status, claim_total, money_text
|
||||
@@ -12,6 +11,8 @@ from ccma.domain.dates import age_label, date_input_hint, format_date_for_displa
|
||||
from ccma.domain.models import MEMBERSHIP_STATUS_LABELS as STATUS_LABELS
|
||||
from ccma.domain.models import Event
|
||||
from ccma.storage.repository import MemberRepository, RepositoryError
|
||||
from ccma.ui.document_dialog import DocumentTemplateDialog
|
||||
from ccma.ui.file_open import open_path
|
||||
from ccma.ui.labels import display_label, storage_key
|
||||
|
||||
|
||||
@@ -161,11 +162,41 @@ class MemberTab(ttk.Frame):
|
||||
|
||||
documents_tab.columnconfigure(0, weight=1)
|
||||
documents_tab.rowconfigure(1, weight=1)
|
||||
ttk.Button(documents_tab, text="Dateiordner öffnen", command=self._open_files).grid(
|
||||
row=0, column=0, sticky="w", pady=(0, 10)
|
||||
document_buttons = ttk.Frame(documents_tab)
|
||||
document_buttons.grid(row=0, column=0, sticky="ew", pady=(0, 10))
|
||||
ttk.Button(
|
||||
document_buttons,
|
||||
text="Dokument aus Template",
|
||||
style="Accent.TButton",
|
||||
command=self._create_document,
|
||||
).pack(side="left", padx=(0, 8))
|
||||
ttk.Button(document_buttons, text="Dateiordner öffnen", command=self._open_files).pack(
|
||||
side="left"
|
||||
)
|
||||
self.documents = tk.Listbox(documents_tab, borderwidth=0, highlightthickness=0)
|
||||
self.documents = ttk.Treeview(
|
||||
documents_tab,
|
||||
columns=("name", "type", "modified", "size"),
|
||||
show="headings",
|
||||
)
|
||||
for key, title, width in (
|
||||
("name", "Dokument", 280),
|
||||
("type", "Typ", 65),
|
||||
("modified", "Geändert", 135),
|
||||
("size", "Größe", 80),
|
||||
):
|
||||
self.documents.heading(key, text=title)
|
||||
self.documents.column(key, width=width, anchor="w")
|
||||
self.documents.grid(row=1, column=0, sticky="nsew")
|
||||
document_scroll = ttk.Scrollbar(
|
||||
documents_tab,
|
||||
orient="vertical",
|
||||
command=self.documents.yview,
|
||||
)
|
||||
document_scroll.grid(row=1, column=1, sticky="ns")
|
||||
self.documents.configure(yscrollcommand=document_scroll.set)
|
||||
self.documents.bind("<Double-1>", lambda _event: self._open_selected_document())
|
||||
self.documents.bind("<Return>", lambda _event: self._open_selected_document())
|
||||
self.document_paths: dict[str, Path] = {}
|
||||
|
||||
def _build_timeline(self, parent: ttk.Frame) -> None:
|
||||
parent.columnconfigure(0, weight=1)
|
||||
@@ -244,11 +275,31 @@ class MemberTab(ttk.Frame):
|
||||
self.on_open_claim(self.member_id, selected[0])
|
||||
|
||||
def _refresh_documents(self) -> None:
|
||||
self.documents.delete(0, "end")
|
||||
self.documents.delete(*self.documents.get_children())
|
||||
self.document_paths.clear()
|
||||
root = self.repository.members_root / self.member_id / "files"
|
||||
for path in sorted(root.rglob("*")):
|
||||
for index, path in enumerate(sorted(root.rglob("*"))):
|
||||
if path.is_file():
|
||||
self.documents.insert("end", str(path.relative_to(root)))
|
||||
item_id = f"document:{index}"
|
||||
self.document_paths[item_id] = path
|
||||
try:
|
||||
stat = path.stat()
|
||||
modified = datetime.fromtimestamp(stat.st_mtime).strftime("%d.%m.%Y %H:%M")
|
||||
size = _file_size(stat.st_size)
|
||||
except OSError:
|
||||
modified = "—"
|
||||
size = "—"
|
||||
self.documents.insert(
|
||||
"",
|
||||
"end",
|
||||
iid=item_id,
|
||||
values=(
|
||||
path.relative_to(root),
|
||||
path.suffix.removeprefix(".").upper() or "DATEI",
|
||||
modified,
|
||||
size,
|
||||
),
|
||||
)
|
||||
|
||||
def _save(self) -> None:
|
||||
for key, variable in self.variables.items():
|
||||
@@ -280,12 +331,31 @@ class MemberTab(ttk.Frame):
|
||||
|
||||
def _open_files(self) -> None:
|
||||
path = self.repository.members_root / self.member_id / "files"
|
||||
if sys.platform == "win32":
|
||||
subprocess.Popen(["explorer", str(path)])
|
||||
elif sys.platform == "darwin":
|
||||
subprocess.Popen(["open", str(path)])
|
||||
else:
|
||||
subprocess.Popen(["xdg-open", str(path)])
|
||||
self._open_path(path)
|
||||
|
||||
def _open_selected_document(self) -> None:
|
||||
selected = self.documents.selection()
|
||||
if selected and selected[0] in self.document_paths:
|
||||
self._open_path(self.document_paths[selected[0]])
|
||||
|
||||
def _open_path(self, path: Path) -> None:
|
||||
try:
|
||||
open_path(path)
|
||||
except OSError as exc:
|
||||
messagebox.showerror("Datei konnte nicht geöffnet werden", str(exc), parent=self)
|
||||
|
||||
def _create_document(self) -> None:
|
||||
DocumentTemplateDialog(
|
||||
self,
|
||||
self.repository,
|
||||
self.member_id,
|
||||
self._document_generated,
|
||||
)
|
||||
|
||||
def _document_generated(self, _path: Path) -> None:
|
||||
self._refresh_documents()
|
||||
self._refresh_events()
|
||||
self.on_changed()
|
||||
|
||||
|
||||
def _format_timestamp(event: Event) -> str:
|
||||
@@ -299,3 +369,11 @@ def _event_label(event: Event) -> str:
|
||||
if event.actor_type == "system":
|
||||
return f"[AUTO] {event.summary}"
|
||||
return event.summary
|
||||
|
||||
|
||||
def _file_size(size: int) -> str:
|
||||
if size < 1024:
|
||||
return f"{size} B"
|
||||
if size < 1024 * 1024:
|
||||
return f"{size / 1024:.1f} KiB"
|
||||
return f"{size / (1024 * 1024):.1f} MiB"
|
||||
|
||||
Reference in New Issue
Block a user