Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 1 | """REST API for accessing the crashreports statistics.""" |
Mitja Nikolaus | 21f3b1b | 2018-11-02 17:10:02 +0100 | [diff] [blame] | 2 | import operator |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 3 | import zipfile |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 4 | from collections import OrderedDict |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 5 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 6 | from drf_yasg import openapi |
| 7 | from drf_yasg.utils import swagger_auto_schema |
| 8 | from rest_framework import generics, status |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 9 | from rest_framework import serializers |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 10 | from rest_framework.exceptions import NotFound |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 11 | from rest_framework.response import Response |
| 12 | from rest_framework.views import APIView |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 13 | |
| 14 | from django.core.exceptions import ObjectDoesNotExist |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 15 | from django.db.models.expressions import F |
Mitja Nikolaus | 4d759da | 2018-08-28 15:31:29 +0200 | [diff] [blame] | 16 | from django.utils.decorators import method_decorator |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 17 | |
| 18 | from django_filters.rest_framework import ( |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 19 | DjangoFilterBackend, |
| 20 | DateFilter, |
| 21 | FilterSet, |
| 22 | CharFilter, |
| 23 | BooleanFilter, |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 24 | ) |
| 25 | |
| 26 | from crashreport_stats.models import ( |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 27 | Version, |
| 28 | VersionDaily, |
| 29 | RadioVersion, |
| 30 | RadioVersionDaily, |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 31 | ) |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 32 | from crashreports.models import Device, Crashreport, HeartBeat, LogFile |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 33 | from crashreports.permissions import ( |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 34 | HasStatsAccess, |
Mitja Nikolaus | 4d759da | 2018-08-28 15:31:29 +0200 | [diff] [blame] | 35 | SWAGGER_SECURITY_REQUIREMENTS_ALL, |
| 36 | SWAGGER_SECURITY_REQUIREMENTS_OAUTH, |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 37 | ) |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 38 | from crashreports.response_descriptions import default_desc |
| 39 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 40 | _RESPONSE_STATUS_200_DESCRIPTION = "OK" |
| 41 | |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 42 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 43 | _DEVICE_UPDATE_HISTORY_SCHEMA = openapi.Schema( |
| 44 | type=openapi.TYPE_ARRAY, |
| 45 | items=openapi.Schema( |
| 46 | type=openapi.TYPE_OBJECT, |
| 47 | title="DeviceUpdateHistoryEntry", |
| 48 | properties=OrderedDict( |
| 49 | [ |
| 50 | ("build_fingerprint", openapi.Schema(type=openapi.TYPE_STRING)), |
| 51 | ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 52 | ("max", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 53 | ("other", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 54 | ("prob_crashes", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 55 | ("smpl", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 56 | ("update_date", openapi.Schema(type=openapi.TYPE_STRING)), |
| 57 | ] |
| 58 | ), |
| 59 | ), |
| 60 | ) |
| 61 | |
| 62 | |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 63 | class DeviceUpdateHistory(APIView): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 64 | """View the update history of a specific device.""" |
| 65 | |
Mitja Nikolaus | 048e20a | 2018-11-12 19:07:30 +0100 | [diff] [blame] | 66 | permission_classes = (HasStatsAccess,) |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 67 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 68 | @swagger_auto_schema( |
| 69 | operation_description="Get the update history of a device", |
Mitja Nikolaus | 4d759da | 2018-08-28 15:31:29 +0200 | [diff] [blame] | 70 | security=SWAGGER_SECURITY_REQUIREMENTS_ALL, |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 71 | responses=dict( |
| 72 | [ |
| 73 | default_desc(NotFound), |
| 74 | ( |
| 75 | status.HTTP_200_OK, |
| 76 | openapi.Response( |
| 77 | _RESPONSE_STATUS_200_DESCRIPTION, |
| 78 | _DEVICE_UPDATE_HISTORY_SCHEMA, |
| 79 | ), |
| 80 | ), |
| 81 | ] |
| 82 | ), |
| 83 | ) |
Mitja Nikolaus | b61247b | 2018-08-31 10:53:28 +0200 | [diff] [blame] | 84 | def get(self, request, uuid): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 85 | """Get the update history of a device. |
| 86 | |
| 87 | Args: |
| 88 | request: Http request |
| 89 | uuid: The UUID of the device |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 90 | |
Mitja Nikolaus | 21f3b1b | 2018-11-02 17:10:02 +0100 | [diff] [blame] | 91 | Returns: |
| 92 | The update history of the requested device, sorted by the update |
| 93 | date. |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 94 | |
| 95 | """ |
Mitja Nikolaus | 21f3b1b | 2018-11-02 17:10:02 +0100 | [diff] [blame] | 96 | device = Device.objects.get(uuid=uuid) |
| 97 | |
| 98 | device_heartbeats = list(device.heartbeats.all()) |
| 99 | device_crashreports = list(device.crashreports.all()) |
| 100 | |
| 101 | build_fingerprints = {hb.build_fingerprint for hb in device_heartbeats} |
| 102 | |
| 103 | response = [ |
| 104 | get_release_stats( |
| 105 | build_fingerprint, |
| 106 | device, |
| 107 | device_crashreports, |
| 108 | device_heartbeats, |
| 109 | ) |
| 110 | for build_fingerprint in build_fingerprints |
| 111 | ] |
| 112 | response = sorted(response, key=operator.itemgetter("update_date")) |
| 113 | |
| 114 | return Response(response) |
| 115 | |
| 116 | |
| 117 | def get_release_stats(build_fingerprint, device, crashreports, heartbeats): |
| 118 | """Get the stats for a device for a specific release.""" |
| 119 | heartbeats = filter_instances( |
| 120 | heartbeats, lambda hb: hb.build_fingerprint == build_fingerprint |
| 121 | ) |
| 122 | crashreports = filter_instances( |
| 123 | crashreports, lambda c: c.build_fingerprint == build_fingerprint |
| 124 | ) |
| 125 | |
| 126 | stats = get_stats(heartbeats, crashreports) |
| 127 | stats.update( |
| 128 | { |
| 129 | "build_fingerprint": build_fingerprint, |
| 130 | "update_date": min([heartbeat.date for heartbeat in heartbeats]), |
| 131 | "max": device.id, |
| 132 | } |
| 133 | ) |
| 134 | |
| 135 | return stats |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 136 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 137 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 138 | _DEVICE_REPORT_HISTORY_SCHEMA = openapi.Schema( |
| 139 | type=openapi.TYPE_ARRAY, |
| 140 | items=openapi.Schema( |
| 141 | type=openapi.TYPE_OBJECT, |
| 142 | title="DeviceReportHistoryEntry", |
| 143 | properties=OrderedDict( |
| 144 | [ |
| 145 | ("date", openapi.Schema(type=openapi.TYPE_STRING)), |
| 146 | ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 147 | ("other", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 148 | ("prob_crashes", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 149 | ("smpl", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 150 | ] |
| 151 | ), |
| 152 | ), |
| 153 | ) |
| 154 | |
| 155 | |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 156 | class DeviceReportHistory(APIView): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 157 | """View the report history of a specific device.""" |
| 158 | |
Mitja Nikolaus | 048e20a | 2018-11-12 19:07:30 +0100 | [diff] [blame] | 159 | permission_classes = (HasStatsAccess,) |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 160 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 161 | @swagger_auto_schema( |
| 162 | operation_description="Get the report history of a device", |
Mitja Nikolaus | 4d759da | 2018-08-28 15:31:29 +0200 | [diff] [blame] | 163 | security=SWAGGER_SECURITY_REQUIREMENTS_ALL, |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 164 | responses=dict( |
| 165 | [ |
| 166 | default_desc(NotFound), |
| 167 | ( |
| 168 | status.HTTP_200_OK, |
| 169 | openapi.Response( |
| 170 | _RESPONSE_STATUS_200_DESCRIPTION, |
| 171 | _DEVICE_REPORT_HISTORY_SCHEMA, |
| 172 | ), |
| 173 | ), |
| 174 | ] |
| 175 | ), |
| 176 | ) |
Mitja Nikolaus | b61247b | 2018-08-31 10:53:28 +0200 | [diff] [blame] | 177 | def get(self, request, uuid): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 178 | """Get the report history of a device. |
| 179 | |
| 180 | Args: |
| 181 | request: Http request |
| 182 | uuid: The UUID of the device |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 183 | |
Mitja Nikolaus | 21f3b1b | 2018-11-02 17:10:02 +0100 | [diff] [blame] | 184 | Returns: The report history of the requested device, sorted by date. |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 185 | |
| 186 | """ |
Mitja Nikolaus | 21f3b1b | 2018-11-02 17:10:02 +0100 | [diff] [blame] | 187 | device = Device.objects.get(uuid=uuid) |
| 188 | |
| 189 | device_heartbeats = list(device.heartbeats.all()) |
| 190 | device_crashreports = list(device.crashreports.all()) |
| 191 | |
Mitja Nikolaus | fd452f8 | 2018-11-07 11:53:59 +0100 | [diff] [blame] | 192 | dates = {heartbeat.date for heartbeat in device_heartbeats} |
Mitja Nikolaus | 21f3b1b | 2018-11-02 17:10:02 +0100 | [diff] [blame] | 193 | |
| 194 | response = [ |
| 195 | get_stats_for_date(date, device_crashreports, device_heartbeats) |
| 196 | for date in sorted(dates) |
| 197 | ] |
| 198 | |
| 199 | return Response(response) |
| 200 | |
| 201 | |
| 202 | def get_stats_for_date(date, crashreports, heartbeats): |
| 203 | """Get the stats for a device for a specific date.""" |
Mitja Nikolaus | fd452f8 | 2018-11-07 11:53:59 +0100 | [diff] [blame] | 204 | heartbeats = filter_instances(heartbeats, lambda hb: hb.date == date) |
Mitja Nikolaus | 21f3b1b | 2018-11-02 17:10:02 +0100 | [diff] [blame] | 205 | crashreports = filter_instances( |
| 206 | crashreports, lambda c: c.date.date() == date |
| 207 | ) |
| 208 | |
| 209 | stats = get_stats(heartbeats, crashreports) |
| 210 | stats.update(date=date) |
| 211 | return stats |
| 212 | |
| 213 | |
| 214 | def filter_instances(instances, filter_expr): |
| 215 | """Filter instances using a lambda filter function.""" |
| 216 | return list(filter(filter_expr, instances)) |
| 217 | |
| 218 | |
| 219 | def get_stats(heartbeats, crashreports): |
| 220 | """Get the numbers of heartbeats and crashes per for each type.""" |
| 221 | crashes = [ |
| 222 | crashreport |
| 223 | for crashreport in crashreports |
| 224 | if crashreport.boot_reason in Crashreport.CRASH_BOOT_REASONS |
| 225 | ] |
| 226 | smpls = [ |
| 227 | crashreport |
| 228 | for crashreport in crashreports |
| 229 | if crashreport.boot_reason in Crashreport.SMPL_BOOT_REASONS |
| 230 | ] |
| 231 | others = [ |
| 232 | crashreport |
| 233 | for crashreport in crashreports |
| 234 | if crashreport not in crashes + smpls |
| 235 | ] |
| 236 | return { |
| 237 | "heartbeats": len(heartbeats), |
| 238 | "smpl": len(smpls), |
| 239 | "prob_crashes": len(crashes), |
| 240 | "other": len(others), |
| 241 | } |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 242 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 243 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 244 | _STATUS_RESPONSE_SCHEMA = openapi.Schema( |
| 245 | title="Status", |
| 246 | type=openapi.TYPE_OBJECT, |
| 247 | properties=OrderedDict( |
| 248 | [ |
| 249 | ("devices", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 250 | ("crashreports", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 251 | ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 252 | ] |
| 253 | ), |
| 254 | ) |
| 255 | |
| 256 | |
Dirk Vogt | 571168c | 2017-12-08 16:54:12 +0100 | [diff] [blame] | 257 | class Status(APIView): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 258 | """View the number of devices, crashreports and heartbeats.""" |
| 259 | |
Borjan Tchakaloff | fa134bd | 2018-04-09 16:16:11 +0200 | [diff] [blame] | 260 | permission_classes = (HasStatsAccess,) |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 261 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 262 | @swagger_auto_schema( |
| 263 | operation_description="Get the number of devices, crashreports and " |
| 264 | "heartbeats", |
Mitja Nikolaus | 4d759da | 2018-08-28 15:31:29 +0200 | [diff] [blame] | 265 | security=SWAGGER_SECURITY_REQUIREMENTS_OAUTH, |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 266 | responses=dict( |
| 267 | [ |
| 268 | ( |
| 269 | status.HTTP_200_OK, |
| 270 | openapi.Response( |
| 271 | _RESPONSE_STATUS_200_DESCRIPTION, |
| 272 | _STATUS_RESPONSE_SCHEMA, |
| 273 | ), |
| 274 | ) |
| 275 | ] |
| 276 | ), |
| 277 | ) |
Mitja Nikolaus | b61247b | 2018-08-31 10:53:28 +0200 | [diff] [blame] | 278 | def get(self, request): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 279 | """Get the number of devices, crashreports and heartbeats. |
| 280 | |
| 281 | Args: |
| 282 | request: Http request |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 283 | |
| 284 | Returns: The number of devices, crashreports and heartbeats. |
| 285 | |
| 286 | """ |
Dirk Vogt | 571168c | 2017-12-08 16:54:12 +0100 | [diff] [blame] | 287 | num_devices = Device.objects.count() |
| 288 | num_crashreports = Crashreport.objects.count() |
| 289 | num_heartbeats = HeartBeat.objects.count() |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 290 | return Response( |
| 291 | { |
| 292 | "devices": num_devices, |
| 293 | "crashreports": num_crashreports, |
| 294 | "heartbeats": num_heartbeats, |
| 295 | } |
| 296 | ) |
Dirk Vogt | 571168c | 2017-12-08 16:54:12 +0100 | [diff] [blame] | 297 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 298 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 299 | _DEVICE_STAT_OVERVIEW_SCHEMA = openapi.Schema( |
| 300 | title="DeviceStatOverview", |
| 301 | type=openapi.TYPE_OBJECT, |
| 302 | properties=OrderedDict( |
| 303 | [ |
| 304 | ("board_date", openapi.Schema(type=openapi.TYPE_STRING)), |
| 305 | ("crashes_per_day", openapi.Schema(type=openapi.TYPE_NUMBER)), |
| 306 | ("crashreports", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 307 | ("heartbeats", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 308 | ("last_active", openapi.Schema(type=openapi.TYPE_STRING)), |
| 309 | ("smpl_per_day", openapi.Schema(type=openapi.TYPE_NUMBER)), |
| 310 | ("smpls", openapi.Schema(type=openapi.TYPE_INTEGER)), |
| 311 | ("uuid", openapi.Schema(type=openapi.TYPE_STRING)), |
| 312 | ] |
| 313 | ), |
| 314 | ) |
| 315 | |
| 316 | |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 317 | class DeviceStat(APIView): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 318 | """View an overview of the statistics of a device.""" |
| 319 | |
Mitja Nikolaus | 048e20a | 2018-11-12 19:07:30 +0100 | [diff] [blame] | 320 | permission_classes = (HasStatsAccess,) |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 321 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 322 | @swagger_auto_schema( |
| 323 | operation_description="Get some general statistics for a device.", |
Mitja Nikolaus | 4d759da | 2018-08-28 15:31:29 +0200 | [diff] [blame] | 324 | security=SWAGGER_SECURITY_REQUIREMENTS_ALL, |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 325 | responses=dict( |
| 326 | [ |
| 327 | default_desc(NotFound), |
| 328 | ( |
| 329 | status.HTTP_200_OK, |
| 330 | openapi.Response( |
| 331 | _RESPONSE_STATUS_200_DESCRIPTION, |
| 332 | _DEVICE_STAT_OVERVIEW_SCHEMA, |
| 333 | ), |
| 334 | ), |
| 335 | ] |
| 336 | ), |
| 337 | ) |
Mitja Nikolaus | b61247b | 2018-08-31 10:53:28 +0200 | [diff] [blame] | 338 | def get(self, request, uuid): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 339 | """Get some general statistics for a device. |
| 340 | |
| 341 | Args: |
| 342 | request: Http request |
| 343 | uuid: The UUID of the device |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 344 | |
| 345 | Returns: Some general information of the device in a dictionary. |
| 346 | |
| 347 | """ |
| 348 | device = Device.objects.filter(uuid=uuid) |
Mitja Nikolaus | c6e5ca0 | 2018-11-02 15:30:45 +0100 | [diff] [blame] | 349 | |
| 350 | heartbeat_instances = HeartBeat.objects.filter(device=device) |
| 351 | if heartbeat_instances.exists(): |
| 352 | last_active = heartbeat_instances.order_by("-date")[0].date |
| 353 | else: |
| 354 | last_active = device[0].board_date |
| 355 | |
| 356 | heartbeats = heartbeat_instances.count() |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 357 | crashreports = ( |
| 358 | Crashreport.objects.filter(device=device) |
| 359 | .filter(boot_reason__in=Crashreport.CRASH_BOOT_REASONS) |
| 360 | .count() |
| 361 | ) |
| 362 | crashes_per_day = ( |
| 363 | crashreports * 1.0 / heartbeats if heartbeats > 0 else 0 |
| 364 | ) |
| 365 | smpls = ( |
| 366 | Crashreport.objects.filter(device=device) |
| 367 | .filter(boot_reason__in=Crashreport.SMPL_BOOT_REASONS) |
| 368 | .count() |
| 369 | ) |
| 370 | smpl_per_day = smpls * 1.0 / heartbeats if heartbeats > 0 else 0 |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 371 | return Response( |
| 372 | { |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 373 | "uuid": uuid, |
| 374 | "last_active": last_active, |
| 375 | "heartbeats": heartbeats, |
| 376 | "crashreports": crashreports, |
| 377 | "crashes_per_day": crashes_per_day, |
| 378 | "smpls": smpls, |
| 379 | "smpl_per_day": smpl_per_day, |
| 380 | "board_date": device[0].board_date, |
| 381 | } |
| 382 | ) |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 383 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 384 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 385 | _LOG_FILE_SCHEMA = openapi.Schema(title="LogFile", type=openapi.TYPE_FILE) |
| 386 | |
| 387 | |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 388 | class LogFileDownload(APIView): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 389 | """View for downloading log files.""" |
| 390 | |
Mitja Nikolaus | 048e20a | 2018-11-12 19:07:30 +0100 | [diff] [blame] | 391 | permission_classes = (HasStatsAccess,) |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 392 | |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 393 | @swagger_auto_schema( |
| 394 | operation_description="Get a log file.", |
Mitja Nikolaus | 4d759da | 2018-08-28 15:31:29 +0200 | [diff] [blame] | 395 | security=SWAGGER_SECURITY_REQUIREMENTS_ALL, |
Mitja Nikolaus | 24f4d12 | 2018-08-24 16:32:58 +0200 | [diff] [blame] | 396 | responses=dict( |
| 397 | [ |
| 398 | default_desc(NotFound), |
| 399 | ( |
| 400 | status.HTTP_200_OK, |
| 401 | openapi.Response( |
| 402 | _RESPONSE_STATUS_200_DESCRIPTION, _LOG_FILE_SCHEMA |
| 403 | ), |
| 404 | ), |
| 405 | ] |
| 406 | ), |
| 407 | ) |
Mitja Nikolaus | b61247b | 2018-08-31 10:53:28 +0200 | [diff] [blame] | 408 | def get(self, request, id_logfile): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 409 | """Get a logfile. |
| 410 | |
| 411 | Args: |
| 412 | request: Http request |
| 413 | id_logfile: The id of the log file |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 414 | |
| 415 | Returns: The log file with the corresponding id. |
| 416 | |
| 417 | """ |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 418 | try: |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 419 | logfile = LogFile.objects.get(id=id_logfile) |
| 420 | except ObjectDoesNotExist: |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 421 | raise NotFound(detail="Logfile does not exist.") |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 422 | zip_file = zipfile.ZipFile(logfile.logfile.path) |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 423 | ret = {} |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 424 | for file in zip_file.filelist: |
| 425 | file_open = zip_file.open(file) |
| 426 | ret[file.filename] = file_open.read() |
Dirk Vogt | 62ff7f2 | 2017-05-04 16:07:21 +0200 | [diff] [blame] | 427 | return Response(ret) |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 428 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 429 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 430 | class _VersionStatsFilter(FilterSet): |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 431 | first_seen_before = DateFilter( |
| 432 | field_name="first_seen_on", lookup_expr="lte" |
| 433 | ) |
| 434 | first_seen_after = DateFilter(field_name="first_seen_on", lookup_expr="gte") |
| 435 | released_before = DateFilter(field_name="released_on", lookup_expr="lte") |
| 436 | released_after = DateFilter(field_name="released_on", lookup_expr="gte") |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 437 | |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 438 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 439 | class _VersionStatsSerializer(serializers.ModelSerializer): |
Borjan Tchakaloff | fa134bd | 2018-04-09 16:16:11 +0200 | [diff] [blame] | 440 | permission_classes = (HasStatsAccess,) |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 441 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 442 | |
Mitja Nikolaus | 4d759da | 2018-08-28 15:31:29 +0200 | [diff] [blame] | 443 | @method_decorator( |
| 444 | name="get", |
| 445 | decorator=swagger_auto_schema(security=SWAGGER_SECURITY_REQUIREMENTS_OAUTH), |
| 446 | ) |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 447 | class _VersionStatsListView(generics.ListAPIView): |
Borjan Tchakaloff | fa134bd | 2018-04-09 16:16:11 +0200 | [diff] [blame] | 448 | permission_classes = (HasStatsAccess,) |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 449 | filter_backends = (DjangoFilterBackend,) |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 450 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 451 | |
| 452 | class _DailyVersionStatsFilter(FilterSet): |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 453 | date_start = DateFilter(field_name="date", lookup_expr="gte") |
| 454 | date_end = DateFilter(field_name="date", lookup_expr="lte") |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 455 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 456 | |
| 457 | class _DailyVersionStatsSerializer(serializers.ModelSerializer): |
Borjan Tchakaloff | fa134bd | 2018-04-09 16:16:11 +0200 | [diff] [blame] | 458 | permission_classes = (HasStatsAccess,) |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 459 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 460 | |
Mitja Nikolaus | 4d759da | 2018-08-28 15:31:29 +0200 | [diff] [blame] | 461 | @method_decorator( |
| 462 | name="get", |
| 463 | decorator=swagger_auto_schema(security=SWAGGER_SECURITY_REQUIREMENTS_OAUTH), |
| 464 | ) |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 465 | class _DailyVersionStatsListView(generics.ListAPIView): |
Borjan Tchakaloff | fa134bd | 2018-04-09 16:16:11 +0200 | [diff] [blame] | 466 | permission_classes = (HasStatsAccess,) |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 467 | filter_backends = (DjangoFilterBackend,) |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 468 | |
| 469 | |
| 470 | class VersionSerializer(_VersionStatsSerializer): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 471 | """Serializer for the Version class.""" |
| 472 | |
| 473 | class Meta: # noqa: D106 |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 474 | model = Version |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 475 | fields = "__all__" |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 476 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 477 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 478 | class VersionFilter(_VersionStatsFilter): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 479 | """Filter for Version instances.""" |
| 480 | |
| 481 | class Meta: # noqa: D106 |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 482 | model = Version |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 483 | fields = "__all__" |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 484 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 485 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 486 | class VersionListView(_VersionStatsListView): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 487 | """View for listing versions.""" |
| 488 | |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 489 | queryset = Version.objects.all().order_by("-heartbeats") |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 490 | filter_class = VersionFilter |
| 491 | serializer_class = VersionSerializer |
| 492 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 493 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 494 | class VersionDailyFilter(_DailyVersionStatsFilter): |
| 495 | """Filter for VersionDaily instances.""" |
| 496 | |
| 497 | version__build_fingerprint = CharFilter() |
| 498 | version__is_official_release = BooleanFilter() |
| 499 | version__is_beta_release = BooleanFilter() |
| 500 | |
| 501 | class Meta: # noqa: D106 |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 502 | model = VersionDaily |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 503 | fields = "__all__" |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 504 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 505 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 506 | class VersionDailySerializer(_DailyVersionStatsSerializer): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 507 | """Serializer for VersionDaily instances.""" |
| 508 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 509 | build_fingerprint = serializers.CharField() |
| 510 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 511 | class Meta: # noqa: D106 |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 512 | model = VersionDaily |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 513 | fields = "__all__" |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 514 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 515 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 516 | class VersionDailyListView(_DailyVersionStatsListView): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 517 | """View for listing VersionDaily instances.""" |
| 518 | |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 519 | queryset = ( |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 520 | VersionDaily.objects.annotate( |
| 521 | build_fingerprint=F("version__build_fingerprint") |
| 522 | ) |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 523 | .all() |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 524 | .order_by("date") |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 525 | ) |
| 526 | filter_class = VersionDailyFilter |
| 527 | filter_fields = ( |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 528 | "version__build_fingerprint", |
| 529 | "version__is_official_release", |
| 530 | "version__is_beta_release", |
Borjan Tchakaloff | 227b431 | 2018-02-23 17:02:16 +0400 | [diff] [blame] | 531 | ) |
Dirk Vogt | 1accb67 | 2017-05-10 14:07:42 +0200 | [diff] [blame] | 532 | serializer_class = VersionDailySerializer |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 533 | |
| 534 | |
| 535 | class RadioVersionSerializer(_VersionStatsSerializer): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 536 | """Serializer for RadioVersion instances.""" |
| 537 | |
| 538 | class Meta: # noqa: D106 |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 539 | model = RadioVersion |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 540 | fields = "__all__" |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 541 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 542 | |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 543 | class RadioVersionFilter(_VersionStatsFilter): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 544 | """Filter for RadioVersion instances.""" |
| 545 | |
| 546 | class Meta: # noqa: D106 |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 547 | model = RadioVersion |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 548 | fields = "__all__" |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 549 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 550 | |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 551 | class RadioVersionListView(_VersionStatsListView): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 552 | """View for listing RadioVersion instances.""" |
| 553 | |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 554 | queryset = RadioVersion.objects.all().order_by("-heartbeats") |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 555 | serializer_class = RadioVersionSerializer |
| 556 | filter_class = RadioVersionFilter |
| 557 | |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 558 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 559 | class RadioVersionDailyFilter(_DailyVersionStatsFilter): |
| 560 | """Filter for RadioVersionDaily instances.""" |
| 561 | |
| 562 | version__radio_version = CharFilter() |
| 563 | version__is_official_release = BooleanFilter() |
| 564 | version__is_beta_release = BooleanFilter() |
| 565 | |
| 566 | class Meta: # noqa: D106 |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 567 | model = RadioVersionDaily |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 568 | fields = "__all__" |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 569 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 570 | |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 571 | class RadioVersionDailySerializer(_DailyVersionStatsSerializer): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 572 | """Serializer for RadioVersionDaily instances.""" |
| 573 | |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 574 | radio_version = serializers.CharField() |
| 575 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 576 | class Meta: # noqa: D106 |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 577 | model = RadioVersionDaily |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 578 | fields = "__all__" |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 579 | |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 580 | |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 581 | class RadioVersionDailyListView(_DailyVersionStatsListView): |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 582 | """View for listing RadioVersionDaily instances.""" |
| 583 | |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 584 | queryset = ( |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 585 | RadioVersionDaily.objects.annotate( |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 586 | radio_version=F("version__radio_version") |
| 587 | ) |
Mitja Nikolaus | 5c3e057 | 2018-07-30 13:36:14 +0200 | [diff] [blame] | 588 | .all() |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 589 | .order_by("date") |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 590 | ) |
| 591 | filter_class = RadioVersionDailyFilter |
| 592 | filter_fields = ( |
Mitja Nikolaus | cb50f2c | 2018-08-24 13:54:48 +0200 | [diff] [blame] | 593 | "version__radio_version", |
| 594 | "version__is_official_release", |
| 595 | "version__is_beta_release", |
Borjan Tchakaloff | 1db45c7 | 2018-02-23 17:03:49 +0400 | [diff] [blame] | 596 | ) |
| 597 | serializer_class = RadioVersionDailySerializer |