blob: fb75a16e7603aab0c159b959d2ef9df2e933b081 [file] [log] [blame]
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +02001"""REST API for accessing the crashreports statistics."""
2import zipfile
Mitja Nikolaus24f4d122018-08-24 16:32:58 +02003from collections import OrderedDict
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +02004
Mitja Nikolaus24f4d122018-08-24 16:32:58 +02005from drf_yasg import openapi
6from drf_yasg.utils import swagger_auto_schema
7from rest_framework import generics, status
Dirk Vogt62ff7f22017-05-04 16:07:21 +02008from rest_framework import serializers
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +02009from rest_framework.exceptions import NotFound
Dirk Vogt62ff7f22017-05-04 16:07:21 +020010from rest_framework.response import Response
11from rest_framework.views import APIView
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020012
13from django.core.exceptions import ObjectDoesNotExist
Dirk Vogt62ff7f22017-05-04 16:07:21 +020014from django.db import connection
Dirk Vogt1accb672017-05-10 14:07:42 +020015from django.db.models.expressions import F
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020016
17from django_filters.rest_framework import (
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020018 DjangoFilterBackend,
19 DateFilter,
20 FilterSet,
21 CharFilter,
22 BooleanFilter,
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020023)
24
25from crashreport_stats.models import (
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020026 Version,
27 VersionDaily,
28 RadioVersion,
29 RadioVersionDaily,
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020030)
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020031from crashreports.models import Device, Crashreport, HeartBeat, LogFile
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020032from crashreports.permissions import (
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020033 HasRightsOrIsDeviceOwnerDeviceCreation,
34 HasStatsAccess,
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020035)
Mitja Nikolaus24f4d122018-08-24 16:32:58 +020036from crashreports.response_descriptions import default_desc
37
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020038from . import raw_querys
39
Mitja Nikolaus24f4d122018-08-24 16:32:58 +020040_RESPONSE_STATUS_200_DESCRIPTION = "OK"
41
Dirk Vogt62ff7f22017-05-04 16:07:21 +020042
43def dictfetchall(cursor):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020044 """Return all rows from a cursor as a dict."""
Dirk Vogt62ff7f22017-05-04 16:07:21 +020045 desc = cursor.description
46 return [
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020047 dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall()
Dirk Vogt62ff7f22017-05-04 16:07:21 +020048 ]
49
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020050
Mitja Nikolaus24f4d122018-08-24 16:32:58 +020051_DEVICE_UPDATE_HISTORY_SCHEMA = openapi.Schema(
52 type=openapi.TYPE_ARRAY,
53 items=openapi.Schema(
54 type=openapi.TYPE_OBJECT,
55 title="DeviceUpdateHistoryEntry",
56 properties=OrderedDict(
57 [
58 ("build_fingerprint", openapi.Schema(type=openapi.TYPE_STRING)),
59 ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)),
60 ("max", openapi.Schema(type=openapi.TYPE_INTEGER)),
61 ("other", openapi.Schema(type=openapi.TYPE_INTEGER)),
62 ("prob_crashes", openapi.Schema(type=openapi.TYPE_INTEGER)),
63 ("smpl", openapi.Schema(type=openapi.TYPE_INTEGER)),
64 ("update_date", openapi.Schema(type=openapi.TYPE_STRING)),
65 ]
66 ),
67 ),
68)
69
70
Dirk Vogt62ff7f22017-05-04 16:07:21 +020071class DeviceUpdateHistory(APIView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020072 """View the update history of a specific device."""
73
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020074 permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020075
Mitja Nikolaus24f4d122018-08-24 16:32:58 +020076 @swagger_auto_schema(
77 operation_description="Get the update history of a device",
78 responses=dict(
79 [
80 default_desc(NotFound),
81 (
82 status.HTTP_200_OK,
83 openapi.Response(
84 _RESPONSE_STATUS_200_DESCRIPTION,
85 _DEVICE_UPDATE_HISTORY_SCHEMA,
86 ),
87 ),
88 ]
89 ),
90 )
Dirk Vogt62ff7f22017-05-04 16:07:21 +020091 def get(self, request, uuid, format=None):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020092 """Get the update history of a device.
93
94 Args:
95 request: Http request
96 uuid: The UUID of the device
97 format: Optional response format parameter
98
99 Returns: The update history of the requested device.
100
101 """
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200102 cursor = connection.cursor()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200103 raw_querys.execute_device_update_history_query(cursor, {"uuid": uuid})
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200104 res = dictfetchall(cursor)
105 return Response(res)
106
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200107
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200108_DEVICE_REPORT_HISTORY_SCHEMA = openapi.Schema(
109 type=openapi.TYPE_ARRAY,
110 items=openapi.Schema(
111 type=openapi.TYPE_OBJECT,
112 title="DeviceReportHistoryEntry",
113 properties=OrderedDict(
114 [
115 ("date", openapi.Schema(type=openapi.TYPE_STRING)),
116 ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)),
117 ("other", openapi.Schema(type=openapi.TYPE_INTEGER)),
118 ("prob_crashes", openapi.Schema(type=openapi.TYPE_INTEGER)),
119 ("smpl", openapi.Schema(type=openapi.TYPE_INTEGER)),
120 ]
121 ),
122 ),
123)
124
125
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200126class DeviceReportHistory(APIView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200127 """View the report history of a specific device."""
128
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200129 permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200130
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200131 @swagger_auto_schema(
132 operation_description="Get the report history of a device",
133 responses=dict(
134 [
135 default_desc(NotFound),
136 (
137 status.HTTP_200_OK,
138 openapi.Response(
139 _RESPONSE_STATUS_200_DESCRIPTION,
140 _DEVICE_REPORT_HISTORY_SCHEMA,
141 ),
142 ),
143 ]
144 ),
145 )
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200146 def get(self, request, uuid, format=None):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200147 """Get the report history of a device.
148
149 Args:
150 request: Http request
151 uuid: The UUID of the device
152 format: Optional response format parameter
153
154 Returns: The report history of the requested device.
155
156 """
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200157 cursor = connection.cursor()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200158 raw_querys.execute_device_report_history(cursor, {"uuid": uuid})
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200159 res = dictfetchall(cursor)
160 return Response(res)
161
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200162
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200163_STATUS_RESPONSE_SCHEMA = openapi.Schema(
164 title="Status",
165 type=openapi.TYPE_OBJECT,
166 properties=OrderedDict(
167 [
168 ("devices", openapi.Schema(type=openapi.TYPE_INTEGER)),
169 ("crashreports", openapi.Schema(type=openapi.TYPE_INTEGER)),
170 ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)),
171 ]
172 ),
173)
174
175
Dirk Vogt571168c2017-12-08 16:54:12 +0100176class Status(APIView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200177 """View the number of devices, crashreports and heartbeats."""
178
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200179 permission_classes = (HasStatsAccess,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200180
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200181 @swagger_auto_schema(
182 operation_description="Get the number of devices, crashreports and "
183 "heartbeats",
184 responses=dict(
185 [
186 (
187 status.HTTP_200_OK,
188 openapi.Response(
189 _RESPONSE_STATUS_200_DESCRIPTION,
190 _STATUS_RESPONSE_SCHEMA,
191 ),
192 )
193 ]
194 ),
195 )
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200196 def get(self, request, format=None):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200197 """Get the number of devices, crashreports and heartbeats.
198
199 Args:
200 request: Http request
201 format: Optional response format parameter
202
203 Returns: The number of devices, crashreports and heartbeats.
204
205 """
Dirk Vogt571168c2017-12-08 16:54:12 +0100206 num_devices = Device.objects.count()
207 num_crashreports = Crashreport.objects.count()
208 num_heartbeats = HeartBeat.objects.count()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200209 return Response(
210 {
211 "devices": num_devices,
212 "crashreports": num_crashreports,
213 "heartbeats": num_heartbeats,
214 }
215 )
Dirk Vogt571168c2017-12-08 16:54:12 +0100216
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200217
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200218_DEVICE_STAT_OVERVIEW_SCHEMA = openapi.Schema(
219 title="DeviceStatOverview",
220 type=openapi.TYPE_OBJECT,
221 properties=OrderedDict(
222 [
223 ("board_date", openapi.Schema(type=openapi.TYPE_STRING)),
224 ("crashes_per_day", openapi.Schema(type=openapi.TYPE_NUMBER)),
225 ("crashreports", openapi.Schema(type=openapi.TYPE_INTEGER)),
226 ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)),
227 ("last_active", openapi.Schema(type=openapi.TYPE_STRING)),
228 ("smpl_per_day", openapi.Schema(type=openapi.TYPE_NUMBER)),
229 ("smpls", openapi.Schema(type=openapi.TYPE_INTEGER)),
230 ("uuid", openapi.Schema(type=openapi.TYPE_STRING)),
231 ]
232 ),
233)
234
235
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200236class DeviceStat(APIView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200237 """View an overview of the statistics of a device."""
238
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200239 permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200240
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200241 @swagger_auto_schema(
242 operation_description="Get some general statistics for a device.",
243 responses=dict(
244 [
245 default_desc(NotFound),
246 (
247 status.HTTP_200_OK,
248 openapi.Response(
249 _RESPONSE_STATUS_200_DESCRIPTION,
250 _DEVICE_STAT_OVERVIEW_SCHEMA,
251 ),
252 ),
253 ]
254 ),
255 )
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200256 def get(self, request, uuid, format=None):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200257 """Get some general statistics for a device.
258
259 Args:
260 request: Http request
261 uuid: The UUID of the device
262 format: Optional response format parameter
263
264 Returns: Some general information of the device in a dictionary.
265
266 """
267 device = Device.objects.filter(uuid=uuid)
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200268 last_active = (
269 HeartBeat.objects.filter(device=device).order_by("-date")[0].date
270 )
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200271 heartbeats = HeartBeat.objects.filter(device=device).count()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200272 crashreports = (
273 Crashreport.objects.filter(device=device)
274 .filter(boot_reason__in=Crashreport.CRASH_BOOT_REASONS)
275 .count()
276 )
277 crashes_per_day = (
278 crashreports * 1.0 / heartbeats if heartbeats > 0 else 0
279 )
280 smpls = (
281 Crashreport.objects.filter(device=device)
282 .filter(boot_reason__in=Crashreport.SMPL_BOOT_REASONS)
283 .count()
284 )
285 smpl_per_day = smpls * 1.0 / heartbeats if heartbeats > 0 else 0
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200286 return Response(
287 {
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200288 "uuid": uuid,
289 "last_active": last_active,
290 "heartbeats": heartbeats,
291 "crashreports": crashreports,
292 "crashes_per_day": crashes_per_day,
293 "smpls": smpls,
294 "smpl_per_day": smpl_per_day,
295 "board_date": device[0].board_date,
296 }
297 )
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200298
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200299
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200300_LOG_FILE_SCHEMA = openapi.Schema(title="LogFile", type=openapi.TYPE_FILE)
301
302
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200303class LogFileDownload(APIView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200304 """View for downloading log files."""
305
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200306 permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200307
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200308 @swagger_auto_schema(
309 operation_description="Get a log file.",
310 responses=dict(
311 [
312 default_desc(NotFound),
313 (
314 status.HTTP_200_OK,
315 openapi.Response(
316 _RESPONSE_STATUS_200_DESCRIPTION, _LOG_FILE_SCHEMA
317 ),
318 ),
319 ]
320 ),
321 )
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200322 def get(self, request, id_logfile, format=None):
323 """Get a logfile.
324
325 Args:
326 request: Http request
327 id_logfile: The id of the log file
328 format: Optional response format parameter
329
330 Returns: The log file with the corresponding id.
331
332 """
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200333 try:
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200334 logfile = LogFile.objects.get(id=id_logfile)
335 except ObjectDoesNotExist:
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200336 raise NotFound(detail="Logfile does not exist.")
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200337 zip_file = zipfile.ZipFile(logfile.logfile.path)
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200338 ret = {}
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200339 for file in zip_file.filelist:
340 file_open = zip_file.open(file)
341 ret[file.filename] = file_open.read()
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200342 return Response(ret)
Dirk Vogt1accb672017-05-10 14:07:42 +0200343
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400344
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200345class _VersionStatsFilter(FilterSet):
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200346 first_seen_before = DateFilter(
347 field_name="first_seen_on", lookup_expr="lte"
348 )
349 first_seen_after = DateFilter(field_name="first_seen_on", lookup_expr="gte")
350 released_before = DateFilter(field_name="released_on", lookup_expr="lte")
351 released_after = DateFilter(field_name="released_on", lookup_expr="gte")
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200352
Dirk Vogt1accb672017-05-10 14:07:42 +0200353
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400354class _VersionStatsSerializer(serializers.ModelSerializer):
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200355 permission_classes = (HasStatsAccess,)
Dirk Vogt1accb672017-05-10 14:07:42 +0200356
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200357
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400358class _VersionStatsListView(generics.ListAPIView):
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200359 permission_classes = (HasStatsAccess,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200360 filter_backends = (DjangoFilterBackend,)
Dirk Vogt1accb672017-05-10 14:07:42 +0200361
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200362
363class _DailyVersionStatsFilter(FilterSet):
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200364 date_start = DateFilter(field_name="date", lookup_expr="gte")
365 date_end = DateFilter(field_name="date", lookup_expr="lte")
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200366
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400367
368class _DailyVersionStatsSerializer(serializers.ModelSerializer):
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200369 permission_classes = (HasStatsAccess,)
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400370
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200371
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400372class _DailyVersionStatsListView(generics.ListAPIView):
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200373 permission_classes = (HasStatsAccess,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200374 filter_backends = (DjangoFilterBackend,)
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400375
376
377class VersionSerializer(_VersionStatsSerializer):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200378 """Serializer for the Version class."""
379
380 class Meta: # noqa: D106
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400381 model = Version
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200382 fields = "__all__"
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400383
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200384
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400385class VersionFilter(_VersionStatsFilter):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200386 """Filter for Version instances."""
387
388 class Meta: # noqa: D106
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400389 model = Version
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200390 fields = "__all__"
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400391
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200392
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400393class VersionListView(_VersionStatsListView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200394 """View for listing versions."""
395
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200396 queryset = Version.objects.all().order_by("-heartbeats")
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400397 filter_class = VersionFilter
398 serializer_class = VersionSerializer
399
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400400
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200401class VersionDailyFilter(_DailyVersionStatsFilter):
402 """Filter for VersionDaily instances."""
403
404 version__build_fingerprint = CharFilter()
405 version__is_official_release = BooleanFilter()
406 version__is_beta_release = BooleanFilter()
407
408 class Meta: # noqa: D106
Dirk Vogt1accb672017-05-10 14:07:42 +0200409 model = VersionDaily
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200410 fields = "__all__"
Dirk Vogt1accb672017-05-10 14:07:42 +0200411
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200412
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400413class VersionDailySerializer(_DailyVersionStatsSerializer):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200414 """Serializer for VersionDaily instances."""
415
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400416 build_fingerprint = serializers.CharField()
417
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200418 class Meta: # noqa: D106
Dirk Vogt1accb672017-05-10 14:07:42 +0200419 model = VersionDaily
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200420 fields = "__all__"
Dirk Vogt1accb672017-05-10 14:07:42 +0200421
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200422
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400423class VersionDailyListView(_DailyVersionStatsListView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200424 """View for listing VersionDaily instances."""
425
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400426 queryset = (
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200427 VersionDaily.objects.annotate(
428 build_fingerprint=F("version__build_fingerprint")
429 )
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200430 .all()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200431 .order_by("date")
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400432 )
433 filter_class = VersionDailyFilter
434 filter_fields = (
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200435 "version__build_fingerprint",
436 "version__is_official_release",
437 "version__is_beta_release",
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400438 )
Dirk Vogt1accb672017-05-10 14:07:42 +0200439 serializer_class = VersionDailySerializer
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400440
441
442class RadioVersionSerializer(_VersionStatsSerializer):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200443 """Serializer for RadioVersion instances."""
444
445 class Meta: # noqa: D106
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400446 model = RadioVersion
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200447 fields = "__all__"
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400448
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200449
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400450class RadioVersionFilter(_VersionStatsFilter):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200451 """Filter for RadioVersion instances."""
452
453 class Meta: # noqa: D106
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400454 model = RadioVersion
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200455 fields = "__all__"
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400456
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200457
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400458class RadioVersionListView(_VersionStatsListView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200459 """View for listing RadioVersion instances."""
460
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200461 queryset = RadioVersion.objects.all().order_by("-heartbeats")
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400462 serializer_class = RadioVersionSerializer
463 filter_class = RadioVersionFilter
464
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400465
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200466class RadioVersionDailyFilter(_DailyVersionStatsFilter):
467 """Filter for RadioVersionDaily instances."""
468
469 version__radio_version = CharFilter()
470 version__is_official_release = BooleanFilter()
471 version__is_beta_release = BooleanFilter()
472
473 class Meta: # noqa: D106
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400474 model = RadioVersionDaily
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200475 fields = "__all__"
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400476
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200477
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400478class RadioVersionDailySerializer(_DailyVersionStatsSerializer):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200479 """Serializer for RadioVersionDaily instances."""
480
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400481 radio_version = serializers.CharField()
482
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200483 class Meta: # noqa: D106
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400484 model = RadioVersionDaily
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200485 fields = "__all__"
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400486
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200487
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400488class RadioVersionDailyListView(_DailyVersionStatsListView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200489 """View for listing RadioVersionDaily instances."""
490
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400491 queryset = (
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200492 RadioVersionDaily.objects.annotate(
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200493 radio_version=F("version__radio_version")
494 )
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200495 .all()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200496 .order_by("date")
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400497 )
498 filter_class = RadioVersionDailyFilter
499 filter_fields = (
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200500 "version__radio_version",
501 "version__is_official_release",
502 "version__is_beta_release",
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400503 )
504 serializer_class = RadioVersionDailySerializer