88from django .utils import timezone
99from dateutil .relativedelta import relativedelta
1010from django .db .models import ExpressionWrapper , F , FloatField , Max , Min , Sum , Avg , Q
11- from django .db .models .functions import Cast , TruncDate
11+ from django .db .models .functions import Cast , TruncHour , TruncDay , TruncMonth
1212from rest_framework import mixins , pagination , viewsets
1313
1414from ..models import SensorDataStat , LastActiveNodes , City , Node
@@ -60,6 +60,7 @@ def get_paginated_response(self, data_stats):
6060 # If filtering from a date
6161 # We will need to have a list of the value_types e.g. { 'P1': [{}, {}] }
6262 from_date = self .request .query_params .get ("from" , None )
63+ interval = self .request .query_params .get ("interval" , None )
6364
6465 results = {}
6566 for data_stat in data_stats :
@@ -69,19 +70,19 @@ def get_paginated_response(self, data_stats):
6970 if city_slug not in results :
7071 results [city_slug ] = {
7172 "city_slug" : city_slug ,
72- value_type : [] if from_date else {},
73+ value_type : [] if from_date or interval else {},
7374 }
7475
7576 if value_type not in results [city_slug ]:
76- results [city_slug ][value_type ] = [] if from_date else {}
77+ results [city_slug ][value_type ] = [] if from_date or interval else {}
7778
7879 values = results [city_slug ][value_type ]
79- include_result = getattr (values , "append" if from_date else "update" )
80+ include_result = getattr (values , "append" if from_date or interval else "update" )
8081 include_result (
8182 {
82- "average" : data_stat ["average " ],
83- "minimum" : data_stat ["minimum " ],
84- "maximum" : data_stat ["maximum " ],
83+ "average" : data_stat ["calculated_average " ],
84+ "minimum" : data_stat ["calculated_minimum " ],
85+ "maximum" : data_stat ["calculated_maximum " ],
8586 "start_datetime" : data_stat ["start_datetime" ],
8687 "end_datetime" : data_stat ["end_datetime" ],
8788 }
@@ -112,6 +113,7 @@ def get_queryset(self):
112113 city_slugs = self .request .query_params .get ("city" , None )
113114 from_date = self .request .query_params .get ("from" , None )
114115 to_date = self .request .query_params .get ("to" , None )
116+ interval = self .request .query_params .get ("interval" , None )
115117
116118 if to_date and not from_date :
117119 raise ValidationError ({"from" : "Must be provide along with to query" })
@@ -129,43 +131,10 @@ def get_queryset(self):
129131 )
130132
131133 if not from_date and not to_date :
132- return self ._retrieve_past_24hrs (city_slugs , filter_value_types )
133-
134- return self ._retrieve_range (from_date , to_date , city_slugs , filter_value_types )
135-
136- @staticmethod
137- def _retrieve_past_24hrs (city_slugs , filter_value_types ):
138- to_date = timezone .now ().replace (minute = 0 , second = 0 , microsecond = 0 )
139- from_date = to_date - datetime .timedelta (hours = 24 )
140-
141- queryset = SensorDataStat .objects .filter (
142- value_type__in = filter_value_types ,
143- timestamp__gte = from_date ,
144- timestamp__lte = to_date ,
145- )
146-
147- if city_slugs :
148- queryset = queryset .filter (city_slug__in = city_slugs .split ("," ))
149-
150- return (
151- queryset .order_by ()
152- .values ("value_type" , "city_slug" )
153- .annotate (
154- start_datetime = Min ("timestamp" ),
155- end_datetime = Max ("timestamp" ),
156- average = ExpressionWrapper (
157- Sum (F ("average" ) * F ("sample_size" )) / Sum ("sample_size" ),
158- output_field = FloatField (),
159- ),
160- minimum = Min ("minimum" ),
161- maximum = Max ("maximum" ),
162- )
163- .order_by ("city_slug" )
164- )
165-
166- @staticmethod
167- def _retrieve_range (from_date , to_date , city_slugs , filter_value_types ):
168- if not to_date :
134+ to_date = timezone .now ().replace (minute = 0 , second = 0 , microsecond = 0 )
135+ from_date = to_date - datetime .timedelta (hours = 24 )
136+ interval = 'day' if not interval else interval
137+ elif not to_date :
169138 from_date = beginning_of_day (from_date )
170139 # Get data from_date until the end
171140 # of day yesterday which is the beginning of today
@@ -177,27 +146,47 @@ def _retrieve_range(from_date, to_date, city_slugs, filter_value_types):
177146 queryset = SensorDataStat .objects .filter (
178147 value_type__in = filter_value_types ,
179148 timestamp__gte = from_date ,
180- timestamp__lt = to_date ,
149+ timestamp__lte = to_date ,
181150 )
182151
152+ if interval == 'month' :
153+ truncate = TruncMonth ("timestamp" )
154+ elif interval == 'day' :
155+ truncate = TruncDay ("timestamp" )
156+ else :
157+ truncate = TruncHour ("timestamp" )
158+
183159 if city_slugs :
184160 queryset = queryset .filter (city_slug__in = city_slugs .split ("," ))
185161
186162 return (
187- queryset .annotate (date = TruncDate ("timestamp" ))
188- .values ("date" , "value_type" )
163+ queryset
164+ .values (
165+ "value_type" ,
166+ "city_slug"
167+ )
189168 .annotate (
190- city_slug = F ( "city_slug" ) ,
169+ truncated_timestamp = truncate ,
191170 start_datetime = Min ("timestamp" ),
192171 end_datetime = Max ("timestamp" ),
193- average = ExpressionWrapper (
172+ calculated_average = ExpressionWrapper (
194173 Sum (F ("average" ) * F ("sample_size" )) / Sum ("sample_size" ),
195174 output_field = FloatField (),
196175 ),
197- minimum = Min ("minimum" ),
198- maximum = Max ("maximum" ),
176+ calculated_minimum = Min ("minimum" ),
177+ calculated_maximum = Max ("maximum" ),
178+ )
179+ .values (
180+ "value_type" ,
181+ "city_slug" ,
182+ "truncated_timestamp" ,
183+ "start_datetime" ,
184+ "end_datetime" ,
185+ "calculated_average" ,
186+ "calculated_minimum" ,
187+ "calculated_maximum"
199188 )
200- .order_by ("-date " )
189+ .order_by ("city_slug" , "-truncated_timestamp " )
201190 )
202191
203192
0 commit comments