diff --git a/server/app/adapters/postgres_query.py b/server/app/adapters/postgres_query.py index 31d534e..01e8f0b 100644 --- a/server/app/adapters/postgres_query.py +++ b/server/app/adapters/postgres_query.py @@ -1,4 +1,5 @@ from datetime import datetime +from dateutil.relativedelta import relativedelta import psycopg2 from psycopg2.extensions import connection @@ -9,6 +10,14 @@ from domain.value_objects import Granularity from ports.reading_query_repository import ReadingQueryRepository +_GRANULARITY_DELTA = { + "hour": relativedelta(hours=1), + "day": relativedelta(days=1), + "week": relativedelta(weeks=1), + "month": relativedelta(months=1), +} + + class PgReadingQueryRepository(ReadingQueryRepository): def __init__(self, conn: connection) -> None: self._conn = conn @@ -20,24 +29,33 @@ class PgReadingQueryRepository(ReadingQueryRepository): end: datetime, granularity: Granularity, ) -> list[ConsumptionPoint]: + if start == end: + end = start + _GRANULARITY_DELTA[granularity] + adjusted_start = start - _GRANULARITY_DELTA[granularity] date_trunc = granularity query = """ + WITH periods AS ( + SELECT + DATE_TRUNC(%s, r.date) AS period, + MAX(r.pulses) AS pulse_end + FROM reading r + JOIN device d ON d.device_id = r.device_id + WHERE d.device_eui = %s + AND r.date >= %s + AND r.date < %s + GROUP BY period + ) SELECT - DATE_TRUNC(%s, r.date) AS period, - MIN(r.pulses) AS pulse_start, - MAX(r.pulses) AS pulse_end, - MAX(r.pulses) - MIN(r.pulses) AS delta_pulses - FROM reading r - JOIN device d ON d.device_id = r.device_id - WHERE d.device_eui = %s - AND r.date >= %s - AND r.date < %s - GROUP BY period + period, + LAG(pulse_end) OVER (ORDER BY period) AS pulse_start, + pulse_end, + pulse_end - LAG(pulse_end) OVER (ORDER BY period) AS delta_pulses + FROM periods ORDER BY period ASC """ try: with self._conn.cursor() as cur: - cur.execute(query, (date_trunc, dev_eui, start, end)) + cur.execute(query, (date_trunc, dev_eui, adjusted_start, end)) rows = cur.fetchall() except psycopg2.DatabaseError as e: raise DatabaseError(f"Erreur requête consumption : {e}") from e @@ -51,4 +69,5 @@ class PgReadingQueryRepository(ReadingQueryRepository): delta_m3=round(row[3] * 0.010, 3), ) for row in rows + if row[1] is not None ] diff --git a/server/app/requirements.txt b/server/app/requirements.txt index 256990d..552c445 100644 --- a/server/app/requirements.txt +++ b/server/app/requirements.txt @@ -2,4 +2,5 @@ paho-mqtt==v2.1.0 psycopg2-binary==2.9.12 pydantic==2.13.4 fastapi==0.136.1 -uvicorn==0.46.0 \ No newline at end of file +uvicorn==0.46.0 +python-dateutil==2.9.0 \ No newline at end of file diff --git a/server/app/services/consumption_service.py b/server/app/services/consumption_service.py index baacb9b..df3b31f 100644 --- a/server/app/services/consumption_service.py +++ b/server/app/services/consumption_service.py @@ -17,8 +17,8 @@ class ConsumptionService: end: datetime, granularity: Granularity, ) -> ConsumptionResponse: - if start >= end: - raise ValidationError("'start' doit être antérieur à 'end'") + if start > end: + raise ValidationError("'start' doit être inférieur ou égal à 'end'") points = self._repo.get_consumption(dev_eui, start, end, granularity)