diff --git a/main.py b/main.py index 689aac7..14a710f 100644 --- a/main.py +++ b/main.py @@ -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)) \ No newline at end of file diff --git a/models/measurement.py b/models/measurement.py index 1184642..145b173 100644 --- a/models/measurement.py +++ b/models/measurement.py @@ -58,4 +58,21 @@ class StationMeasurementResponse(SQLModel): indoor: bool class MeasurementListResponse(SQLModel): - stations: list[StationMeasurementResponse] \ No newline at end of file + 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 \ No newline at end of file diff --git a/services/measurementService.py b/services/measurementService.py index 27f32d7..5db712c 100644 --- a/services/measurementService.py +++ b/services/measurementService.py @@ -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: @@ -208,4 +209,92 @@ def get_measurements(session: Session, station_ids: list[int] | None, indoor: bo stations=[ *[StationMeasurementResponse(station=outdoor_result.station, measurements=outdoor_result.measurements, indoor=False) for outdoor_result in outdoor_results], ] - ) \ No newline at end of file + ) + +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), + ) + ) \ No newline at end of file