ADD: Measurement Records

This commit is contained in:
sebastian 2026-06-30 22:02:21 +02:00
parent ed0d2897ce
commit 3483a7008c
3 changed files with 118 additions and 6 deletions

View File

@ -4,12 +4,14 @@ from typing import List
from fastapi import FastAPI, Depends, Query from fastapi import FastAPI, Depends, Query
from enum import Enum from enum import Enum
from requests import session
from sqlmodel import Session from sqlmodel import Session
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
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 MeasurementResolution, RecordResponse
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
@ -59,3 +61,7 @@ async def get_measurements(
limit: int | None = None, limit: int | None = None,
session: Session = Depends(get_session)): session: Session = Depends(get_session)):
return measurementService.get_measurements(session, station_ids, indoor, from_timestamp, to_timestamp, limit, resolution) return measurementService.get_measurements(session, station_ids, indoor, from_timestamp, to_timestamp, limit, resolution)
@app.get('/measurements/records', response_model=RecordResponse, status_code=200)
async def get_records(station_ids: list[int] | None = Query(default=None), from_timestamp: datetime | None = None, session: Session = Depends(get_session)):
return RecordResponse.model_validate(measurementService.get_measurement_records(session, station_ids, from_timestamp))

View File

@ -58,4 +58,21 @@ class StationMeasurementResponse(SQLModel):
indoor: bool indoor: bool
class MeasurementListResponse(SQLModel): class MeasurementListResponse(SQLModel):
stations: list[StationMeasurementResponse] stations: list[StationMeasurementResponse]
class MeasurementRecordItem(SQLModel):
recordValue: float
timestamp: datetime
station: StationListResponse
class MeasurementRecord(SQLModel):
min_value: MeasurementRecordItem
max_value: MeasurementRecordItem
class RecordResponse(SQLModel):
inner_temperature_record: MeasurementRecord
outer_temperature_record: MeasurementRecord
inner_humidity_record: MeasurementRecord
outer_humidity_record: MeasurementRecord
pressure_record: MeasurementRecord
night_temperature_record: MeasurementRecord

View File

@ -1,10 +1,11 @@
from datetime import datetime from datetime import datetime
from sqlalchemy import func from sqlalchemy import func, Select, Integer
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, MeasurementResolution OutdoorMeasurement, MeasurementListResponse, StationMeasurementResponse, MeasurementResponse, MeasurementResolution, \
MeasurementRecord, RecordResponse, MeasurementRecordItem
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
@ -48,7 +49,7 @@ def push_outdoor_measurement(session: Session, raw_measurement: OutdoorMeasureme
session.add(OutdoorMeasurement.model_validate(measurement)) session.add(OutdoorMeasurement.model_validate(measurement))
session.commit() session.commit()
from typing import Type, Union from typing import Type, Union, List
def period_format(resolution: MeasurementResolution) -> str: def period_format(resolution: MeasurementResolution) -> str:
@ -208,4 +209,92 @@ def get_measurements(session: Session, station_ids: list[int] | None, indoor: bo
stations=[ stations=[
*[StationMeasurementResponse(station=outdoor_result.station, measurements=outdoor_result.measurements, indoor=False) for outdoor_result in outdoor_results], *[StationMeasurementResponse(station=outdoor_result.station, measurements=outdoor_result.measurements, indoor=False) for outdoor_result in outdoor_results],
] ]
) )
def get_measurement_record(
session: Session,
model: Type[IndoorMeasurement | OutdoorMeasurement],
field: str,
station_ids: list[int] | None,
from_timestamp: datetime | None,
order: str,
night_only: bool = False,
) -> MeasurementRecordItem | None:
column = getattr(model, field)
statement = select(model)
if station_ids is not None:
statement = statement.where(model.station_id.in_(station_ids))
if from_timestamp is not None:
to_timestamp = datetime.now()
statement = statement.where(model.timestamp >= from_timestamp)
statement = statement.where(model.timestamp <= to_timestamp)
if night_only:
hour = func.cast(func.strftime("%H", model.timestamp), Integer)
statement = statement.where((hour >= 22) | (hour < 6))
statement = statement.where(column.is_not(None))
statement = statement.order_by(column.desc() if order == 'max' else column.asc())
statement = statement.limit(1)
result = session.exec(statement).first()
if result is None:
return None
station = session.get(Station, result.station_id)
return MeasurementRecordItem(
recordValue=getattr(result, field),
timestamp=result.timestamp,
station=StationListResponse.model_validate(station),
)
def get_inner_temperature_record(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None, order: str) -> MeasurementRecordItem | None:
return get_measurement_record(session, IndoorMeasurement, "temperature", station_ids, from_timestamp, order)
def get_outer_temperature_record(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None, order: str) -> MeasurementRecordItem | None:
return get_measurement_record(session, OutdoorMeasurement, "temperature", station_ids, from_timestamp, order)
def get_inner_humidity_record(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None, order: str) -> MeasurementRecordItem | None:
return get_measurement_record(session, IndoorMeasurement, "humidity", station_ids, from_timestamp, order)
def get_outer_humidity_record(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None, order: str) -> MeasurementRecordItem | None:
return get_measurement_record(session, OutdoorMeasurement, "humidity", station_ids, from_timestamp, order)
def get_pressure_record(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None, order: str) -> MeasurementRecordItem | None:
return get_measurement_record(session, OutdoorMeasurement, "pressure", station_ids, from_timestamp, order)
def get_warmest_night_record(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None) -> MeasurementRecordItem | None:
return get_measurement_record(session, OutdoorMeasurement, "temperature", station_ids, from_timestamp, "max", night_only=True)
def get_coldest_night_record(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None) -> MeasurementRecordItem | None:
return get_measurement_record(session, OutdoorMeasurement, "temperature", station_ids, from_timestamp, "min", night_only=True)
def get_measurement_records(session: Session, station_ids: list[int] | None, from_timestamp: datetime | None):
return RecordResponse(
inner_temperature_record = MeasurementRecord(
max_value=get_inner_temperature_record(session, station_ids, from_timestamp, order='max'),
min_value=get_inner_temperature_record(session, station_ids, from_timestamp, order='min')
),
outer_temperature_record = MeasurementRecord(
max_value=get_outer_temperature_record(session, station_ids, from_timestamp, order='max'),
min_value=get_outer_temperature_record(session, station_ids, from_timestamp, order='min')
),
inner_humidity_record = MeasurementRecord(
max_value=get_inner_humidity_record(session, station_ids, from_timestamp, order='max'),
min_value=get_inner_humidity_record(session, station_ids, from_timestamp, order='min')
),
outer_humidity_record = MeasurementRecord(
max_value=get_outer_humidity_record(session, station_ids, from_timestamp, order='max'),
min_value=get_outer_humidity_record(session, station_ids, from_timestamp, order='min')
),
pressure_record = MeasurementRecord(
max_value=get_pressure_record(session, station_ids, from_timestamp, order='max'),
min_value=get_pressure_record(session, station_ids, from_timestamp, order='min')
),
night_temperature_record = MeasurementRecord(
max_value=get_warmest_night_record(session, None, from_timestamp),
min_value=get_coldest_night_record(session, None, from_timestamp),
)
)