calcul des heures
This commit is contained in:
40
app/main.py
40
app/main.py
@ -18,10 +18,11 @@ from models import (
|
|||||||
toggle_conge, list_all_weeks,
|
toggle_conge, list_all_weeks,
|
||||||
load_notif_config, save_notif_config, find_user_by_token, save_presence,
|
load_notif_config, save_notif_config, find_user_by_token, save_presence,
|
||||||
load_auth, save_auth, find_user_by_reset_token,
|
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 calcul import compute_week, minutes_to_hhmm, hhmm_to_minutes, heures_dues
|
||||||
from stats import compute_all_stats
|
from stats import compute_all_stats
|
||||||
|
from export import export_semaine_ods, export_annee_ods
|
||||||
from notifications import reminder_loop
|
from notifications import reminder_loop
|
||||||
|
|
||||||
logging.basicConfig(
|
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)]
|
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:
|
def _weeks_by_year(all_weeks: list) -> dict:
|
||||||
result = {}
|
result = {}
|
||||||
for yr, wks in groupby(sorted(all_weeks, reverse=True), key=lambda x: x[0]):
|
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)
|
@app.get("/settings", response_class=HTMLResponse)
|
||||||
def settings_page(request: Request):
|
def settings_page(request: Request):
|
||||||
user_id = _get_user(request)
|
user_id = _get_user(request)
|
||||||
|
|||||||
@ -68,6 +68,10 @@ def save_config(cfg: dict):
|
|||||||
CONFIG_FILE.write_text(json.dumps(cfg, indent=2, ensure_ascii=False))
|
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 ─────────────────────────────────────────────────────────────────
|
# ── Pointages ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def _week_file(user_id: str, year: int, week: int) -> Path:
|
def _week_file(user_id: str, year: int, week: int) -> Path:
|
||||||
|
|||||||
@ -127,6 +127,19 @@
|
|||||||
}
|
}
|
||||||
.week-id .wk { color: var(--muted); font-size: 1rem; font-weight: 300; }
|
.week-id .wk { color: var(--muted); font-size: 1rem; font-weight: 300; }
|
||||||
.week-id .slash { color: var(--rule); margin: 0 .2em; }
|
.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 ── */
|
||||||
.soldes {
|
.soldes {
|
||||||
@ -384,6 +397,8 @@
|
|||||||
}
|
}
|
||||||
.chip:hover { border-color: var(--text); color: var(--text); }
|
.chip:hover { border-color: var(--text); color: var(--text); }
|
||||||
.chip.here { background: var(--text); border-color: var(--text); color: var(--ground); }
|
.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 ── */
|
/* ── Calendrier sélecteur ── */
|
||||||
.cal-trigger-wrap { position: relative; }
|
.cal-trigger-wrap { position: relative; }
|
||||||
|
|||||||
@ -32,6 +32,11 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
|
|
||||||
<!-- Soldes -->
|
<!-- Soldes -->
|
||||||
@ -80,6 +85,9 @@
|
|||||||
S{{ '%02d' % yw[1] }}
|
S{{ '%02d' % yw[1] }}
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<a href="/export/annee/{{ yr }}" class="chip chip-export" title="Exporter l'année {{ yr }} (.ods)">
|
||||||
|
⇩ {{ yr }}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
Reference in New Issue
Block a user