Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

45 add month and year avg for endpoint stationhistorical #46

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions code/enums.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from enum import Enum


class Dimension():
PM0_1 = 1
PM1_0 = 2
Expand Down Expand Up @@ -209,3 +212,34 @@ def get_name(cls, source_id: int) -> str:
:return: Der zugehörige Name oder 'Unknown', wenn kein Name vorhanden ist
"""
return cls._names.get(source_id, "Unknown")


class Precision(str, Enum):
MAX = "all"
HOURLY = "hour"
DAYLY = "day"
WEEKLY = "week"
MONTHLY = "month"
YEARYLY = "year"

__to_time_frame = {
MAX: "milliseconds",
HOURLY: "hour",
DAYLY: "day",
WEEKLY: "week",
MONTHLY: "month",
YEARYLY: "year",
}

@classmethod
def get_time_frame(cls, precision: str):
return cls.__to_time_frame[precision]


class OutputFormat(str, Enum):
JSON = "json"
CSV = "csv"

class Order(str, Enum):
MIN="min"
MAX="max"
73 changes: 35 additions & 38 deletions code/routers/station.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

from models import Station, Location, Measurement, Values, StationStatus, HourlyDimensionAverages, City
from schemas import StationDataCreate, SensorsCreate, StationStatusCreate
from utils import get_or_create_location, download_csv, get_or_create_station
from utils import get_or_create_location, download_csv, get_or_create_station, standard_output_to_csv, standard_output_to_json
from enums import Precision, OutputFormat, Order, Dimension


router = APIRouter()
Expand Down Expand Up @@ -284,25 +285,47 @@ async def create_station_data(
return {"status": "success"}


class Precision(str, Enum):
MAX = "all data points"
HOURLY = "hourly avg (one data point per hour)"
DAYLY = "dayly avg (one data point per day)"
@router.get("/topn", response_class=Response, tags=["station"])
async def get_topn_stations_by_dim(
n: int = Query(..., description="limit"),
dimension: int = Query(..., description="Dimension ID to compare"),
order: Order = Query(Order.MIN, description="Get (Min/Max) n stations"),
output_format: OutputFormat = Query(OutputFormat.CSV, description="Ouput format"),
db: Session = Depends(get_db)
):

compare = Values.value
if order == Order.MAX:
compare = Values.value.desc()
q = (
db.query(
Station.device,
Measurement.time_measured,
Values.dimension,
Values.value,
)
.join(Measurement, Measurement.station_id == Station.id)
.join(Values, Values.measurement_id == Measurement.id)
.filter(Station.last_active == Measurement.time_measured)
.filter(Values.dimension == dimension)
.order_by(compare)
.limit(n)
)

class OutputFormat(str, Enum):
JSON = "json"
CSV = "csv"
if output_format == 'csv':
return Response(content=standard_output_to_csv(q.all()), media_type="text/csv")
elif output_format == 'json':
return Response(content=standard_output_to_json(q.all()), media_type="application/json")


@router.get("/historical", response_class=Response, tags=["station"])
async def get_historical_station_data(
station_ids: str = Query(..., description="Comma-separated list of station devices"),
start: str = Query(None, description="Supply in format: YYYY-MM-DDThh:mm. Time is optional."),
end: str = Query(None, description="Supply in format: YYYY-MM-DDThh:mm. Time is optional."),
output_format: OutputFormat = Query(OutputFormat.CSV, description="Ouput format"),
precision: Precision = Query(Precision.MAX, description="Precision of data points"),
city_slugs: str = Query(None, description="Comma-seperated list of city_slugs"),
output_format: OutputFormat = Query(OutputFormat.CSV, description="Ouput format"),
db: Session = Depends(get_db)
):
# Konvertiere die Liste von station_devices in eine Liste
Expand All @@ -316,14 +339,7 @@ async def get_historical_station_data(
except ValueError:
raise HTTPException(status_code=400, detail="Invalid date format. Use YYYY-MM-DDThh:mm")

time_fram = None
if precision == Precision.MAX:
time_fram = 'milliseconds'
if precision == Precision.HOURLY:
time_fram = 'hour'
if precision == Precision.DAYLY:
time_fram = 'day'

time_fram = Precision.get_time_frame(precision)
truncated_time = func.date_trunc(time_fram, Measurement.time_measured).label('time')

q = (
Expand All @@ -349,28 +365,9 @@ async def get_historical_station_data(
q = q.filter(truncated_time <= end_date)

if output_format == 'csv':
csv_data = "device,time_measured,dimension,value\n"
for device, time, dim, val in q.all():
csv_data += f"{device},{time.strftime("%Y-%m-%dT%H:%M")},{dim},{val}\n"
return Response(content=csv_data, media_type="text/csv")
return Response(content=standard_output_to_csv(q.all()), media_type="text/csv")
elif output_format == 'json':
groups = groupby(q.all(), lambda x: (x[0], x[1]))
json_data = [
{
"device": device,
"time_measured": time.strftime("%Y-%m-%dT%H:%M"),
"values": [
{
"dimension": dim,
"value": val
}
for (_, _, dim, val) in data
]
}
for ((device, time), data) in groups
]

return Response(content=json.dumps(json_data), media_type="application/json")
return Response(content=standard_output_to_json(q.all()), media_type="application/json")


@router.get("/all", response_class=Response, tags=["station"])
Expand Down
58 changes: 58 additions & 0 deletions code/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import requests
import json
from geopy.geocoders import Nominatim
from timezonefinder import TimezoneFinder
from sqlalchemy.orm import Session
from fastapi import HTTPException
from itertools import groupby
from models import City, Country, Location, Station
import logging
from schemas import StationDataCreate
Expand Down Expand Up @@ -163,3 +165,59 @@ def get_or_create_station(db: Session, station: StationDataCreate):
db.commit()

return db_station

def standard_output_to_csv(data) -> str:
"""
data: list[(
Station.device,
Measurement.time_measured,
Values.dimension,
Values.value
)]
ret: str -> csv: device,time_measured,dimension,value\n
"""

csv_data = "device,time_measured,dimension,value\n"
for device, time, dim, val in data:
csv_data += f"{device},{time.strftime("%Y-%m-%dT%H:%M")},{dim},{val}\n"
return csv_data

def standard_output_to_json(data):
"""
data: list[(
Station.device,
Measurement.time_measured,
Values.dimension,
Values.value
)]
ret: str -> json example:
[
{
"device": "75509",
"time_measured": "2024-11-29T08:18",
"values": [
{
"dimension": 7,
"value": 28.32
}
},
]
"""

groups = groupby(data, lambda x: (x[0], x[1]))
json_data = [
{
"device": device,
"time_measured": time.strftime("%Y-%m-%dT%H:%M"),
"values": [
{
"dimension": dim,
"value": val
}
for (_, _, dim, val) in data
]
}
for ((device, time), data) in groups
]

return json.dumps(json_data)