added graph endpoint

This commit is contained in:
stepan323446 2025-12-12 12:56:26 +01:00
parent 9783501744
commit 6f13e5bf3b
3 changed files with 115 additions and 6 deletions

View File

@ -1,4 +1,5 @@
from rest_framework import serializers
from django.db import models
from .models import WeatherStats
class WeatherStatSerializer(serializers.ModelSerializer):
@ -8,13 +9,32 @@ class WeatherStatSerializer(serializers.ModelSerializer):
model = WeatherStats
fields = ('humidity_air', 'humidity_ground', 'temperature', 'light', 'created_at')
class OpenWeatherAPISerializer(serializers.Serializer):
date = serializers.DateField()
temp_night = serializers.FloatField()
temp_day = serializers.FloatField()
humidity = serializers.FloatField()
wind_speed = serializers.FloatField()
precip_probability = serializers.FloatField()
precip_probability = serializers.FloatField()
class WeatherPeriods(models.TextChoices):
min_10 = 'min_10', '10 Minutes'
min_30 = 'min_30', '30 Minutes'
hour = 'hour', '1 Hour'
hour_6 = 'hour_6', '6 Hours'
hour_12 = 'hour_12', '12 Hours'
hour_24 = 'hour_24', '24 Hours'
@classmethod
def only_values(cls):
return [value for value, label in cls.choices]
class WeatherByPeriodRequestSerializer(serializers.Serializer):
period = serializers.ChoiceField(WeatherPeriods.choices, required=True)
class WeatherByPeriodSerializer(serializers.Serializer):
date = serializers.DateTimeField(required=True)
humidity_air = serializers.FloatField(required=True)
humidity_ground = serializers.FloatField(required=True)
temperature = serializers.FloatField(required=True)
light = serializers.FloatField(required=True)

View File

@ -5,5 +5,6 @@ urlpatterns = [
path('create/', CreateStatAPI.as_view()),
path('last/', LastStatAPI.as_view()),
path('history/', StatsHistoryAPI.as_view()),
path('open-meteo/', OpenMeteoWeatherAPI.as_view()),
path('open-meteo/', OpenMeteoWeatherAPI.as_view()),
path('stats-by-period/', StatsByPeriodAPI.as_view())
]

View File

@ -1,6 +1,10 @@
import time
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.utils import timezone
from django.db.models import Avg
from django.db.models.functions import TruncMinute, TruncHour
from datetime import timedelta
from django.core.cache import cache
from rest_framework.generics import CreateAPIView, ListAPIView
from rest_framework.views import APIView
@ -11,7 +15,7 @@ from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiRespon
from project.serializers import MessageSerializer
from .models import WeatherStats
from .serializers import WeatherStatSerializer, OpenWeatherAPISerializer
from .serializers import *
from .utils import IsMikrokontroller, PageNumberPagination, get_weather_api_data
CACHE_SAVE_CONTROL = 'stats_latest_saved_control'
@ -76,6 +80,90 @@ class StatsHistoryAPI(ListAPIView):
queryset = WeatherStats.objects.order_by('-created_at')
pagination_class = PageNumberPagination
class StatsByPeriodAPI(APIView):
@extend_schema(tags=['Weather'],
description="Weather data for graph by period",
parameters=[
OpenApiParameter(
name='period',
type=str,
location=OpenApiParameter.QUERY,
description='Period',
required=True,
enum=WeatherPeriods.only_values()
)
],
responses={
200: WeatherByPeriodSerializer(many=True),
400: MessageSerializer
}
)
def get(self, request: Request, format=None):
serializer = WeatherByPeriodRequestSerializer(data=request.query_params)
serializer.is_valid(raise_exception=True)
period = serializer.validated_data['period']
now = timezone.now()
if period == WeatherPeriods.min_10:
start_time = now - timedelta(minutes=10)
trunc_func = TruncMinute
step = 1
elif period == WeatherPeriods.min_30:
start_time = now - timedelta(minutes=30)
trunc_func = TruncMinute
step = 1
elif period == WeatherPeriods.hour:
start_time = now - timedelta(hours=1)
trunc_func = TruncMinute
step = 10
elif period == WeatherPeriods.hour_6:
start_time = now - timedelta(hours=6)
trunc_func = TruncHour
elif period == WeatherPeriods.hour_12:
start_time = now - timedelta(hours=12)
trunc_func = TruncHour
elif period == WeatherPeriods.hour_24:
start_time = now - timedelta(hours=24)
trunc_func = TruncHour
qs = (
WeatherStats.objects
.filter(created_at__gte=start_time)
.annotate(time_slot=trunc_func('created_at'))
.values('time_slot')
.annotate(
humidity_air=Avg('humidity_air'),
humidity_ground=Avg('humidity_ground'),
temperature=Avg('temperature'),
light=Avg('light')
)
.order_by('-time_slot')
)
data_dict = {entry['date']: entry for entry in qs}
slots = []
current = start_time
while current <= now:
if trunc_func == TruncMinute:
slot = current.replace(second=0, microsecond=0, minute=(current.minute // step) * step)
else:
slot = current.replace(minute=0, second=0, microsecond=0)
entry = data_dict.get(slot, {
"date": slot,
"humidity_air": 0,
"humidity_ground": 0,
"temperature": 0,
"light": 0
})
slots.append(entry)
current += timedelta(minutes=step if trunc_func == TruncMinute else 60)
serializer = WeatherByPeriodSerializer(slots, many=True)
return Response(serializer.data)
@method_decorator(cache_page(CACHE_TIMEOUT_WEATHER), name='dispatch')