diff --git a/app/main.py b/app/main.py index f66f4d6..ff55c2f 100644 --- a/app/main.py +++ b/app/main.py @@ -1,19 +1,15 @@ from datetime import datetime, timedelta from itertools import groupby -from pathlib import Path -import shutil -import tempfile -from fastapi import FastAPI, Form, Request, UploadFile, File +from fastapi import FastAPI, Form, Request from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates from fastapi.staticfiles import StaticFiles from models import ( load_config, load_week_pointages, save_week_pointages, load_conges, - toggle_conge, upsert_pointages, list_all_weeks, + toggle_conge, list_all_weeks, ) -from ods_import import parse_ods from calcul import compute_week, minutes_to_hhmm, hhmm_to_minutes, heures_dues app = FastAPI(title="Décompte Horaire") @@ -280,22 +276,3 @@ def toggle_conge_route(date: str, type_conge: str, request: Request): return RedirectResponse(f"/semaine/{iso.year}/{iso.week}", status_code=303) -@app.get("/import", response_class=HTMLResponse) -def import_page(request: Request, msg: str = ""): - return templates.TemplateResponse("import.html", {"request": request, "msg": msg}) - - -@app.post("/import") -async def do_import(request: Request, fichier: UploadFile = File(...)): - with tempfile.NamedTemporaryFile(suffix=".ods", delete=False) as tmp: - shutil.copyfileobj(fichier.file, tmp) - tmp_path = tmp.name - try: - records = parse_ods(tmp_path) - except Exception as e: - return RedirectResponse(f"/import?msg=Erreur+parsing:+{e}", status_code=303) - finally: - Path(tmp_path).unlink(missing_ok=True) - - upsert_pointages(records) - return RedirectResponse(f"/import?msg={len(records)}+jours+importés", status_code=303) diff --git a/app/ods_import.py b/app/ods_import.py deleted file mode 100644 index fa89ea2..0000000 --- a/app/ods_import.py +++ /dev/null @@ -1,69 +0,0 @@ -"""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 diff --git a/app/templates/base.html b/app/templates/base.html index 9a61c48..a824765 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -368,34 +368,6 @@ .chip:hover { border-color: var(--text); color: var(--text); } .chip.here { background: var(--text); border-color: var(--text); color: var(--ground); } - /* ── Import ── */ - .import-wrap { padding: 2rem; } - .import-wrap h2 { font-size: 1rem; font-weight: 600; margin-bottom: .75rem; border-bottom: 1px solid var(--rule); padding-bottom: .5rem; } - .import-desc { color: var(--muted); font-size: 13px; margin-bottom: 1.25rem; line-height: 1.65; } - .file-input { display: block; margin-bottom: 1rem; font-size: 13px; } - .btn-submit { - display: inline-flex; - align-items: center; - gap: .4rem; - padding: .5rem 1.1rem; - border: 1.5px solid var(--text); - background: var(--text); - color: var(--ground); - font-size: 12px; - font-family: var(--mono); - font-weight: 500; - letter-spacing: .05em; - text-transform: uppercase; - cursor: pointer; - transition: background .15s; - } - .btn-submit:hover { background: #33271E; border-color: #33271E; } - .msg-ok { padding: .6rem 1rem; border-left: 3px solid var(--pos); background: var(--pos-bg); color: var(--pos); font-size: 13px; margin-bottom: 1rem; } - .msg-err { padding: .6rem 1rem; border-left: 3px solid var(--neg); background: var(--neg-bg); color: var(--neg); font-size: 13px; margin-bottom: 1rem; } - - /* ── Card shell (import page) ── */ - .card { background: var(--surface); border: 1.5px solid var(--rule); } - /* ── Calendrier sélecteur ── */ .cal-trigger-wrap { position: relative; } @@ -519,7 +491,6 @@ Décompte Horaire Semaine courante - Importer ODS
- Sélectionne le fichier exporté par la badgeuse.
- Les données existantes sont mises à jour — les congés ne sont pas affectés.
-