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 enum import Enum
from requests import session
from sqlmodel import Session
from fastapi.middleware.cors import CORSMiddleware
from db import get_session
from models.measurement import IndoorMeasurementCreateRequest, MeasurementListResponse, OutdoorMeasurementCreateRequest, \
MeasurementResolution
MeasurementResolution, RecordResponse
from models.station import StationCreateRequest, StationCreateResponse, Station, StationListResponse, \
StationUpdateResponse, StationUpdateRequest
from services import stationService, measurementService
@ -59,3 +61,7 @@ async def get_measurements(
limit: int | None = None,
session: Session = Depends(get_session)):
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

@ -59,3 +59,20 @@ class StationMeasurementResponse(SQLModel):
class MeasurementListResponse(SQLModel):
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 sqlalchemy import func
from sqlalchemy import func, Select, Integer
from sqlmodel import Session, select
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 services import stationService
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.commit()
from typing import Type, Union
from typing import Type, Union, List
def period_format(resolution: MeasurementResolution) -> str:
@ -209,3 +210,91 @@ def get_measurements(session: Session, station_ids: list[int] | None, indoor: bo
*[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),
)
)