diff --git a/app/__init__.py b/app/__init__.py index 124a53c..c59b50b 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,8 +1,9 @@ +import os + +import sqlalchemy as sa +import tomllib from flask import Flask from flask_sqlalchemy import SQLAlchemy -import tomllib -import os -import sqlalchemy as sa db = SQLAlchemy() @@ -10,6 +11,7 @@ db = SQLAlchemy() def _migrate_db(app): """Applique les migrations de schéma manquantes (pas d'Alembic).""" import sqlite3 + db_path = os.path.join(app.instance_path, "worklog.db") if not os.path.exists(db_path): return # Nouvelle DB, create_all() s'en charge @@ -25,27 +27,40 @@ def _migrate_db(app): engine = db.engine if "motor_vehicle_id" not in columns: with engine.connect() as conn: - conn.execute(sa.text( - "ALTER TABLE work_entries ADD COLUMN motor_vehicle_id VARCHAR(64)" - )) + conn.execute( + sa.text("ALTER TABLE work_entries ADD COLUMN motor_vehicle_id VARCHAR(64)") + ) conn.commit() _JOURS_FR = ["lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"] -_MOIS_FR = ["", "janvier", "février", "mars", "avril", "mai", "juin", - "juillet", "août", "septembre", "octobre", "novembre", "décembre"] +_MOIS_FR = [ + "", + "janvier", + "février", + "mars", + "avril", + "mai", + "juin", + "juillet", + "août", + "septembre", + "octobre", + "novembre", + "décembre", +] _DAY_TYPE_LABELS = { - "WORK": "Travail", - "TT": "Télétravail", - "GARDE": "Garde", - "ASTREINTE": "Astreinte", - "FORMATION": "Formation", - "RTT": "RTT", - "CONGE": "Congé", - "MALADE": "Maladie", - "FERIE": "Férié", + "WORK": "Travail", + "TT": "Télétravail", + "GARDE": "Garde", + "ASTREINTE": "Astreinte", + "FORMATION": "Formation", + "RTT": "RTT", + "CONGE": "Congé", + "MALADE": "Maladie", + "FERIE": "Férié", } @@ -55,7 +70,6 @@ def _day_type_fr(code): def _date_fr(d): """Formate une date en français : 'mercredi 11 mars 2026'.""" - from datetime import date as date_type jour = _JOURS_FR[d.weekday()] mois = _MOIS_FR[d.month] return f"{jour} {d.day} {mois} {d.year}" @@ -66,7 +80,9 @@ def create_app(config_path=None): os.makedirs(app.instance_path, exist_ok=True) - app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{os.path.join(app.instance_path, 'worklog.db')}" + app.config["SQLALCHEMY_DATABASE_URI"] = ( + f"sqlite:///{os.path.join(app.instance_path, 'worklog.db')}" + ) app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY", "dev-secret-change-in-prod") @@ -86,6 +102,7 @@ def create_app(config_path=None): from app.routes.dashboard import bp as dashboard_bp from app.routes.entries import bp as entries_bp from app.routes.reports import bp as reports_bp + app.register_blueprint(dashboard_bp) app.register_blueprint(entries_bp) app.register_blueprint(reports_bp) diff --git a/app/business/leave_calc.py b/app/business/leave_calc.py index 7624836..1a40863 100644 --- a/app/business/leave_calc.py +++ b/app/business/leave_calc.py @@ -1,34 +1,40 @@ -from app import db -from app.models import WorkEntry, LeaveBalance -import sqlalchemy as sa from datetime import date +import sqlalchemy as sa + +from app import db +from app.models import LeaveBalance, WorkEntry + def compute_leave_used(year: int) -> dict[str, int]: start = date(year, 1, 1) end = date(year, 12, 31) - conges = db.session.scalar( - sa.select(sa.func.count()).where( - WorkEntry.date.between(start, end), - WorkEntry.day_type == "CONGE", + conges = ( + db.session.scalar( + sa.select(sa.func.count()).where( + WorkEntry.date.between(start, end), + WorkEntry.day_type == "CONGE", + ) ) - ) or 0 + or 0 + ) - rtt = db.session.scalar( - sa.select(sa.func.count()).where( - WorkEntry.date.between(start, end), - WorkEntry.day_type == "RTT", + rtt = ( + db.session.scalar( + sa.select(sa.func.count()).where( + WorkEntry.date.between(start, end), + WorkEntry.day_type == "RTT", + ) ) - ) or 0 + or 0 + ) return {"conges": conges, "rtt": rtt} def get_or_create_balance(year: int) -> LeaveBalance: - balance = db.session.scalar( - sa.select(LeaveBalance).where(LeaveBalance.year == year) - ) + balance = db.session.scalar(sa.select(LeaveBalance).where(LeaveBalance.year == year)) if balance is None: balance = LeaveBalance(year=year) db.session.add(balance) diff --git a/app/business/travel_calc.py b/app/business/travel_calc.py index 8a01ad6..aaf0985 100644 --- a/app/business/travel_calc.py +++ b/app/business/travel_calc.py @@ -32,7 +32,9 @@ def compute_co2_grams(km_by_vehicle: dict[str, int], vehicles: dict) -> float: return total -def compute_frais_reels(total_km_moteur: float, tranches: list[dict], electric: bool = False) -> float: +def compute_frais_reels( + total_km_moteur: float, tranches: list[dict], electric: bool = False +) -> float: """ Calcule les frais réels fiscaux selon le barème kilométrique. km_max = 0 signifie "pas de limite" (dernière tranche). diff --git a/app/models.py b/app/models.py index 4054384..02ab5b8 100644 --- a/app/models.py +++ b/app/models.py @@ -1,8 +1,10 @@ -from app import db -from datetime import date, time, datetime +from datetime import date, datetime, time + import sqlalchemy as sa import sqlalchemy.orm as so +from app import db + class WorkEntry(db.Model): __tablename__ = "work_entries" diff --git a/app/routes/dashboard.py b/app/routes/dashboard.py index 0d1e44a..88b187a 100644 --- a/app/routes/dashboard.py +++ b/app/routes/dashboard.py @@ -1,12 +1,14 @@ -from flask import Blueprint, render_template from datetime import date, timedelta + import sqlalchemy as sa +from flask import Blueprint, render_template + from app import db -from app.models import WorkEntry -from app.business.time_calc import minutes_to_str, work_minutes_reference -from app.business.travel_calc import compute_km_for_entry, compute_co2_grams from app.business.leave_calc import compute_leave_used, get_or_create_balance -from app.config_loader import get_vehicles, get_journeys +from app.business.time_calc import minutes_to_str, work_minutes_reference +from app.business.travel_calc import compute_co2_grams, compute_km_for_entry +from app.config_loader import get_journeys, get_vehicles +from app.models import WorkEntry bp = Blueprint("dashboard", __name__) @@ -45,9 +47,7 @@ def index(): balance = get_or_create_balance(year) used = compute_leave_used(year) - today_entry = db.session.scalar( - sa.select(WorkEntry).where(WorkEntry.date == today) - ) + today_entry = db.session.scalar(sa.select(WorkEntry).where(WorkEntry.date == today)) return render_template( "dashboard.html", diff --git a/app/routes/entries.py b/app/routes/entries.py index 9e97cfb..2b62609 100644 --- a/app/routes/entries.py +++ b/app/routes/entries.py @@ -1,9 +1,16 @@ -from flask import Blueprint, render_template, request, redirect, url_for, flash from datetime import date, time + import sqlalchemy as sa +from flask import Blueprint, flash, redirect, render_template, request, url_for + from app import db -from app.models import WorkEntry, TimeSlot -from app.config_loader import get_journeys, get_motor_vehicles, day_types_without_journey, journey_has_motor +from app.config_loader import ( + day_types_without_journey, + get_journeys, + get_motor_vehicles, + journey_has_motor, +) +from app.models import TimeSlot, WorkEntry bp = Blueprint("entries", __name__, url_prefix="/entries") @@ -22,9 +29,7 @@ DAY_TYPES = [ @bp.route("/") def list_entries(): - entries = db.session.scalars( - sa.select(WorkEntry).order_by(WorkEntry.date.desc()) - ).all() + entries = db.session.scalars(sa.select(WorkEntry).order_by(WorkEntry.date.desc())).all() return render_template("entry_list.html", entries=entries) @@ -51,9 +56,7 @@ def entry_form(entry_id=None): journey_profile_id = None if entry is None: - existing = db.session.scalar( - sa.select(WorkEntry).where(WorkEntry.date == entry_date) - ) + existing = db.session.scalar(sa.select(WorkEntry).where(WorkEntry.date == entry_date)) if existing: flash(f"Une entrée existe déjà pour le {entry_date}.", "error") return redirect(url_for("entries.entry_form")) @@ -72,11 +75,13 @@ def entry_form(entry_id=None): ends = request.form.getlist("end_time") for s, e in zip(starts, ends): if s and e: - db.session.add(TimeSlot( - entry=entry, - start_time=time.fromisoformat(s), - end_time=time.fromisoformat(e), - )) + db.session.add( + TimeSlot( + entry=entry, + start_time=time.fromisoformat(s), + end_time=time.fromisoformat(e), + ) + ) db.session.commit() flash("Entrée enregistrée.", "success") diff --git a/app/routes/reports.py b/app/routes/reports.py index 294425b..1bd0a8b 100644 --- a/app/routes/reports.py +++ b/app/routes/reports.py @@ -1,19 +1,30 @@ -from flask import Blueprint, render_template, request -from datetime import date from collections import defaultdict +from datetime import date + import sqlalchemy as sa +from flask import Blueprint, render_template, request + from app import db +from app.business.time_calc import count_day_types, minutes_to_str, monthly_stats +from app.business.travel_calc import compute_co2_grams, compute_frais_reels, compute_km_for_entry +from app.config_loader import get_bareme, get_journeys, get_vehicles from app.models import WorkEntry -from app.business.travel_calc import compute_km_for_entry, compute_co2_grams, compute_frais_reels -from app.business.time_calc import count_day_types, monthly_stats, minutes_to_str -from app.config_loader import get_vehicles, get_journeys, get_bareme bp = Blueprint("reports", __name__, url_prefix="/reports") MONTHS_FR = { - 1: "Janvier", 2: "Février", 3: "Mars", 4: "Avril", - 5: "Mai", 6: "Juin", 7: "Juillet", 8: "Août", - 9: "Septembre", 10: "Octobre", 11: "Novembre", 12: "Décembre", + 1: "Janvier", + 2: "Février", + 3: "Mars", + 4: "Avril", + 5: "Mai", + 6: "Juin", + 7: "Juillet", + 8: "Août", + 9: "Septembre", + 10: "Octobre", + 11: "Novembre", + 12: "Décembre", } @@ -73,7 +84,9 @@ def index(): "km_by_vehicle": month_km, "km_total": sum(month_km.values()), "median_daily_str": minutes_to_str(stats["median_daily_min"]) if month_entries else "–", - "median_weekly_str": minutes_to_str(stats["median_weekly_min"]) if month_entries else "–", + "median_weekly_str": minutes_to_str(stats["median_weekly_min"]) + if month_entries + else "–", } return render_template( diff --git a/scripts/import_csv.py b/scripts/import_csv.py index 82dfb9c..1372e85 100755 --- a/scripts/import_csv.py +++ b/scripts/import_csv.py @@ -27,29 +27,26 @@ import sqlalchemy as sa sys.path.insert(0, str(Path(__file__).parent.parent)) from app import create_app, db -from app.models import WorkEntry, TimeSlot from app.config_loader import day_types_without_journey, journey_has_motor +from app.models import TimeSlot, WorkEntry - -DAY_TYPES = { - "WORK", "TT", "GARDE", "ASTREINTE", "FORMATION", "RTT", "CONGE", "MALADE", "FERIE" -} +DAY_TYPES = {"WORK", "TT", "GARDE", "ASTREINTE", "FORMATION", "RTT", "CONGE", "MALADE", "FERIE"} def main(csv_path: str, config_path: str = None): """Importe les données depuis un fichier CSV vers la base de données.""" - + # Créer l'application Flask avec la config app = create_app(config_path=config_path) - + with app.app_context(): conflicts = [] imported_count = 0 - + # Lire le fichier CSV with open(csv_path, "r", encoding="utf-8") as f: csv_reader = csv.DictReader(f) - + for row_num, row in enumerate(csv_reader, start=2): try: # Parser la date @@ -57,38 +54,38 @@ def main(csv_path: str, config_path: str = None): except (ValueError, AttributeError): conflicts.append(f"Ligne {row_num}: date invalide ou manquante") continue - + # Valider le type de jour day_type = row.get("day_type", "WORK").strip().upper() if day_type not in DAY_TYPES: conflicts.append(f"Ligne {row_num}: type de jour invalide '{day_type}'") continue - + # Récupérer les autres champs journey_profile_id = row.get("journey_profile_id", "").strip() or None motor_vehicle_id = row.get("motor_vehicle_id", "").strip() or None comment = row.get("comment", "").strip() or None - + # Si le type de jour n'a pas de trajet, forcer journey_profile_id à None if day_type in day_types_without_journey(): journey_profile_id = None - + # Si le profil de trajet n'a pas de moteur, forcer motor_vehicle_id à None if journey_profile_id and not journey_has_motor(journey_profile_id): motor_vehicle_id = None - + # Vérifier si une entrée existe déjà pour cette date existing = db.session.scalar( sa.select(WorkEntry).where(WorkEntry.date == entry_date) ) - + if existing: # Vérifier si les données sont différentes is_different = ( - existing.day_type != day_type or - existing.journey_profile_id != journey_profile_id or - existing.motor_vehicle_id != motor_vehicle_id or - existing.comment != comment + existing.day_type != day_type + or existing.journey_profile_id != journey_profile_id + or existing.motor_vehicle_id != motor_vehicle_id + or existing.comment != comment ) if is_different: conflicts.append( @@ -96,7 +93,7 @@ def main(csv_path: str, config_path: str = None): f"Données existantes conservées." ) continue - + # Créer la nouvelle entrée entry = WorkEntry( date=entry_date, @@ -106,58 +103,64 @@ def main(csv_path: str, config_path: str = None): comment=comment, ) db.session.add(entry) - + # Ajouter les plages horaires start_times = row.get("start_time", "").split(";") end_times = row.get("end_time", "").split(";") - + for s, e in zip(start_times, end_times): s = s.strip() e = e.strip() if s and e: try: - db.session.add(TimeSlot( - entry=entry, - start_time=time.fromisoformat(s), - end_time=time.fromisoformat(e), - )) + db.session.add( + TimeSlot( + entry=entry, + start_time=time.fromisoformat(s), + end_time=time.fromisoformat(e), + ) + ) except (ValueError, AttributeError): - conflicts.append(f"Ligne {row_num}: format d'heure invalide '{s}' ou '{e}'") + conflicts.append( + f"Ligne {row_num}: format d'heure invalide '{s}' ou '{e}'" + ) db.session.rollback() break else: imported_count += 1 continue - + db.session.rollback() break - + # Valider et commiter try: db.session.commit() - except Exception as e: + except sa.exc.SQLAlchemyError as e: db.session.rollback() print(f"Erreur lors du commit: {e}", file=sys.stderr) return 1 - + # Afficher les résultats print(f"Import terminé: {imported_count} entrée(s) importée(s)") - + if conflicts: print(f"\n⚠️ {len(conflicts)} avertissement(s):") for conflict in conflicts: print(f" - {conflict}") - + return 0 if __name__ == "__main__": import argparse - + parser = argparse.ArgumentParser(description="Importer un fichier CSV dans la base de données") parser.add_argument("csv_file", help="Chemin vers le fichier CSV à importer") - parser.add_argument("--config", default=None, help="Chemin vers le fichier config.toml (optionnel)") - + parser.add_argument( + "--config", default=None, help="Chemin vers le fichier config.toml (optionnel)" + ) + args = parser.parse_args() - + sys.exit(main(args.csv_file, args.config)) diff --git a/tests/conftest.py b/tests/conftest.py index 1f18bc4..d1436bd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,14 @@ import pytest -from app import create_app, db as _db + +from app import create_app +from app import db as _db @pytest.fixture def app(tmp_path): config_path = tmp_path / "config.toml" - config_path.write_text(""" + config_path.write_text( + """ [vehicles.citadine] name = "Citadine électrique" fuel = "electric" @@ -59,7 +62,9 @@ forfait = 699 km_max = 0 taux = 0.364 forfait = 0 -""", encoding="utf-8") +""", + encoding="utf-8", + ) application = create_app(config_path=str(config_path)) application.config["TESTING"] = True diff --git a/tests/test_config_loader.py b/tests/test_config_loader.py index 53ed7d6..38abe35 100644 --- a/tests/test_config_loader.py +++ b/tests/test_config_loader.py @@ -1,6 +1,7 @@ def test_get_vehicles_returns_configured_vehicles(app): with app.app_context(): from app.config_loader import get_vehicles + vehicles = get_vehicles() assert "familiale" in vehicles assert vehicles["familiale"]["co2_per_km"] == 142 @@ -9,6 +10,7 @@ def test_get_vehicles_returns_configured_vehicles(app): def test_get_motor_vehicles_excludes_velo(app): with app.app_context(): from app.config_loader import get_motor_vehicles + motor = get_motor_vehicles() assert "familiale" in motor assert "citadine" in motor @@ -19,6 +21,7 @@ def test_get_motor_vehicles_excludes_velo(app): def test_get_journeys_returns_profiles(app): with app.app_context(): from app.config_loader import get_journeys + journeys = get_journeys() assert "moteur_seul" in journeys assert journeys["moteur_seul"]["distances"]["moteur"] == 25 @@ -27,6 +30,7 @@ def test_get_journeys_returns_profiles(app): def test_journey_has_motor_true(app): with app.app_context(): from app.config_loader import journey_has_motor + assert journey_has_motor("moteur_seul") is True assert journey_has_motor("moteur_velo") is True @@ -34,6 +38,7 @@ def test_journey_has_motor_true(app): def test_journey_has_motor_false(app): with app.app_context(): from app.config_loader import journey_has_motor + assert journey_has_motor("velo_seul") is False assert journey_has_motor(None) is False @@ -41,6 +46,7 @@ def test_journey_has_motor_false(app): def test_get_bareme_returns_tranches(app): with app.app_context(): from app.config_loader import get_bareme + tranches = get_bareme(2025, 5) assert len(tranches) == 3 assert tranches[0]["taux"] == 0.548 @@ -49,6 +55,7 @@ def test_get_bareme_returns_tranches(app): def test_day_types_without_journey(app): with app.app_context(): from app.config_loader import day_types_without_journey + types = day_types_without_journey() assert "TT" in types assert "WORK" not in types diff --git a/tests/test_leave_calc.py b/tests/test_leave_calc.py index 1b13862..2397665 100644 --- a/tests/test_leave_calc.py +++ b/tests/test_leave_calc.py @@ -1,8 +1,8 @@ -from app.business.leave_calc import compute_leave_used, get_or_create_balance -from app.models import WorkEntry, LeaveBalance -from app import db from datetime import date -import sqlalchemy as sa + +from app import db +from app.business.leave_calc import compute_leave_used, get_or_create_balance +from app.models import LeaveBalance, WorkEntry def test_compute_leave_used_conges(app): diff --git a/tests/test_routes.py b/tests/test_routes.py index 4560d7f..864a620 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -1,8 +1,10 @@ -from app.models import WorkEntry, TimeSlot -from app import db -from datetime import date, time +from datetime import date + import sqlalchemy as sa +from app import db +from app.models import WorkEntry + def test_dashboard_empty(client): response = client.get("/") @@ -17,21 +19,23 @@ def test_entry_form_get(client): def test_create_entry(client, app): - response = client.post("/entries/new", data={ - "date": "2025-06-02", - "day_type": "WORK", - "journey_profile_id": "moteur_seul", - "motor_vehicle_id": "familiale", - "start_time": ["09:00"], - "end_time": ["17:45"], - "comment": "", - }, follow_redirects=True) + response = client.post( + "/entries/new", + data={ + "date": "2025-06-02", + "day_type": "WORK", + "journey_profile_id": "moteur_seul", + "motor_vehicle_id": "familiale", + "start_time": ["09:00"], + "end_time": ["17:45"], + "comment": "", + }, + follow_redirects=True, + ) assert response.status_code == 200 with app.app_context(): - entry = db.session.scalar( - sa.select(WorkEntry).where(WorkEntry.date == date(2025, 6, 2)) - ) + entry = db.session.scalar(sa.select(WorkEntry).where(WorkEntry.date == date(2025, 6, 2))) assert entry is not None assert entry.day_type == "WORK" assert len(entry.time_slots) == 1 @@ -66,29 +70,29 @@ def test_delete_entry(client, app): assert response.status_code == 200 with app.app_context(): - deleted = db.session.scalar( - sa.select(WorkEntry).where(WorkEntry.id == entry_id) - ) + deleted = db.session.scalar(sa.select(WorkEntry).where(WorkEntry.id == entry_id)) assert deleted is None def test_create_entry_velo_no_motor_vehicle(client, app): """Un trajet vélo seul ne doit pas enregistrer de motor_vehicle_id.""" - response = client.post("/entries/new", data={ - "date": "2025-06-10", - "day_type": "WORK", - "journey_profile_id": "velo_seul", - "motor_vehicle_id": "", - "start_time": ["08:30"], - "end_time": ["17:00"], - "comment": "", - }, follow_redirects=True) + response = client.post( + "/entries/new", + data={ + "date": "2025-06-10", + "day_type": "WORK", + "journey_profile_id": "velo_seul", + "motor_vehicle_id": "", + "start_time": ["08:30"], + "end_time": ["17:00"], + "comment": "", + }, + follow_redirects=True, + ) assert response.status_code == 200 with app.app_context(): - entry = db.session.scalar( - sa.select(WorkEntry).where(WorkEntry.date == date(2025, 6, 10)) - ) + entry = db.session.scalar(sa.select(WorkEntry).where(WorkEntry.date == date(2025, 6, 10))) assert entry is not None assert entry.motor_vehicle_id is None diff --git a/tests/test_time_calc.py b/tests/test_time_calc.py index 446d233..f1ac627 100644 --- a/tests/test_time_calc.py +++ b/tests/test_time_calc.py @@ -1,7 +1,7 @@ from app.business.time_calc import ( minutes_to_str, - work_minutes_reference, week_balance_minutes, + work_minutes_reference, ) @@ -42,10 +42,11 @@ def test_week_balance_negative(): assert week_balance_minutes(2200, 2325) == -125 -from app.business.time_calc import count_day_types -from app.models import WorkEntry, TimeSlot -from app.business.time_calc import monthly_stats -from datetime import date, time as dtime +from datetime import date +from datetime import time as dtime + +from app.business.time_calc import count_day_types, monthly_stats +from app.models import TimeSlot, WorkEntry def test_count_day_types_basic(): @@ -67,8 +68,10 @@ def _entry(d: date, *slots: tuple[str, str]) -> WorkEntry: """Helper : crée un WorkEntry avec des TimeSlots.""" entry = WorkEntry(date=d, day_type="WORK") entry.time_slots = [ - TimeSlot(start_time=dtime(*[int(x) for x in s.split(":")]), - end_time=dtime(*[int(x) for x in e.split(":")])) + TimeSlot( + start_time=dtime(*[int(x) for x in s.split(":")]), + end_time=dtime(*[int(x) for x in e.split(":")]), + ) for s, e in slots ] return entry @@ -89,9 +92,9 @@ def test_monthly_stats_empty(): def test_monthly_stats_median_daily_odd(): # 420, 465, 510 → médiane = 465 entries = [ - _entry(date(2025, 1, 6), ("9:00", "16:00")), # 420 min - _entry(date(2025, 1, 7), ("9:00", "16:45")), # 465 min - _entry(date(2025, 1, 8), ("9:00", "17:30")), # 510 min + _entry(date(2025, 1, 6), ("9:00", "16:00")), # 420 min + _entry(date(2025, 1, 7), ("9:00", "16:45")), # 465 min + _entry(date(2025, 1, 8), ("9:00", "17:30")), # 510 min ] result = monthly_stats(entries) assert result["median_daily_min"] == 465 @@ -114,7 +117,7 @@ def test_monthly_stats_median_weekly(): entries = [ _entry(date(2025, 1, 6), ("9:00", "16:45")), # sem 2 _entry(date(2025, 1, 7), ("9:00", "16:45")), # sem 2 - _entry(date(2025, 1, 13), ("9:00", "16:00")), # sem 3 + _entry(date(2025, 1, 13), ("9:00", "16:00")), # sem 3 ] result = monthly_stats(entries) assert result["median_weekly_min"] == 675 diff --git a/tests/test_travel_calc.py b/tests/test_travel_calc.py index 5284c73..ee9b654 100644 --- a/tests/test_travel_calc.py +++ b/tests/test_travel_calc.py @@ -1,7 +1,7 @@ from app.business.travel_calc import ( - compute_km_for_entry, compute_co2_grams, compute_frais_reels, + compute_km_for_entry, ) VEHICLES = { @@ -17,15 +17,15 @@ JOURNEYS = { } TRANCHES_CV3 = [ - {"km_max": 5000, "taux": 0.529, "forfait": 0}, + {"km_max": 5000, "taux": 0.529, "forfait": 0}, {"km_max": 20000, "taux": 0.316, "forfait": 1065}, - {"km_max": 0, "taux": 0.370, "forfait": 0}, + {"km_max": 0, "taux": 0.370, "forfait": 0}, ] TRANCHES_CV5 = [ - {"km_max": 5000, "taux": 0.636, "forfait": 0}, + {"km_max": 5000, "taux": 0.636, "forfait": 0}, {"km_max": 20000, "taux": 0.357, "forfait": 1395}, - {"km_max": 0, "taux": 0.427, "forfait": 0}, + {"km_max": 0, "taux": 0.427, "forfait": 0}, ]