App web de suivi des heures de travail (FastAPI + HTMX + JSON plats). - Saisie inline par semaine avec bouton ✓ par ligne - Congés MA/AM/J avec coloration cellule et mise à jour live via OOB HTMX - Import fichier ODS badgeuse - Solde semaine et solde global recalculés à la volée - Design "ledger" écru, DM Mono, zéro border-radius - Déploiement Docker sur port 8000 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
70 lines
2.0 KiB
Python
70 lines
2.0 KiB
Python
"""Parse badge-reader ODS files and return structured data."""
|
|
from datetime import datetime, time
|
|
from odf.opendocument import load
|
|
from odf.table import Table, TableRow, TableCell
|
|
from odf.text import P
|
|
|
|
|
|
def _cell_text(cell) -> str:
|
|
parts = []
|
|
for p in cell.getElementsByType(P):
|
|
for node in p.childNodes:
|
|
if hasattr(node, "data"):
|
|
parts.append(node.data)
|
|
return "".join(parts).strip()
|
|
|
|
|
|
def _parse_time(val: str) -> str | None:
|
|
"""Return HH:MM or None. Ignores zero-times (formula placeholders)."""
|
|
val = val.strip()
|
|
if not val:
|
|
return None
|
|
for fmt in ("%H:%M:%S", "%H:%M"):
|
|
try:
|
|
t = datetime.strptime(val, fmt)
|
|
hhmm = t.strftime("%H:%M")
|
|
if hhmm == "00:00":
|
|
return None
|
|
return hhmm
|
|
except ValueError:
|
|
pass
|
|
return None
|
|
|
|
|
|
def _parse_date(val: str) -> str | None:
|
|
"""Return YYYY-MM-DD or None."""
|
|
val = val.strip()
|
|
for fmt in ("%d/%m/%Y", "%Y-%m-%d"):
|
|
try:
|
|
return datetime.strptime(val, fmt).strftime("%Y-%m-%d")
|
|
except ValueError:
|
|
pass
|
|
return None
|
|
|
|
|
|
def parse_ods(path: str) -> list[dict]:
|
|
"""Return list of pointage dicts from all sheets in the ODS file."""
|
|
doc = load(path)
|
|
sheets = doc.spreadsheet.getElementsByType(Table)
|
|
records = []
|
|
|
|
for sheet in sheets:
|
|
rows = sheet.getElementsByType(TableRow)
|
|
for row in rows:
|
|
cells = row.getElementsByType(TableCell)
|
|
if len(cells) < 5:
|
|
continue
|
|
date_val = _cell_text(cells[0])
|
|
date = _parse_date(date_val)
|
|
if not date:
|
|
continue
|
|
records.append({
|
|
"date": date,
|
|
"matin_entree": _parse_time(_cell_text(cells[1])),
|
|
"matin_sortie": _parse_time(_cell_text(cells[2])),
|
|
"aprem_entree": _parse_time(_cell_text(cells[3])),
|
|
"aprem_sortie": _parse_time(_cell_text(cells[4])),
|
|
})
|
|
|
|
return records
|