blob: c54f7f1eec48c4171822188e0de23bda62ba6b55 [file] [log] [blame]
Dirk Vogtc9e10ab2016-10-12 13:58:15 +02001# -*- coding: utf-8 -*-
Mitja Nikolaus6a679132018-08-30 14:35:29 +02002"""Models for devices, heartbeats, crashreports and log files."""
3
Mitja Nikolausb6cf6972018-10-04 15:03:31 +02004import os
Mitja Nikolausbcaf5022018-08-30 16:40:38 +02005import uuid
Dirk Vogtc9e10ab2016-10-12 13:58:15 +02006
Mitja Nikolausbcaf5022018-08-30 16:40:38 +02007from django.db import models, transaction
Dirk Vogtf2a33422016-10-11 17:17:26 +02008from django.contrib.auth.models import User
9from taggit.managers import TaggableManager
Dirk Vogtc9e10ab2016-10-12 13:58:15 +020010
Dirk Vogtc9e10ab2016-10-12 13:58:15 +020011
Dirk Vogtf2a33422016-10-11 17:17:26 +020012class Device(models.Model):
Mitja Nikolaus6a679132018-08-30 14:35:29 +020013 """A device representing a phone that has been registered on Hiccup."""
14
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020015 def __str__(self):
Mitja Nikolaus6a679132018-08-30 14:35:29 +020016 """Return the UUID as string representation of a device."""
Dirk Vogt83107df2017-05-02 12:04:19 +020017 return self.uuid
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020018
Dirk Vogtf2a33422016-10-11 17:17:26 +020019 # for every device there is a django user
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020020 uuid = models.CharField(
21 db_index=True,
22 max_length=64,
23 unique=True,
24 default=uuid.uuid4,
25 editable=False,
26 )
Dirk Vogtc9e10ab2016-10-12 13:58:15 +020027 user = models.OneToOneField(
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020028 User,
29 related_name="Hiccup_Device",
30 on_delete=models.CASCADE,
31 unique=True,
32 )
Dirk Vogtc9e10ab2016-10-12 13:58:15 +020033 imei = models.CharField(max_length=32, null=True, blank=True)
34 board_date = models.DateTimeField(null=True, blank=True)
35 chipset = models.CharField(max_length=200, null=True, blank=True)
36 tags = TaggableManager(blank=True)
Dirk Vogtf2a33422016-10-11 17:17:26 +020037 last_heartbeat = models.DateTimeField(null=True, blank=True)
Dirk Vogtc9e10ab2016-10-12 13:58:15 +020038 token = models.CharField(max_length=200, null=True, blank=True)
Dirk Vogt67eb1482016-10-13 12:42:56 +020039 next_per_crashreport_key = models.PositiveIntegerField(default=1)
40 next_per_heartbeat_key = models.PositiveIntegerField(default=1)
41
42 @transaction.atomic
43 def get_crashreport_key(self):
Mitja Nikolaus6a679132018-08-30 14:35:29 +020044 """Get the next key for a crashreport and update the ID-counter."""
Dirk Vogt67eb1482016-10-13 12:42:56 +020045 ret = self.next_per_crashreport_key
46 self.next_per_crashreport_key = self.next_per_crashreport_key + 1
47 self.save()
48 return ret
49
50 @transaction.atomic
51 def get_heartbeat_key(self):
Mitja Nikolaus6a679132018-08-30 14:35:29 +020052 """Get the next key for a heartbeat and update the ID-counter."""
Dirk Vogt67eb1482016-10-13 12:42:56 +020053 ret = self.next_per_heartbeat_key
Dirk Vogt0d9d5d22016-10-13 16:17:57 +020054 self.next_per_heartbeat_key = self.next_per_heartbeat_key + 1
Dirk Vogt67eb1482016-10-13 12:42:56 +020055 self.save()
56 return ret
Dirk Vogtc9e10ab2016-10-12 13:58:15 +020057
Dirk Vogtf130c752016-08-23 14:45:01 +020058
59def crashreport_file_name(instance, filename):
Mitja Nikolaus6a679132018-08-30 14:35:29 +020060 """Generate the full path for new uploaded log files.
61
Mitja Nikolausb6cf6972018-10-04 15:03:31 +020062 The path is created by splitting up the device UUID into 3 parts: The
63 first 2 characters, the second 2 characters and the rest. This way the
64 number of directories in each subdirectory does not get too big.
65
Mitja Nikolaus6a679132018-08-30 14:35:29 +020066 Args:
67 instance: The log file instance.
68 filename: The name of the actual log file.
69
70 Returns: The generated path including the file name.
71
72 """
Mitja Nikolausb6cf6972018-10-04 15:03:31 +020073 return os.path.join(
Mitja Nikolausb6cf6972018-10-04 15:03:31 +020074 str(instance.crashreport.device.uuid)[0:2],
75 str(instance.crashreport.device.uuid)[2:4],
76 str(instance.crashreport.device.uuid)[4:],
77 str(instance.crashreport.id),
78 filename,
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020079 )
Dirk Vogtf130c752016-08-23 14:45:01 +020080
Dirk Vogtc9e10ab2016-10-12 13:58:15 +020081
Dirk Vogtf130c752016-08-23 14:45:01 +020082class Crashreport(models.Model):
Mitja Nikolaus6a679132018-08-30 14:35:29 +020083 """A crashreport that was sent by a device."""
84
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020085 BOOT_REASON_UNKOWN = "UNKNOWN"
86 BOOT_REASON_KEYBOARD_POWER_ON = "keyboard power on"
87 BOOT_REASON_RTC_ALARM = "RTC alarm"
88 CRASH_BOOT_REASONS = [BOOT_REASON_UNKOWN, BOOT_REASON_KEYBOARD_POWER_ON]
89 SMPL_BOOT_REASONS = [BOOT_REASON_RTC_ALARM]
Franz-Xaver Geiger0b3a48e2018-04-16 15:00:14 +020090
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +020091 device = models.ForeignKey(
92 Device,
93 db_index=True,
94 related_name="crashreports",
95 on_delete=models.CASCADE,
96 )
Dirk Vogtc9e10ab2016-10-12 13:58:15 +020097 is_fake_report = models.BooleanField(default=False)
98 app_version = models.IntegerField()
99 uptime = models.CharField(max_length=200)
Dirk Vogt83107df2017-05-02 12:04:19 +0200100 build_fingerprint = models.CharField(db_index=True, max_length=200)
Borjan Tchakaloff6f239a62018-02-19 09:05:50 +0100101 radio_version = models.CharField(db_index=True, max_length=200, null=True)
Dirk Vogt83107df2017-05-02 12:04:19 +0200102 boot_reason = models.CharField(db_index=True, max_length=200)
103 power_on_reason = models.CharField(db_index=True, max_length=200)
104 power_off_reason = models.CharField(db_index=True, max_length=200)
105 date = models.DateTimeField(db_index=True)
Dirk Vogtf2a33422016-10-11 17:17:26 +0200106 tags = TaggableManager(blank=True)
Dirk Vogt67eb1482016-10-13 12:42:56 +0200107 device_local_id = models.PositiveIntegerField(blank=True)
Dirk Vogt36635692016-10-17 12:19:10 +0200108 next_logfile_key = models.PositiveIntegerField(default=1)
Dirk Vogteda80d32016-11-21 11:45:50 +0100109 created_at = models.DateTimeField(auto_now_add=True)
Dirk Vogt67eb1482016-10-13 12:42:56 +0200110
111 @transaction.atomic
112 def get_logfile_key(self):
Mitja Nikolaus6a679132018-08-30 14:35:29 +0200113 """Get the next key for a log file and update the ID-counter."""
Dirk Vogt67eb1482016-10-13 12:42:56 +0200114 ret = self.next_logfile_key
115 self.next_logfile_key = self.next_logfile_key + 1
116 self.save()
117 return ret
118
Mitja Nikolaus773d0cd2018-08-31 10:55:43 +0200119 def save(
120 self,
121 force_insert=False,
122 force_update=False,
123 using=None,
124 update_fields=None,
125 ):
Mitja Nikolaus6a679132018-08-30 14:35:29 +0200126 """Save the crashreport and set its local ID if it was not set."""
Dirk Vogt67eb1482016-10-13 12:42:56 +0200127 if not self.device_local_id:
128 self.device_local_id = self.device.get_crashreport_key()
Mitja Nikolaus773d0cd2018-08-31 10:55:43 +0200129 super(Crashreport, self).save(
130 force_insert, force_update, using, update_fields
131 )
Dirk Vogtc9e10ab2016-10-12 13:58:15 +0200132
Dirk Vogtf2a33422016-10-11 17:17:26 +0200133 def _get_uuid(self):
Mitja Nikolaus6a679132018-08-30 14:35:29 +0200134 """Return the device UUID."""
Dirk Vogtf2a33422016-10-11 17:17:26 +0200135 return self.device.uuid
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200136
Dirk Vogtf2a33422016-10-11 17:17:26 +0200137 uuid = property(_get_uuid)
Dirk Vogtc9e10ab2016-10-12 13:58:15 +0200138
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200139
Dirk Vogtf2a33422016-10-11 17:17:26 +0200140class LogFile(models.Model):
Mitja Nikolaus6a679132018-08-30 14:35:29 +0200141 """A log file that was sent along with a crashreport."""
142
Dirk Vogt7160b5e2016-10-12 17:04:40 +0200143 logfile_type = models.TextField(max_length=36, default="last_kmsg")
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200144 crashreport = models.ForeignKey(
145 Crashreport, related_name="logfiles", on_delete=models.CASCADE
146 )
Dirk Vogteda80d32016-11-21 11:45:50 +0100147 logfile = models.FileField(upload_to=crashreport_file_name, max_length=500)
Dirk Vogt67eb1482016-10-13 12:42:56 +0200148 crashreport_local_id = models.PositiveIntegerField(blank=True)
Dirk Vogteda80d32016-11-21 11:45:50 +0100149 created_at = models.DateTimeField(auto_now_add=True)
Dirk Vogt67eb1482016-10-13 12:42:56 +0200150
Mitja Nikolaus773d0cd2018-08-31 10:55:43 +0200151 def save(
152 self,
153 force_insert=False,
154 force_update=False,
155 using=None,
156 update_fields=None,
157 ):
Mitja Nikolaus6a679132018-08-30 14:35:29 +0200158 """Save the log file and set its local ID if it was not set."""
Dirk Vogt36635692016-10-17 12:19:10 +0200159 if not self.crashreport_local_id:
160 self.crashreport_local_id = self.crashreport.get_logfile_key()
Mitja Nikolaus773d0cd2018-08-31 10:55:43 +0200161 super(LogFile, self).save(
162 force_insert, force_update, using, update_fields
163 )
Dirk Vogtf2a33422016-10-11 17:17:26 +0200164
Dirk Vogtc9e10ab2016-10-12 13:58:15 +0200165
166class HeartBeat(models.Model):
Mitja Nikolaus6a679132018-08-30 14:35:29 +0200167 """A heartbeat that was sent by a device."""
168
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200169 device = models.ForeignKey(
170 Device,
Dirk Vogt83107df2017-05-02 12:04:19 +0200171 db_index=True,
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200172 related_name="heartbeats",
173 on_delete=models.CASCADE,
174 )
Dirk Vogt1433f7c2016-09-20 15:30:56 +0200175 app_version = models.IntegerField()
Dirk Vogtf130c752016-08-23 14:45:01 +0200176 uptime = models.CharField(max_length=200)
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200177 build_fingerprint = models.CharField(db_index=True, max_length=200)
Borjan Tchakaloff6f239a62018-02-19 09:05:50 +0100178 radio_version = models.CharField(db_index=True, max_length=200, null=True)
Dirk Vogt83107df2017-05-02 12:04:19 +0200179 date = models.DateTimeField(db_index=True)
Dirk Vogt67eb1482016-10-13 12:42:56 +0200180 device_local_id = models.PositiveIntegerField(blank=True)
Dirk Vogteda80d32016-11-21 11:45:50 +0100181 created_at = models.DateTimeField(auto_now_add=True)
Dirk Vogt67eb1482016-10-13 12:42:56 +0200182
Mitja Nikolaus773d0cd2018-08-31 10:55:43 +0200183 def save(
184 self,
185 force_insert=False,
186 force_update=False,
187 using=None,
188 update_fields=None,
189 ):
Mitja Nikolaus6a679132018-08-30 14:35:29 +0200190 """Save the heartbeat and set its local ID if it was not set."""
Dirk Vogt67eb1482016-10-13 12:42:56 +0200191 if not self.device_local_id:
192 self.device_local_id = self.device.get_heartbeat_key()
Mitja Nikolaus773d0cd2018-08-31 10:55:43 +0200193 super(HeartBeat, self).save(
194 force_insert, force_update, using, update_fields
195 )
Dirk Vogtc9e10ab2016-10-12 13:58:15 +0200196
197 def _get_uuid(self):
Mitja Nikolaus6a679132018-08-30 14:35:29 +0200198 """Return the device UUID."""
Dirk Vogtc9e10ab2016-10-12 13:58:15 +0200199 return self.device.uuid
Mitja Nikolauscb50f2c2018-08-24 13:54:48 +0200200
Dirk Vogtc9e10ab2016-10-12 13:58:15 +0200201 uuid = property(_get_uuid)