ADD: Fetch Measurements
This commit is contained in:
parent
706edeb429
commit
32fc8a031d
7
main.py
7
main.py
@ -2,10 +2,12 @@ import functools
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from fastapi import FastAPI, Depends, Query
|
from fastapi import FastAPI, Depends, Query
|
||||||
|
from enum import Enum
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
|
|
||||||
from db import get_session
|
from db import get_session
|
||||||
from models.measurement import IndoorMeasurementCreateRequest, MeasurementListResponse, OutdoorMeasurementCreateRequest
|
from models.measurement import IndoorMeasurementCreateRequest, MeasurementListResponse, OutdoorMeasurementCreateRequest, \
|
||||||
|
MeasurementResolution
|
||||||
from models.station import StationCreateRequest, StationCreateResponse, Station, StationListResponse, \
|
from models.station import StationCreateRequest, StationCreateResponse, Station, StationListResponse, \
|
||||||
StationUpdateResponse, StationUpdateRequest
|
StationUpdateResponse, StationUpdateRequest
|
||||||
from services import stationService, measurementService
|
from services import stationService, measurementService
|
||||||
@ -45,9 +47,10 @@ async def get_measurements(
|
|||||||
indoor: bool | None = Query(default=None),
|
indoor: bool | None = Query(default=None),
|
||||||
from_timestamp: datetime | None = None,
|
from_timestamp: datetime | None = None,
|
||||||
to_timestamp: datetime | None = None,
|
to_timestamp: datetime | None = None,
|
||||||
|
resolution: MeasurementResolution = MeasurementResolution.hourly,
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
session: Session = Depends(get_session)):
|
session: Session = Depends(get_session)):
|
||||||
return measurementService.get_measurements(session, station_ids, indoor, from_timestamp, to_timestamp, limit)
|
return measurementService.get_measurements(session, station_ids, indoor, from_timestamp, to_timestamp, limit, resolution)
|
||||||
@app.get("/hello/{name}")
|
@app.get("/hello/{name}")
|
||||||
async def say_hello(name: str):
|
async def say_hello(name: str):
|
||||||
return {"message": f"Hello {name}"}
|
return {"message": f"Hello {name}"}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
from enum import Enum
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from sqlmodel import SQLModel, Field
|
from sqlmodel import SQLModel, Field
|
||||||
@ -9,6 +10,13 @@ from models.station import StationListResponse
|
|||||||
def utc_now() -> datetime:
|
def utc_now() -> datetime:
|
||||||
return datetime.now(timezone.utc)
|
return datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
class MeasurementResolution(str, Enum):
|
||||||
|
hourly = "hourly"
|
||||||
|
daily = "daily"
|
||||||
|
weekly = "weekly"
|
||||||
|
monthly = "monthly"
|
||||||
|
yearly = "yearly"
|
||||||
|
|
||||||
|
|
||||||
class IndoorMeasurement(SQLModel, table=True):
|
class IndoorMeasurement(SQLModel, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from itertools import groupby
|
||||||
|
from statistics import mean
|
||||||
|
|
||||||
|
from sqlalchemy import func
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
|
||||||
from models.measurement import IndoorMeasurement, IndoorMeasurementCreateRequest, OutdoorMeasurementCreateRequest, \
|
from models.measurement import IndoorMeasurement, IndoorMeasurementCreateRequest, OutdoorMeasurementCreateRequest, \
|
||||||
OutdoorMeasurement, MeasurementListResponse, StationMeasurementResponse, MeasurementResponse
|
OutdoorMeasurement, MeasurementListResponse, StationMeasurementResponse, MeasurementResponse, MeasurementResolution
|
||||||
from models.station import Station, StationCreateRequest, StationListResponse
|
from models.station import Station, StationCreateRequest, StationListResponse
|
||||||
from services import stationService
|
from services import stationService
|
||||||
from coolname import generate_slug
|
from coolname import generate_slug
|
||||||
@ -49,6 +52,29 @@ def push_outdoor_measurement(session: Session, raw_measurement: OutdoorMeasureme
|
|||||||
session.commit()
|
session.commit()
|
||||||
from typing import Type, Union
|
from typing import Type, Union
|
||||||
|
|
||||||
|
def aggregate(measurements: list[MeasurementResponse], resolution: MeasurementResolution) -> list[MeasurementResponse]:
|
||||||
|
def period_key(m: MeasurementResponse) -> str:
|
||||||
|
formats = {
|
||||||
|
MeasurementResolution.hourly: "%Y-%m-%d %H",
|
||||||
|
MeasurementResolution.daily: "%Y-%m-%d",
|
||||||
|
MeasurementResolution.weekly: "%Y-%W",
|
||||||
|
MeasurementResolution.monthly: "%Y-%m",
|
||||||
|
MeasurementResolution.yearly: "%Y",
|
||||||
|
}
|
||||||
|
return m.timestamp.strftime(formats[resolution])
|
||||||
|
|
||||||
|
result = []
|
||||||
|
sorted_measurements = sorted(measurements, key=period_key)
|
||||||
|
for _, group in groupby(sorted_measurements, key=period_key):
|
||||||
|
group_list = list(group)
|
||||||
|
result.append(MeasurementResponse(
|
||||||
|
timestamp=group_list[0].timestamp,
|
||||||
|
temperature=round(mean(m.temperature for m in group_list if m.temperature is not None), 1),
|
||||||
|
humidity=round(mean(m.humidity for m in group_list if m.humidity is not None), 1) if any(m.humidity for m in group_list) else None,
|
||||||
|
pressure=round(mean(m.pressure for m in group_list if m.pressure is not None), 1) if any(m.pressure for m in group_list) else None,
|
||||||
|
))
|
||||||
|
return result
|
||||||
|
|
||||||
def _query_measurements(
|
def _query_measurements(
|
||||||
session: Session,
|
session: Session,
|
||||||
model: Type[Union[IndoorMeasurement, OutdoorMeasurement]],
|
model: Type[Union[IndoorMeasurement, OutdoorMeasurement]],
|
||||||
@ -56,7 +82,8 @@ def _query_measurements(
|
|||||||
station_ids: list[int] | None,
|
station_ids: list[int] | None,
|
||||||
from_timestamp: datetime | None = None,
|
from_timestamp: datetime | None = None,
|
||||||
to_timestamp: datetime | None = None,
|
to_timestamp: datetime | None = None,
|
||||||
limit: int | None = None
|
limit: int | None = None,
|
||||||
|
resolution: MeasurementResolution = MeasurementResolution.hourly,
|
||||||
) -> list[StationMeasurementResponse]:
|
) -> list[StationMeasurementResponse]:
|
||||||
statement = select(model)
|
statement = select(model)
|
||||||
if station_ids:
|
if station_ids:
|
||||||
@ -65,7 +92,7 @@ def _query_measurements(
|
|||||||
statement = statement.where(model.timestamp >= from_timestamp)
|
statement = statement.where(model.timestamp >= from_timestamp)
|
||||||
if to_timestamp:
|
if to_timestamp:
|
||||||
statement = statement.where(model.timestamp <= to_timestamp)
|
statement = statement.where(model.timestamp <= to_timestamp)
|
||||||
statement = statement.order_by(model.timestamp.desc()).limit(limit)
|
statement = statement.order_by(model.timestamp.desc())
|
||||||
results = session.exec(statement).all()
|
results = session.exec(statement).all()
|
||||||
|
|
||||||
grouped: dict[int, list[MeasurementResponse]] = {}
|
grouped: dict[int, list[MeasurementResponse]] = {}
|
||||||
@ -74,6 +101,17 @@ def _query_measurements(
|
|||||||
grouped[m.station_id] = []
|
grouped[m.station_id] = []
|
||||||
grouped[m.station_id].append(MeasurementResponse.model_validate(m))
|
grouped[m.station_id].append(MeasurementResponse.model_validate(m))
|
||||||
|
|
||||||
|
grouped = {
|
||||||
|
station_id: aggregate(measurements, resolution)
|
||||||
|
for station_id, measurements in grouped.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit:
|
||||||
|
grouped = {
|
||||||
|
station_id: measurements[:limit]
|
||||||
|
for station_id, measurements in grouped.items()
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
StationMeasurementResponse(
|
StationMeasurementResponse(
|
||||||
station=StationListResponse.model_validate(session.get(Station, station_id)),
|
station=StationListResponse.model_validate(session.get(Station, station_id)),
|
||||||
@ -89,7 +127,7 @@ def get_indoor_measurements(session: Session, station_ids: list[int] | None, fro
|
|||||||
def get_outdoor_measurements(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None = None, to_timestamp: datetime | None = None, limit: int | None = None) -> list[StationMeasurementResponse]:
|
def get_outdoor_measurements(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None = None, to_timestamp: datetime | None = None, limit: int | None = None) -> list[StationMeasurementResponse]:
|
||||||
return _query_measurements(session, OutdoorMeasurement, False, station_ids, from_timestamp, to_timestamp, limit)
|
return _query_measurements(session, OutdoorMeasurement, False, station_ids, from_timestamp, to_timestamp, limit)
|
||||||
|
|
||||||
def get_measurements(session: Session, station_ids: list[int] | None, indoor: bool | None, from_timestamp: datetime | None = None, to_timestamp: datetime | None = None, limit: int | None = None):
|
def get_measurements(session: Session, station_ids: list[int] | None, indoor: bool, from_timestamp: datetime | None, to_timestamp: datetime | None,limit: int, resolution: MeasurementResolution ):
|
||||||
if indoor is None:
|
if indoor is None:
|
||||||
indoor_results = get_indoor_measurements(session, station_ids, from_timestamp, to_timestamp, limit)
|
indoor_results = get_indoor_measurements(session, station_ids, from_timestamp, to_timestamp, limit)
|
||||||
outdoor_results = get_outdoor_measurements(session, station_ids, from_timestamp, to_timestamp, limit)
|
outdoor_results = get_outdoor_measurements(session, station_ids, from_timestamp, to_timestamp, limit)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user