added graph endpoint
This commit is contained in:
parent
9783501744
commit
6f13e5bf3b
@ -1,4 +1,5 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from django.db import models
|
||||||
from .models import WeatherStats
|
from .models import WeatherStats
|
||||||
|
|
||||||
class WeatherStatSerializer(serializers.ModelSerializer):
|
class WeatherStatSerializer(serializers.ModelSerializer):
|
||||||
@ -8,13 +9,32 @@ class WeatherStatSerializer(serializers.ModelSerializer):
|
|||||||
model = WeatherStats
|
model = WeatherStats
|
||||||
fields = ('humidity_air', 'humidity_ground', 'temperature', 'light', 'created_at')
|
fields = ('humidity_air', 'humidity_ground', 'temperature', 'light', 'created_at')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class OpenWeatherAPISerializer(serializers.Serializer):
|
class OpenWeatherAPISerializer(serializers.Serializer):
|
||||||
date = serializers.DateField()
|
date = serializers.DateField()
|
||||||
temp_night = serializers.FloatField()
|
temp_night = serializers.FloatField()
|
||||||
temp_day = serializers.FloatField()
|
temp_day = serializers.FloatField()
|
||||||
humidity = serializers.FloatField()
|
humidity = serializers.FloatField()
|
||||||
wind_speed = 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)
|
||||||
@ -5,5 +5,6 @@ urlpatterns = [
|
|||||||
path('create/', CreateStatAPI.as_view()),
|
path('create/', CreateStatAPI.as_view()),
|
||||||
path('last/', LastStatAPI.as_view()),
|
path('last/', LastStatAPI.as_view()),
|
||||||
path('history/', StatsHistoryAPI.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())
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import time
|
import time
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.cache import cache_page
|
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 django.core.cache import cache
|
||||||
from rest_framework.generics import CreateAPIView, ListAPIView
|
from rest_framework.generics import CreateAPIView, ListAPIView
|
||||||
from rest_framework.views import APIView
|
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 project.serializers import MessageSerializer
|
||||||
from .models import WeatherStats
|
from .models import WeatherStats
|
||||||
from .serializers import WeatherStatSerializer, OpenWeatherAPISerializer
|
from .serializers import *
|
||||||
from .utils import IsMikrokontroller, PageNumberPagination, get_weather_api_data
|
from .utils import IsMikrokontroller, PageNumberPagination, get_weather_api_data
|
||||||
|
|
||||||
CACHE_SAVE_CONTROL = 'stats_latest_saved_control'
|
CACHE_SAVE_CONTROL = 'stats_latest_saved_control'
|
||||||
@ -76,6 +80,90 @@ class StatsHistoryAPI(ListAPIView):
|
|||||||
queryset = WeatherStats.objects.order_by('-created_at')
|
queryset = WeatherStats.objects.order_by('-created_at')
|
||||||
pagination_class = PageNumberPagination
|
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')
|
@method_decorator(cache_page(CACHE_TIMEOUT_WEATHER), name='dispatch')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user