calcul des heures

This commit is contained in:
Antoine
2026-07-17 23:51:32 +02:00
parent 12666e73f4
commit 5e9d974875
4 changed files with 62 additions and 5 deletions

View File

@ -18,10 +18,11 @@ from models import (
toggle_conge, list_all_weeks,
load_notif_config, save_notif_config, find_user_by_token, save_presence,
load_auth, save_auth, find_user_by_reset_token,
load_plages, save_plages,
load_plages, save_plages, heures_jour_min,
)
from calcul import compute_week, minutes_to_hhmm, hhmm_to_minutes, heures_dues
from stats import compute_all_stats
from export import export_semaine_ods, export_annee_ods
from notifications import reminder_loop
logging.basicConfig(
@ -94,10 +95,6 @@ def week_dates(year: int, week: int) -> list[str]:
return [(first + timedelta(days=i)).strftime("%Y-%m-%d") for i in range(5)]
def heures_jour_min() -> int:
return int(float(load_config().get("heures_jour", 7.8)) * 60)
def _weeks_by_year(all_weeks: list) -> dict:
result = {}
for yr, wks in groupby(sorted(all_weeks, reverse=True), key=lambda x: x[0]):
@ -518,6 +515,39 @@ def stats_page(request: Request):
})
ODS_MEDIA_TYPE = "application/vnd.oasis.opendocument.spreadsheet"
@app.get("/export/semaine/{year}/{week}")
def export_semaine(request: Request, year: int, week: int):
user_id = _get_user(request)
if not user_id:
return RedirectResponse("/login", status_code=302)
content = export_semaine_ods(year, week, user_id)
filename = f"pointages-{user_id}-{year}-S{week:02d}.ods"
logger.info("export ODS semaine: user_id=%r year=%d week=%d", user_id, year, week)
return Response(
content=content, media_type=ODS_MEDIA_TYPE,
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
@app.get("/export/annee/{year}")
def export_annee(request: Request, year: int):
user_id = _get_user(request)
if not user_id:
return RedirectResponse("/login", status_code=302)
content = export_annee_ods(year, user_id)
filename = f"pointages-{user_id}-{year}.ods"
logger.info("export ODS année: user_id=%r year=%d", user_id, year)
return Response(
content=content, media_type=ODS_MEDIA_TYPE,
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
@app.get("/settings", response_class=HTMLResponse)
def settings_page(request: Request):
user_id = _get_user(request)

View File

@ -68,6 +68,10 @@ def save_config(cfg: dict):
CONFIG_FILE.write_text(json.dumps(cfg, indent=2, ensure_ascii=False))
def heures_jour_min() -> int:
return int(float(load_config().get("heures_jour", 7.8)) * 60)
# ── Pointages ─────────────────────────────────────────────────────────────────
def _week_file(user_id: str, year: int, week: int) -> Path:

View File

@ -127,6 +127,19 @@
}
.week-id .wk { color: var(--muted); font-size: 1rem; font-weight: 300; }
.week-id .slash { color: var(--rule); margin: 0 .2em; }
.week-header-spacer { flex: 1; }
.export-btn {
font-family: var(--mono);
font-size: 11px;
letter-spacing: .04em;
color: var(--muted);
text-decoration: none;
border: 1.5px solid var(--rule);
padding: .4rem .75rem;
transition: border-color .15s, color .15s;
white-space: nowrap;
}
.export-btn:hover { border-color: var(--accent); color: var(--accent); }
/* ── Soldes ── */
.soldes {
@ -384,6 +397,8 @@
}
.chip:hover { border-color: var(--text); color: var(--text); }
.chip.here { background: var(--text); border-color: var(--text); color: var(--ground); }
.chip.chip-export { color: var(--accent); border-color: rgba(47,95,160,.35); margin-left: .3rem; }
.chip.chip-export:hover { border-color: var(--accent); background: rgba(47,95,160,.06); }
/* ── Calendrier sélecteur ── */
.cal-trigger-wrap { position: relative; }

View File

@ -32,6 +32,11 @@
</table>
</div>
</div>
<span class="week-header-spacer"></span>
<a href="/export/semaine/{{ year }}/{{ week }}" class="export-btn" title="Exporter cette semaine (.ods)">
⇩ Exporter
</a>
</div>
<!-- Soldes -->
@ -80,6 +85,9 @@
S{{ '%02d' % yw[1] }}
</a>
{% endfor %}
<a href="/export/annee/{{ yr }}" class="chip chip-export" title="Exporter l'année {{ yr }} (.ods)">
⇩ {{ yr }}
</a>
</div>
</div>
{% endfor %}