blob: a84a1524bee93b202a36729640a3d05ab341861c [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 )
Mitja Nikolausb61247b2018-08-31 10:53:28 +020091 def get(self, request, uuid):
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
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +020097
98 Returns: The update history of the requested device.
99
100 """
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200101 cursor = connection.cursor()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200102 raw_querys.execute_device_update_history_query(cursor, {"uuid": uuid})
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200103 res = dictfetchall(cursor)
104 return Response(res)
105
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200106
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200107_DEVICE_REPORT_HISTORY_SCHEMA = openapi.Schema(
108 type=openapi.TYPE_ARRAY,
109 items=openapi.Schema(
110 type=openapi.TYPE_OBJECT,
111 title="DeviceReportHistoryEntry",
112 properties=OrderedDict(
113 [
114 ("date", openapi.Schema(type=openapi.TYPE_STRING)),
115 ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)),
116 ("other", openapi.Schema(type=openapi.TYPE_INTEGER)),
117 ("prob_crashes", openapi.Schema(type=openapi.TYPE_INTEGER)),
118 ("smpl", openapi.Schema(type=openapi.TYPE_INTEGER)),
119 ]
120 ),
121 ),
122)
123
124
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200125class DeviceReportHistory(APIView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200126 """View the report history of a specific device."""
127
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200128 permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200129
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200130 @swagger_auto_schema(
131 operation_description="Get the report history of a device",
132 responses=dict(
133 [
134 default_desc(NotFound),
135 (
136 status.HTTP_200_OK,
137 openapi.Response(
138 _RESPONSE_STATUS_200_DESCRIPTION,
139 _DEVICE_REPORT_HISTORY_SCHEMA,
140 ),
141 ),
142 ]
143 ),
144 )
Mitja Nikolausb61247b2018-08-31 10:53:28 +0200145 def get(self, request, uuid):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200146 """Get the report history of a device.
147
148 Args:
149 request: Http request
150 uuid: The UUID of the device
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200151
152 Returns: The report history of the requested device.
153
154 """
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200155 cursor = connection.cursor()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200156 raw_querys.execute_device_report_history(cursor, {"uuid": uuid})
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200157 res = dictfetchall(cursor)
158 return Response(res)
159
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200160
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200161_STATUS_RESPONSE_SCHEMA = openapi.Schema(
162 title="Status",
163 type=openapi.TYPE_OBJECT,
164 properties=OrderedDict(
165 [
166 ("devices", openapi.Schema(type=openapi.TYPE_INTEGER)),
167 ("crashreports", openapi.Schema(type=openapi.TYPE_INTEGER)),
168 ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)),
169 ]
170 ),
171)
172
173
Dirk Vogt571168c2017-12-08 16:54:12 +0100174class Status(APIView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200175 """View the number of devices, crashreports and heartbeats."""
176
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200177 permission_classes = (HasStatsAccess,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200178
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200179 @swagger_auto_schema(
180 operation_description="Get the number of devices, crashreports and "
181 "heartbeats",
182 responses=dict(
183 [
184 (
185 status.HTTP_200_OK,
186 openapi.Response(
187 _RESPONSE_STATUS_200_DESCRIPTION,
188 _STATUS_RESPONSE_SCHEMA,
189 ),
190 )
191 ]
192 ),
193 )
Mitja Nikolausb61247b2018-08-31 10:53:28 +0200194 def get(self, request):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200195 """Get the number of devices, crashreports and heartbeats.
196
197 Args:
198 request: Http request
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200199
200 Returns: The number of devices, crashreports and heartbeats.
201
202 """
Dirk Vogt571168c2017-12-08 16:54:12 +0100203 num_devices = Device.objects.count()
204 num_crashreports = Crashreport.objects.count()
205 num_heartbeats = HeartBeat.objects.count()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200206 return Response(
207 {
208 "devices": num_devices,
209 "crashreports": num_crashreports,
210 "heartbeats": num_heartbeats,
211 }
212 )
Dirk Vogt571168c2017-12-08 16:54:12 +0100213
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200214
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200215_DEVICE_STAT_OVERVIEW_SCHEMA = openapi.Schema(
216 title="DeviceStatOverview",
217 type=openapi.TYPE_OBJECT,
218 properties=OrderedDict(
219 [
220 ("board_date", openapi.Schema(type=openapi.TYPE_STRING)),
221 ("crashes_per_day", openapi.Schema(type=openapi.TYPE_NUMBER)),
222 ("crashreports", openapi.Schema(type=openapi.TYPE_INTEGER)),
223 ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)),
224 ("last_active", openapi.Schema(type=openapi.TYPE_STRING)),
225 ("smpl_per_day", openapi.Schema(type=openapi.TYPE_NUMBER)),
226 ("smpls", openapi.Schema(type=openapi.TYPE_INTEGER)),
227 ("uuid", openapi.Schema(type=openapi.TYPE_STRING)),
228 ]
229 ),
230)
231
232
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200233class DeviceStat(APIView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200234 """View an overview of the statistics of a device."""
235
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200236 permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200237
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200238 @swagger_auto_schema(
239 operation_description="Get some general statistics for a device.",
240 responses=dict(
241 [
242 default_desc(NotFound),
243 (
244 status.HTTP_200_OK,
245 openapi.Response(
246 _RESPONSE_STATUS_200_DESCRIPTION,
247 _DEVICE_STAT_OVERVIEW_SCHEMA,
248 ),
249 ),
250 ]
251 ),
252 )
Mitja Nikolausb61247b2018-08-31 10:53:28 +0200253 def get(self, request, uuid):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200254 """Get some general statistics for a device.
255
256 Args:
257 request: Http request
258 uuid: The UUID of the device
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200259
260 Returns: Some general information of the device in a dictionary.
261
262 """
263 device = Device.objects.filter(uuid=uuid)
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200264 last_active = (
265 HeartBeat.objects.filter(device=device).order_by("-date")[0].date
266 )
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200267 heartbeats = HeartBeat.objects.filter(device=device).count()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200268 crashreports = (
269 Crashreport.objects.filter(device=device)
270 .filter(boot_reason__in=Crashreport.CRASH_BOOT_REASONS)
271 .count()
272 )
273 crashes_per_day = (
274 crashreports * 1.0 / heartbeats if heartbeats > 0 else 0
275 )
276 smpls = (
277 Crashreport.objects.filter(device=device)
278 .filter(boot_reason__in=Crashreport.SMPL_BOOT_REASONS)
279 .count()
280 )
281 smpl_per_day = smpls * 1.0 / heartbeats if heartbeats > 0 else 0
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200282 return Response(
283 {
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200284 "uuid": uuid,
285 "last_active": last_active,
286 "heartbeats": heartbeats,
287 "crashreports": crashreports,
288 "crashes_per_day": crashes_per_day,
289 "smpls": smpls,
290 "smpl_per_day": smpl_per_day,
291 "board_date": device[0].board_date,
292 }
293 )
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200294
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200295
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200296_LOG_FILE_SCHEMA = openapi.Schema(title="LogFile", type=openapi.TYPE_FILE)
297
298
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200299class LogFileDownload(APIView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200300 """View for downloading log files."""
301
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200302 permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200303
Mitja Nikolaus24f4d122018-08-24 16:32:58 +0200304 @swagger_auto_schema(
305 operation_description="Get a log file.",
306 responses=dict(
307 [
308 default_desc(NotFound),
309 (
310 status.HTTP_200_OK,
311 openapi.Response(
312 _RESPONSE_STATUS_200_DESCRIPTION, _LOG_FILE_SCHEMA
313 ),
314 ),
315 ]
316 ),
317 )
Mitja Nikolausb61247b2018-08-31 10:53:28 +0200318 def get(self, request, id_logfile):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200319 """Get a logfile.
320
321 Args:
322 request: Http request
323 id_logfile: The id of the log file
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200324
325 Returns: The log file with the corresponding id.
326
327 """
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200328 try:
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200329 logfile = LogFile.objects.get(id=id_logfile)
330 except ObjectDoesNotExist:
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200331 raise NotFound(detail="Logfile does not exist.")
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200332 zip_file = zipfile.ZipFile(logfile.logfile.path)
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200333 ret = {}
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200334 for file in zip_file.filelist:
335 file_open = zip_file.open(file)
336 ret[file.filename] = file_open.read()
Dirk Vogt62ff7f22017-05-04 16:07:21 +0200337 return Response(ret)
Dirk Vogt1accb672017-05-10 14:07:42 +0200338
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400339
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200340class _VersionStatsFilter(FilterSet):
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200341 first_seen_before = DateFilter(
342 field_name="first_seen_on", lookup_expr="lte"
343 )
344 first_seen_after = DateFilter(field_name="first_seen_on", lookup_expr="gte")
345 released_before = DateFilter(field_name="released_on", lookup_expr="lte")
346 released_after = DateFilter(field_name="released_on", lookup_expr="gte")
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200347
Dirk Vogt1accb672017-05-10 14:07:42 +0200348
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400349class _VersionStatsSerializer(serializers.ModelSerializer):
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200350 permission_classes = (HasStatsAccess,)
Dirk Vogt1accb672017-05-10 14:07:42 +0200351
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200352
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400353class _VersionStatsListView(generics.ListAPIView):
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200354 permission_classes = (HasStatsAccess,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200355 filter_backends = (DjangoFilterBackend,)
Dirk Vogt1accb672017-05-10 14:07:42 +0200356
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200357
358class _DailyVersionStatsFilter(FilterSet):
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200359 date_start = DateFilter(field_name="date", lookup_expr="gte")
360 date_end = DateFilter(field_name="date", lookup_expr="lte")
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200361
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400362
363class _DailyVersionStatsSerializer(serializers.ModelSerializer):
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200364 permission_classes = (HasStatsAccess,)
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400365
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200366
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400367class _DailyVersionStatsListView(generics.ListAPIView):
Borjan Tchakalofffa134bd2018-04-09 16:16:11 +0200368 permission_classes = (HasStatsAccess,)
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200369 filter_backends = (DjangoFilterBackend,)
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400370
371
372class VersionSerializer(_VersionStatsSerializer):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200373 """Serializer for the Version class."""
374
375 class Meta: # noqa: D106
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400376 model = Version
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200377 fields = "__all__"
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400378
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200379
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400380class VersionFilter(_VersionStatsFilter):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200381 """Filter for Version instances."""
382
383 class Meta: # noqa: D106
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400384 model = Version
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200385 fields = "__all__"
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400386
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200387
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400388class VersionListView(_VersionStatsListView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200389 """View for listing versions."""
390
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200391 queryset = Version.objects.all().order_by("-heartbeats")
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400392 filter_class = VersionFilter
393 serializer_class = VersionSerializer
394
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400395
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200396class VersionDailyFilter(_DailyVersionStatsFilter):
397 """Filter for VersionDaily instances."""
398
399 version__build_fingerprint = CharFilter()
400 version__is_official_release = BooleanFilter()
401 version__is_beta_release = BooleanFilter()
402
403 class Meta: # noqa: D106
Dirk Vogt1accb672017-05-10 14:07:42 +0200404 model = VersionDaily
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200405 fields = "__all__"
Dirk Vogt1accb672017-05-10 14:07:42 +0200406
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200407
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400408class VersionDailySerializer(_DailyVersionStatsSerializer):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200409 """Serializer for VersionDaily instances."""
410
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400411 build_fingerprint = serializers.CharField()
412
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200413 class Meta: # noqa: D106
Dirk Vogt1accb672017-05-10 14:07:42 +0200414 model = VersionDaily
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200415 fields = "__all__"
Dirk Vogt1accb672017-05-10 14:07:42 +0200416
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200417
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400418class VersionDailyListView(_DailyVersionStatsListView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200419 """View for listing VersionDaily instances."""
420
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400421 queryset = (
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200422 VersionDaily.objects.annotate(
423 build_fingerprint=F("version__build_fingerprint")
424 )
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200425 .all()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200426 .order_by("date")
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400427 )
428 filter_class = VersionDailyFilter
429 filter_fields = (
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200430 "version__build_fingerprint",
431 "version__is_official_release",
432 "version__is_beta_release",
Borjan Tchakaloff227b4312018-02-23 17:02:16 +0400433 )
Dirk Vogt1accb672017-05-10 14:07:42 +0200434 serializer_class = VersionDailySerializer
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400435
436
437class RadioVersionSerializer(_VersionStatsSerializer):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200438 """Serializer for RadioVersion instances."""
439
440 class Meta: # noqa: D106
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400441 model = RadioVersion
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200442 fields = "__all__"
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400443
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200444
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400445class RadioVersionFilter(_VersionStatsFilter):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200446 """Filter for RadioVersion instances."""
447
448 class Meta: # noqa: D106
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400449 model = RadioVersion
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200450 fields = "__all__"
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400451
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200452
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400453class RadioVersionListView(_VersionStatsListView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200454 """View for listing RadioVersion instances."""
455
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200456 queryset = RadioVersion.objects.all().order_by("-heartbeats")
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400457 serializer_class = RadioVersionSerializer
458 filter_class = RadioVersionFilter
459
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400460
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200461class RadioVersionDailyFilter(_DailyVersionStatsFilter):
462 """Filter for RadioVersionDaily instances."""
463
464 version__radio_version = CharFilter()
465 version__is_official_release = BooleanFilter()
466 version__is_beta_release = BooleanFilter()
467
468 class Meta: # noqa: D106
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400469 model = RadioVersionDaily
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200470 fields = "__all__"
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400471
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200472
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400473class RadioVersionDailySerializer(_DailyVersionStatsSerializer):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200474 """Serializer for RadioVersionDaily instances."""
475
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400476 radio_version = serializers.CharField()
477
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200478 class Meta: # noqa: D106
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400479 model = RadioVersionDaily
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200480 fields = "__all__"
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400481
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200482
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400483class RadioVersionDailyListView(_DailyVersionStatsListView):
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200484 """View for listing RadioVersionDaily instances."""
485
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400486 queryset = (
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200487 RadioVersionDaily.objects.annotate(
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200488 radio_version=F("version__radio_version")
489 )
Mitja Nikolaus5c3e0572018-07-30 13:36:14 +0200490 .all()
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200491 .order_by("date")
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400492 )
493 filter_class = RadioVersionDailyFilter
494 filter_fields = (
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200495 "version__radio_version",
496 "version__is_official_release",
497 "version__is_beta_release",
Borjan Tchakaloff1db45c72018-02-23 17:03:49 +0400498 )
499 serializer_class = RadioVersionDailySerializer