Format all python files using the formatter
Run `git ls-files '*.py' | xargs black`
Issue: HIC-161
Change-Id: I1619e6296bc4036504c5bb73128f769a1b7b688d
diff --git a/crashreport_stats/admin.py b/crashreport_stats/admin.py
index 3c1939b..db5b576 100644
--- a/crashreport_stats/admin.py
+++ b/crashreport_stats/admin.py
@@ -4,7 +4,7 @@
Version,
VersionDaily,
RadioVersion,
- RadioVersionDaily
+ RadioVersionDaily,
)
@@ -15,4 +15,5 @@
@admin.register(VersionDaily, RadioVersionDaily)
class DailyVersionStatsAdmin(admin.ModelAdmin):
"""Admin for daily version stats."""
- list_display = ('version', 'date')
+
+ list_display = ("version", "date")
diff --git a/crashreport_stats/apps.py b/crashreport_stats/apps.py
index 4b0fb94..07dd518 100644
--- a/crashreport_stats/apps.py
+++ b/crashreport_stats/apps.py
@@ -1,4 +1,5 @@
from django.apps import AppConfig
+
class CrashreportStatsConfig(AppConfig):
- name = 'crashreport_stats'
+ name = "crashreport_stats"
diff --git a/crashreport_stats/management/commands/stats.py b/crashreport_stats/management/commands/stats.py
index 5ac2dea..c30c2e0 100644
--- a/crashreport_stats/management/commands/stats.py
+++ b/crashreport_stats/management/commands/stats.py
@@ -12,9 +12,11 @@
import pytz
from crashreport_stats.models import (
- RadioVersion, RadioVersionDaily,
+ RadioVersion,
+ RadioVersionDaily,
StatsMetadata,
- Version, VersionDaily,
+ Version,
+ VersionDaily,
)
from crashreports.models import Crashreport, HeartBeat
@@ -23,7 +25,7 @@
# Classes in this file inherit from each other and are not method containers.
-class _ReportCounterFilter():
+class _ReportCounterFilter:
"""Filter reports matching a report counter requirements.
Attributes:
@@ -68,7 +70,8 @@
def __init__(self):
"""Initialise the filter."""
super(HeartBeatCounterFilter, self).__init__(
- model=HeartBeat, name='heartbeats', field_name='heartbeats')
+ model=HeartBeat, name="heartbeats", field_name="heartbeats"
+ )
class CrashreportCounterFilter(_ReportCounterFilter):
@@ -87,8 +90,12 @@
"""
def __init__(
- self, name, field_name, include_boot_reasons=None,
- exclude_boot_reasons=None):
+ self,
+ name,
+ field_name,
+ include_boot_reasons=None,
+ exclude_boot_reasons=None,
+ ):
"""Initialise the filter.
One or both of `include_boot_reasons` and `exclude_boot_reasons` must
@@ -113,11 +120,13 @@
"""
if not include_boot_reasons and not exclude_boot_reasons:
raise ValueError(
- 'One or both of `include_boot_reasons` and '
- '`exclude_boot_reasons` must be specified.')
+ "One or both of `include_boot_reasons` and "
+ "`exclude_boot_reasons` must be specified."
+ )
super(CrashreportCounterFilter, self).__init__(
- model=Crashreport, name=name, field_name=field_name)
+ model=Crashreport, name=name, field_name=field_name
+ )
# Cache the boot reasons inclusive filter
self.include_boot_reasons = include_boot_reasons
@@ -163,7 +172,7 @@
return query_objects
-class _StatsModelsEngine():
+class _StatsModelsEngine:
"""Stats models engine.
An engine to update general stats (_VersionStats) and their daily
@@ -232,11 +241,12 @@
"""
return (
- query_objects
- .annotate(_report_day=TruncDate('date'))
- .values(self.version_field_name, '_report_day')
+ query_objects.annotate(_report_day=TruncDate("date")).values(
+ self.version_field_name, "_report_day"
+ )
# FIXME Agressively drop duplicates
- .annotate(count=Count('date', distinct=True)))
+ .annotate(count=Count("date", distinct=True))
+ )
def delete_stats(self):
"""Delete the general and daily stats instances.
@@ -277,13 +287,15 @@
"""
counts_per_model = {
- self.stats_model: {'created': 0, 'updated': 0},
- self.daily_stats_model: {'created': 0, 'updated': 0}}
+ self.stats_model: {"created": 0, "updated": 0},
+ self.daily_stats_model: {"created": 0, "updated": 0},
+ }
query_objects = self._valid_objects(report_counter.model.objects.all())
# Only include reports from the interesting period of time
query_objects = self._objects_within_period(
- query_objects, up_to, starting_from)
+ query_objects, up_to, starting_from
+ )
# Apply the report counter requirements
query_objects = report_counter.filter(query_objects)
# Chain our own filters
@@ -292,17 +304,22 @@
# Explicitly use the iterator() method to avoid caching as we will
# not re-use the QuerySet
for query_object in query_objects.iterator():
- report_day = query_object['_report_day']
+ report_day = query_object["_report_day"]
# Use a dict to be able to dereference the field name
- stats, created = self.stats_model.objects.get_or_create(**{
- self.version_field_name: query_object[self.version_field_name],
- 'defaults': {
- 'first_seen_on': report_day,
- 'released_on': report_day,
+ stats, created = self.stats_model.objects.get_or_create(
+ **{
+ self.version_field_name: query_object[
+ self.version_field_name
+ ],
+ "defaults": {
+ "first_seen_on": report_day,
+ "released_on": report_day,
+ },
}
- })
- counts_per_model[self.stats_model][(
- 'created' if created else 'updated')] += 1
+ )
+ counts_per_model[self.stats_model][
+ ("created" if created else "updated")
+ ] += 1
# Reports are coming in an unordered manner, a late report can
# be older (device time wise). Make sure that the current reports
@@ -315,18 +332,23 @@
stats.released_on = report_day
stats.first_seen_on = report_day
- daily_stats, created = (
- self.daily_stats_model.objects.get_or_create(
- version=stats, date=report_day))
- counts_per_model[self.daily_stats_model][(
- 'created' if created else 'updated')] += 1
+ daily_stats, created = self.daily_stats_model.objects.get_or_create(
+ version=stats, date=report_day
+ )
+ counts_per_model[self.daily_stats_model][
+ ("created" if created else "updated")
+ ] += 1
setattr(
- stats, report_counter.field_name,
- F(report_counter.field_name) + query_object['count'])
+ stats,
+ report_counter.field_name,
+ F(report_counter.field_name) + query_object["count"],
+ )
setattr(
- daily_stats, report_counter.field_name,
- F(report_counter.field_name) + query_object['count'])
+ daily_stats,
+ report_counter.field_name,
+ F(report_counter.field_name) + query_object["count"],
+ )
stats.save()
daily_stats.save()
@@ -344,8 +366,10 @@
def __init__(self):
"""Initialise the engine."""
super(VersionStatsEngine, self).__init__(
- stats_model=Version, daily_stats_model=VersionDaily,
- version_field_name='build_fingerprint')
+ stats_model=Version,
+ daily_stats_model=VersionDaily,
+ version_field_name="build_fingerprint",
+ )
class RadioVersionStatsEngine(_StatsModelsEngine):
@@ -358,8 +382,10 @@
def __init__(self):
"""Initialise the engine."""
super(RadioVersionStatsEngine, self).__init__(
- stats_model=RadioVersion, daily_stats_model=RadioVersionDaily,
- version_field_name='radio_version')
+ stats_model=RadioVersion,
+ daily_stats_model=RadioVersionDaily,
+ version_field_name="radio_version",
+ )
def _valid_objects(self, query_objects):
# For legacy reasons, the version field might be null
@@ -369,43 +395,46 @@
class Command(BaseCommand):
"""Management command to compute Hiccup statistics."""
- _STATS_MODELS_ENGINES = [
- VersionStatsEngine(),
- RadioVersionStatsEngine(),
- ]
+ _STATS_MODELS_ENGINES = [VersionStatsEngine(), RadioVersionStatsEngine()]
# All the report counters that are listed in the stats models
_REPORT_COUNTER_FILTERS = [
HeartBeatCounterFilter(),
CrashreportCounterFilter(
- name='crashes', field_name='prob_crashes',
- include_boot_reasons=Crashreport.CRASH_BOOT_REASONS),
+ name="crashes",
+ field_name="prob_crashes",
+ include_boot_reasons=Crashreport.CRASH_BOOT_REASONS,
+ ),
CrashreportCounterFilter(
- name='smpl', field_name='smpl',
- include_boot_reasons=Crashreport.SMPL_BOOT_REASONS),
+ name="smpl",
+ field_name="smpl",
+ include_boot_reasons=Crashreport.SMPL_BOOT_REASONS,
+ ),
CrashreportCounterFilter(
- name='other', field_name='other',
+ name="other",
+ field_name="other",
exclude_boot_reasons=(
- Crashreport.SMPL_BOOT_REASONS
- + Crashreport.CRASH_BOOT_REASONS)),
+ Crashreport.SMPL_BOOT_REASONS + Crashreport.CRASH_BOOT_REASONS
+ ),
+ ),
]
help = __doc__
def add_arguments(self, parser):
"""Add custom arguments to the command."""
- parser.add_argument('action', choices=['reset', 'update'])
+ parser.add_argument("action", choices=["reset", "update"])
def handle(self, *args, **options):
"""Carry out the command executive logic."""
# pylint: disable=attribute-defined-outside-init
# self.debug is only ever read through calls of handle().
- self.debug = int(options['verbosity']) >= 2
+ self.debug = int(options["verbosity"]) >= 2
- if options['action'] == 'reset':
+ if options["action"] == "reset":
self.delete_all_stats()
self.update_all_stats()
- elif options['action'] == 'update':
+ elif options["action"] == "update":
self.update_all_stats()
def _success(self, msg, *args, **kwargs):
@@ -423,22 +452,21 @@
if not counts_per_model:
counts_per_model = {
engine.stats_model._meta.label: 0,
- engine.daily_stats_model._meta.label: 0}
+ engine.daily_stats_model._meta.label: 0,
+ }
for model, count in counts_per_model.items():
- name = model.split('.')[-1]
- self._success(
- '{} {} deleted'.format(count, name))
+ name = model.split(".")[-1]
+ self._success("{} {} deleted".format(count, name))
# Reset the metadata
count, _ = StatsMetadata.objects.all().delete()
if self.debug:
- self._success(
- '{} StatsMetadata deleted'.format(count))
+ self._success("{} StatsMetadata deleted".format(count))
def update_all_stats(self):
"""Update the statistics from all stats models."""
try:
- previous_update = StatsMetadata.objects.latest('updated_at')
+ previous_update = StatsMetadata.objects.latest("updated_at")
starting_from = previous_update.updated_at
except StatsMetadata.DoesNotExist:
starting_from = None
@@ -450,13 +478,14 @@
with transaction.atomic():
for filter_ in self._REPORT_COUNTER_FILTERS:
counts_per_model = engine.update_stats(
- filter_, up_to, starting_from)
+ filter_, up_to, starting_from
+ )
if self.debug:
for model, counts in counts_per_model.items():
for action, count in counts.items():
- msg = '{} {} {} for counter {}'.format(
- count, model.__name__, action,
- filter_.name)
+ msg = "{} {} {} for counter {}".format(
+ count, model.__name__, action, filter_.name
+ )
self._success(msg)
StatsMetadata(updated_at=up_to).save()
diff --git a/crashreport_stats/migrations/0001_initial.py b/crashreport_stats/migrations/0001_initial.py
index b87395e..2ff76d4 100644
--- a/crashreport_stats/migrations/0001_initial.py
+++ b/crashreport_stats/migrations/0001_initial.py
@@ -8,39 +8,65 @@
from django.db import connection
from datetime import date, timedelta
+
class Migration(migrations.Migration):
initial = True
- dependencies = [
- ]
+ dependencies = []
operations = [
migrations.CreateModel(
- name='Version',
+ name="Version",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('build_fingerprint', models.CharField(max_length=200, unique=True)),
- ('is_official_release', models.BooleanField(default=False)),
- ('is_beta_release', models.BooleanField(default=False)),
- ('first_seen_on', models.DateField()),
- ('released_on', models.DateField()),
- ('heartbeats', models.IntegerField()),
- ('prob_crashes', models.IntegerField()),
- ('smpl', models.IntegerField()),
- ('other', models.IntegerField()),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "build_fingerprint",
+ models.CharField(max_length=200, unique=True),
+ ),
+ ("is_official_release", models.BooleanField(default=False)),
+ ("is_beta_release", models.BooleanField(default=False)),
+ ("first_seen_on", models.DateField()),
+ ("released_on", models.DateField()),
+ ("heartbeats", models.IntegerField()),
+ ("prob_crashes", models.IntegerField()),
+ ("smpl", models.IntegerField()),
+ ("other", models.IntegerField()),
],
),
migrations.CreateModel(
- name='VersionDaily',
+ name="VersionDaily",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('date', models.DateField()),
- ('heartbeats', models.IntegerField()),
- ('prob_crashes', models.IntegerField()),
- ('smpl', models.IntegerField()),
- ('other', models.IntegerField()),
- ('version', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='daily_stats', to='crashreport_stats.Version')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("date", models.DateField()),
+ ("heartbeats", models.IntegerField()),
+ ("prob_crashes", models.IntegerField()),
+ ("smpl", models.IntegerField()),
+ ("other", models.IntegerField()),
+ (
+ "version",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="daily_stats",
+ to="crashreport_stats.Version",
+ ),
+ ),
],
),
]
diff --git a/crashreport_stats/migrations/0002_version_and_versiondaily_with_defaults.py b/crashreport_stats/migrations/0002_version_and_versiondaily_with_defaults.py
index 0a58758..29da259 100644
--- a/crashreport_stats/migrations/0002_version_and_versiondaily_with_defaults.py
+++ b/crashreport_stats/migrations/0002_version_and_versiondaily_with_defaults.py
@@ -9,64 +9,62 @@
class Migration(migrations.Migration):
- dependencies = [
- ('crashreport_stats', '0001_initial'),
- ]
+ dependencies = [("crashreport_stats", "0001_initial")]
operations = [
migrations.AlterField(
- model_name='version',
- name='first_seen_on',
+ model_name="version",
+ name="first_seen_on",
field=models.DateField(auto_now_add=True),
),
migrations.AlterField(
- model_name='version',
- name='heartbeats',
+ model_name="version",
+ name="heartbeats",
field=models.IntegerField(default=0),
),
migrations.AlterField(
- model_name='version',
- name='other',
+ model_name="version",
+ name="other",
field=models.IntegerField(default=0),
),
migrations.AlterField(
- model_name='version',
- name='prob_crashes',
+ model_name="version",
+ name="prob_crashes",
field=models.IntegerField(default=0),
),
migrations.AlterField(
- model_name='version',
- name='released_on',
+ model_name="version",
+ name="released_on",
field=models.DateField(auto_now_add=True),
),
migrations.AlterField(
- model_name='version',
- name='smpl',
+ model_name="version",
+ name="smpl",
field=models.IntegerField(default=0),
),
migrations.AlterField(
- model_name='versiondaily',
- name='date',
+ model_name="versiondaily",
+ name="date",
field=models.DateField(auto_now_add=True),
),
migrations.AlterField(
- model_name='versiondaily',
- name='heartbeats',
+ model_name="versiondaily",
+ name="heartbeats",
field=models.IntegerField(default=0),
),
migrations.AlterField(
- model_name='versiondaily',
- name='other',
+ model_name="versiondaily",
+ name="other",
field=models.IntegerField(default=0),
),
migrations.AlterField(
- model_name='versiondaily',
- name='prob_crashes',
+ model_name="versiondaily",
+ name="prob_crashes",
field=models.IntegerField(default=0),
),
migrations.AlterField(
- model_name='versiondaily',
- name='smpl',
+ model_name="versiondaily",
+ name="smpl",
field=models.IntegerField(default=0),
),
]
diff --git a/crashreport_stats/migrations/0003_radioversion_radioversiondaily.py b/crashreport_stats/migrations/0003_radioversion_radioversiondaily.py
index 3ce431a..e3e8261 100644
--- a/crashreport_stats/migrations/0003_radioversion_radioversiondaily.py
+++ b/crashreport_stats/migrations/0003_radioversion_radioversiondaily.py
@@ -10,41 +10,63 @@
class Migration(migrations.Migration):
dependencies = [
- ('crashreport_stats', '0002_version_and_versiondaily_with_defaults'),
+ ("crashreport_stats", "0002_version_and_versiondaily_with_defaults")
]
operations = [
migrations.CreateModel(
- name='RadioVersion',
+ name="RadioVersion",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('is_official_release', models.BooleanField(default=False)),
- ('is_beta_release', models.BooleanField(default=False)),
- ('first_seen_on', models.DateField(auto_now_add=True)),
- ('released_on', models.DateField(auto_now_add=True)),
- ('heartbeats', models.IntegerField(default=0)),
- ('prob_crashes', models.IntegerField(default=0)),
- ('smpl', models.IntegerField(default=0)),
- ('other', models.IntegerField(default=0)),
- ('radio_version', models.CharField(max_length=200, unique=True)),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("is_official_release", models.BooleanField(default=False)),
+ ("is_beta_release", models.BooleanField(default=False)),
+ ("first_seen_on", models.DateField(auto_now_add=True)),
+ ("released_on", models.DateField(auto_now_add=True)),
+ ("heartbeats", models.IntegerField(default=0)),
+ ("prob_crashes", models.IntegerField(default=0)),
+ ("smpl", models.IntegerField(default=0)),
+ ("other", models.IntegerField(default=0)),
+ (
+ "radio_version",
+ models.CharField(max_length=200, unique=True),
+ ),
],
- options={
- 'abstract': False,
- },
+ options={"abstract": False},
),
migrations.CreateModel(
- name='RadioVersionDaily',
+ name="RadioVersionDaily",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('date', models.DateField(auto_now_add=True)),
- ('heartbeats', models.IntegerField(default=0)),
- ('prob_crashes', models.IntegerField(default=0)),
- ('smpl', models.IntegerField(default=0)),
- ('other', models.IntegerField(default=0)),
- ('version', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='daily_stats', to='crashreport_stats.RadioVersion')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("date", models.DateField(auto_now_add=True)),
+ ("heartbeats", models.IntegerField(default=0)),
+ ("prob_crashes", models.IntegerField(default=0)),
+ ("smpl", models.IntegerField(default=0)),
+ ("other", models.IntegerField(default=0)),
+ (
+ "version",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="daily_stats",
+ to="crashreport_stats.RadioVersion",
+ ),
+ ),
],
- options={
- 'abstract': False,
- },
+ options={"abstract": False},
),
]
diff --git a/crashreport_stats/migrations/0004_statsmedata_and_no_broken_default_dates.py b/crashreport_stats/migrations/0004_statsmedata_and_no_broken_default_dates.py
index bf0ff94..7cbd433 100644
--- a/crashreport_stats/migrations/0004_statsmedata_and_no_broken_default_dates.py
+++ b/crashreport_stats/migrations/0004_statsmedata_and_no_broken_default_dates.py
@@ -8,48 +8,47 @@
"""Introduce the StatsMetadata model and remove broken default dates."""
dependencies = [
- ('crashreport_stats', '0003_radioversion_radioversiondaily'),
+ ("crashreport_stats", "0003_radioversion_radioversiondaily")
]
operations = [
migrations.CreateModel(
- name='StatsMetadata',
+ name="StatsMetadata",
fields=[
(
- 'id', models.AutoField(
- auto_created=True, primary_key=True,
- serialize=False, verbose_name='ID')),
- ('updated_at', models.DateTimeField(default=None)),
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("updated_at", models.DateTimeField(default=None)),
],
),
migrations.AlterField(
- model_name='radioversiondaily',
- name='date',
+ model_name="radioversiondaily",
+ name="date",
field=models.DateField(),
),
migrations.AlterField(
- model_name='versiondaily',
- name='date',
+ model_name="versiondaily", name="date", field=models.DateField()
+ ),
+ migrations.AlterField(
+ model_name="radioversion",
+ name="first_seen_on",
field=models.DateField(),
),
migrations.AlterField(
- model_name='radioversion',
- name='first_seen_on',
+ model_name="radioversion",
+ name="released_on",
field=models.DateField(),
),
migrations.AlterField(
- model_name='radioversion',
- name='released_on',
- field=models.DateField(),
+ model_name="version", name="first_seen_on", field=models.DateField()
),
migrations.AlterField(
- model_name='version',
- name='first_seen_on',
- field=models.DateField(),
- ),
- migrations.AlterField(
- model_name='version',
- name='released_on',
- field=models.DateField(),
+ model_name="version", name="released_on", field=models.DateField()
),
]
diff --git a/crashreport_stats/migrations/0005_remove_manual_default_value.py b/crashreport_stats/migrations/0005_remove_manual_default_value.py
index 3c508ed..ebc9db5 100644
--- a/crashreport_stats/migrations/0005_remove_manual_default_value.py
+++ b/crashreport_stats/migrations/0005_remove_manual_default_value.py
@@ -10,13 +10,13 @@
"""Remove default None value which was added manually."""
dependencies = [
- ('crashreport_stats', '0004_statsmedata_and_no_broken_default_dates'),
+ ("crashreport_stats", "0004_statsmedata_and_no_broken_default_dates")
]
operations = [
migrations.AlterField(
- model_name='statsmetadata',
- name='updated_at',
+ model_name="statsmetadata",
+ name="updated_at",
field=models.DateTimeField(),
- ),
+ )
]
diff --git a/crashreport_stats/models.py b/crashreport_stats/models.py
index 16ccb51..e4bb683 100644
--- a/crashreport_stats/models.py
+++ b/crashreport_stats/models.py
@@ -96,8 +96,11 @@
"""
version = models.ForeignKey(
- Version, db_index=True, related_name='daily_stats',
- on_delete=models.CASCADE)
+ Version,
+ db_index=True,
+ related_name="daily_stats",
+ on_delete=models.CASCADE,
+ )
class RadioVersion(_VersionStats):
@@ -125,8 +128,11 @@
"""
version = models.ForeignKey(
- RadioVersion, db_index=True, related_name='daily_stats',
- on_delete=models.CASCADE)
+ RadioVersion,
+ db_index=True,
+ related_name="daily_stats",
+ on_delete=models.CASCADE,
+ )
class StatsMetadata(models.Model):
diff --git a/crashreport_stats/raw_querys.py b/crashreport_stats/raw_querys.py
index 5b1878b..a71a346 100644
--- a/crashreport_stats/raw_querys.py
+++ b/crashreport_stats/raw_querys.py
@@ -1,34 +1,51 @@
from django.conf import settings
+
class FormatDict(dict):
def __missing__(self, key):
return "{" + key + "}"
+
def fill_in_build_fingerprints(query, build_fingerprints):
- all_fingerprints_query = 'select distinct build_fingerprint from crashreports_crashreport'
- if len(build_fingerprints) > 0 :
+ all_fingerprints_query = (
+ "select distinct build_fingerprint from crashreports_crashreport"
+ )
+ if len(build_fingerprints) > 0:
return query.format(
- FormatDict(fingerprint_placeholers=
- ','.join(["%s"] * len(build_fingerprints))))
+ FormatDict(
+ fingerprint_placeholers=",".join(
+ ["%s"] * len(build_fingerprints)
+ )
+ )
+ )
else:
- return query.format(FormatDict(fingerprint_placeholers = all_fingerprints_query))
+ return query.format(
+ FormatDict(fingerprint_placeholers=all_fingerprints_query)
+ )
def execute_device_update_history_query(cursor, params):
- if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql_psycopg2':
+ if (
+ settings.DATABASES["default"]["ENGINE"]
+ == "django.db.backends.postgresql_psycopg2"
+ ):
return psql_execute_device_update_history_query(cursor, params)
else:
return sqlite_execute_device_update_history_query(cursor, params)
def execute_device_report_history(cursor, params):
- if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql_psycopg2':
+ if (
+ settings.DATABASES["default"]["ENGINE"]
+ == "django.db.backends.postgresql_psycopg2"
+ ):
return psql_execute_device_report_history(cursor, params)
else:
return sqlite_execute_device_report_history(cursor, params)
+
def sqlite_execute_device_update_history_query(cursor, params):
- query = '''
+ query = """
SELECT
min(crashreports_heartbeat.date) as update_date,
build_fingerprint,
@@ -54,14 +71,14 @@
where
crashreports_device.uuid=%s
group by build_fingerprint;
- '''
- uuid = params.get('uuid', '18f530d7-e9c3-4dcf-adba-3dddcd7d3155')
+ """
+ uuid = params.get("uuid", "18f530d7-e9c3-4dcf-adba-3dddcd7d3155")
param_array = [uuid]
cursor.execute(query, param_array)
def psql_execute_device_update_history_query(cursor, params):
- query = '''
+ query = """
SELECT
min(crashreports_heartbeat.date) as update_date,
build_fingerprint,
@@ -88,14 +105,14 @@
where
crashreports_device.uuid=%s
group by build_fingerprint;
- '''
- uuid = params.get('uuid', '18f530d7-e9c3-4dcf-adba-3dddcd7d3155')
+ """
+ uuid = params.get("uuid", "18f530d7-e9c3-4dcf-adba-3dddcd7d3155")
param_array = [uuid]
cursor.execute(query, param_array)
def sqlite_execute_device_report_history(cursor, params):
- query = '''
+ query = """
SELECT
strftime("%%Y-%%m-%%d",crashreports_heartbeat.date) as date,
count(crashreports_heartbeat.id) as heartbeats,
@@ -126,14 +143,14 @@
where
crashreports_device.uuid = %s
group by date;
- '''
- uuid = params.get('uuid', '18f530d7-e9c3-4dcf-adba-3dddcd7d3155')
+ """
+ uuid = params.get("uuid", "18f530d7-e9c3-4dcf-adba-3dddcd7d3155")
param_array = [uuid]
cursor.execute(query, param_array)
def psql_execute_device_report_history(cursor, params):
- query = '''
+ query = """
SELECT
crashreports_heartbeat.date::date as date,
count(crashreports_heartbeat.id) as heartbeats,
@@ -145,7 +162,7 @@
left join crashreports_crashreport on crashreports_device.id = crashreports_crashreport.device_id and crashreports_heartbeat.date::date = crashreports_crashreport.date::date
where
crashreports_device.uuid = %s group by crashreports_heartbeat.date, crashreports_device.id;
- '''
- uuid = params.get('uuid', '18f530d7-e9c3-4dcf-adba-3dddcd7d3155')
+ """
+ uuid = params.get("uuid", "18f530d7-e9c3-4dcf-adba-3dddcd7d3155")
param_array = [uuid]
cursor.execute(query, param_array)
diff --git a/crashreport_stats/rest_endpoints.py b/crashreport_stats/rest_endpoints.py
index 7da1a5f..5db2e36 100644
--- a/crashreport_stats/rest_endpoints.py
+++ b/crashreport_stats/rest_endpoints.py
@@ -12,18 +12,23 @@
from django.db.models.expressions import F
from django_filters.rest_framework import (
- DjangoFilterBackend, DateFilter,
- FilterSet, CharFilter, BooleanFilter
+ DjangoFilterBackend,
+ DateFilter,
+ FilterSet,
+ CharFilter,
+ BooleanFilter,
)
from crashreport_stats.models import (
- Version, VersionDaily, RadioVersion, RadioVersionDaily
+ Version,
+ VersionDaily,
+ RadioVersion,
+ RadioVersionDaily,
)
-from crashreports.models import (
- Device, Crashreport, HeartBeat, LogFile
-)
+from crashreports.models import Device, Crashreport, HeartBeat, LogFile
from crashreports.permissions import (
- HasRightsOrIsDeviceOwnerDeviceCreation, HasStatsAccess
+ HasRightsOrIsDeviceOwnerDeviceCreation,
+ HasStatsAccess,
)
from . import raw_querys
@@ -32,15 +37,14 @@
"""Return all rows from a cursor as a dict."""
desc = cursor.description
return [
- dict(zip([col[0] for col in desc], row))
- for row in cursor.fetchall()
+ dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall()
]
class DeviceUpdateHistory(APIView):
"""View the update history of a specific device."""
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
def get(self, request, uuid, format=None):
"""Get the update history of a device.
@@ -54,11 +58,7 @@
"""
cursor = connection.cursor()
- raw_querys.execute_device_update_history_query(
- cursor,
- {
- 'uuid': uuid
- })
+ raw_querys.execute_device_update_history_query(cursor, {"uuid": uuid})
res = dictfetchall(cursor)
return Response(res)
@@ -66,7 +66,7 @@
class DeviceReportHistory(APIView):
"""View the report history of a specific device."""
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
def get(self, request, uuid, format=None):
"""Get the report history of a device.
@@ -80,11 +80,7 @@
"""
cursor = connection.cursor()
- raw_querys.execute_device_report_history(
- cursor,
- {
- 'uuid': uuid
- })
+ raw_querys.execute_device_report_history(cursor, {"uuid": uuid})
res = dictfetchall(cursor)
return Response(res)
@@ -94,7 +90,7 @@
permission_classes = (HasStatsAccess,)
- def get(self, request, format=None, ):
+ def get(self, request, format=None):
"""Get the number of devices, crashreports and heartbeats.
Args:
@@ -107,19 +103,21 @@
num_devices = Device.objects.count()
num_crashreports = Crashreport.objects.count()
num_heartbeats = HeartBeat.objects.count()
- return Response({
- 'devices': num_devices,
- 'crashreports': num_crashreports,
- 'heartbeats': num_heartbeats
- })
+ return Response(
+ {
+ "devices": num_devices,
+ "crashreports": num_crashreports,
+ "heartbeats": num_heartbeats,
+ }
+ )
class DeviceStat(APIView):
"""View an overview of the statistics of a device."""
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
- def get(self, request, uuid, format=None, ):
+ def get(self, request, uuid, format=None):
"""Get some general statistics for a device.
Args:
@@ -131,32 +129,42 @@
"""
device = Device.objects.filter(uuid=uuid)
- last_active = HeartBeat.objects.filter(device=device).order_by(
- '-date')[0].date
+ last_active = (
+ HeartBeat.objects.filter(device=device).order_by("-date")[0].date
+ )
heartbeats = HeartBeat.objects.filter(device=device).count()
- crashreports = Crashreport.objects.filter(device=device).filter(
- boot_reason__in=Crashreport.CRASH_BOOT_REASONS).count()
- crashes_per_day = crashreports*1.0/heartbeats if heartbeats > 0 else 0
- smpls = Crashreport.objects.filter(device=device).filter(
- boot_reason__in=Crashreport.SMPL_BOOT_REASONS).count()
- smpl_per_day = smpls*1.0/heartbeats if heartbeats > 0 else 0
+ crashreports = (
+ Crashreport.objects.filter(device=device)
+ .filter(boot_reason__in=Crashreport.CRASH_BOOT_REASONS)
+ .count()
+ )
+ crashes_per_day = (
+ crashreports * 1.0 / heartbeats if heartbeats > 0 else 0
+ )
+ smpls = (
+ Crashreport.objects.filter(device=device)
+ .filter(boot_reason__in=Crashreport.SMPL_BOOT_REASONS)
+ .count()
+ )
+ smpl_per_day = smpls * 1.0 / heartbeats if heartbeats > 0 else 0
return Response(
{
- 'uuid': uuid,
- 'last_active': last_active,
- 'heartbeats': heartbeats,
- 'crashreports': crashreports,
- 'crashes_per_day': crashes_per_day,
- 'smpls': smpls,
- 'smpl_per_day': smpl_per_day,
- 'board_date': device[0].board_date,
- })
+ "uuid": uuid,
+ "last_active": last_active,
+ "heartbeats": heartbeats,
+ "crashreports": crashreports,
+ "crashes_per_day": crashes_per_day,
+ "smpls": smpls,
+ "smpl_per_day": smpl_per_day,
+ "board_date": device[0].board_date,
+ }
+ )
class LogFileDownload(APIView):
"""View for downloading log files."""
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
def get(self, request, id_logfile, format=None):
"""Get a logfile.
@@ -182,12 +190,12 @@
class _VersionStatsFilter(FilterSet):
- first_seen_before = DateFilter(field_name="first_seen_on",
- lookup_expr='lte')
- first_seen_after = DateFilter(field_name="first_seen_on",
- lookup_expr='gte')
- released_before = DateFilter(field_name="released_on", lookup_expr='lte')
- released_after = DateFilter(field_name="released_on", lookup_expr='gte')
+ first_seen_before = DateFilter(
+ field_name="first_seen_on", lookup_expr="lte"
+ )
+ first_seen_after = DateFilter(field_name="first_seen_on", lookup_expr="gte")
+ released_before = DateFilter(field_name="released_on", lookup_expr="lte")
+ released_after = DateFilter(field_name="released_on", lookup_expr="gte")
class _VersionStatsSerializer(serializers.ModelSerializer):
@@ -200,8 +208,8 @@
class _DailyVersionStatsFilter(FilterSet):
- date_start = DateFilter(field_name="date", lookup_expr='gte')
- date_end = DateFilter(field_name="date", lookup_expr='lte')
+ date_start = DateFilter(field_name="date", lookup_expr="gte")
+ date_end = DateFilter(field_name="date", lookup_expr="lte")
class _DailyVersionStatsSerializer(serializers.ModelSerializer):
@@ -218,7 +226,7 @@
class Meta: # noqa: D106
model = Version
- fields = '__all__'
+ fields = "__all__"
class VersionFilter(_VersionStatsFilter):
@@ -226,13 +234,13 @@
class Meta: # noqa: D106
model = Version
- fields = '__all__'
+ fields = "__all__"
class VersionListView(_VersionStatsListView):
"""View for listing versions."""
- queryset = Version.objects.all().order_by('-heartbeats')
+ queryset = Version.objects.all().order_by("-heartbeats")
filter_class = VersionFilter
serializer_class = VersionSerializer
@@ -246,7 +254,7 @@
class Meta: # noqa: D106
model = VersionDaily
- fields = '__all__'
+ fields = "__all__"
class VersionDailySerializer(_DailyVersionStatsSerializer):
@@ -256,24 +264,24 @@
class Meta: # noqa: D106
model = VersionDaily
- fields = '__all__'
+ fields = "__all__"
class VersionDailyListView(_DailyVersionStatsListView):
"""View for listing VersionDaily instances."""
queryset = (
- VersionDaily
- .objects
- .annotate(build_fingerprint=F('version__build_fingerprint'))
+ VersionDaily.objects.annotate(
+ build_fingerprint=F("version__build_fingerprint")
+ )
.all()
- .order_by('date')
+ .order_by("date")
)
filter_class = VersionDailyFilter
filter_fields = (
- 'version__build_fingerprint',
- 'version__is_official_release',
- 'version__is_beta_release',
+ "version__build_fingerprint",
+ "version__is_official_release",
+ "version__is_beta_release",
)
serializer_class = VersionDailySerializer
@@ -283,7 +291,7 @@
class Meta: # noqa: D106
model = RadioVersion
- fields = '__all__'
+ fields = "__all__"
class RadioVersionFilter(_VersionStatsFilter):
@@ -291,13 +299,13 @@
class Meta: # noqa: D106
model = RadioVersion
- fields = '__all__'
+ fields = "__all__"
class RadioVersionListView(_VersionStatsListView):
"""View for listing RadioVersion instances."""
- queryset = RadioVersion.objects.all().order_by('-heartbeats')
+ queryset = RadioVersion.objects.all().order_by("-heartbeats")
serializer_class = RadioVersionSerializer
filter_class = RadioVersionFilter
@@ -311,7 +319,7 @@
class Meta: # noqa: D106
model = RadioVersionDaily
- fields = '__all__'
+ fields = "__all__"
class RadioVersionDailySerializer(_DailyVersionStatsSerializer):
@@ -321,7 +329,7 @@
class Meta: # noqa: D106
model = RadioVersionDaily
- fields = '__all__'
+ fields = "__all__"
class RadioVersionDailyListView(_DailyVersionStatsListView):
@@ -329,14 +337,15 @@
queryset = (
RadioVersionDaily.objects.annotate(
- radio_version=F('version__radio_version'))
+ radio_version=F("version__radio_version")
+ )
.all()
- .order_by('date')
+ .order_by("date")
)
filter_class = RadioVersionDailyFilter
filter_fields = (
- 'version__radio_version',
- 'version__is_official_release',
- 'version__is_beta_release',
+ "version__radio_version",
+ "version__is_official_release",
+ "version__is_beta_release",
)
serializer_class = RadioVersionDailySerializer
diff --git a/crashreport_stats/templatetags/crashreport_stats_tags.py b/crashreport_stats/templatetags/crashreport_stats_tags.py
index 235175d..22f475d 100644
--- a/crashreport_stats/templatetags/crashreport_stats_tags.py
+++ b/crashreport_stats/templatetags/crashreport_stats_tags.py
@@ -3,67 +3,118 @@
register = template.Library()
-@register.simple_tag
-def device_overview(title = "General Information", uuid='e1c0cc95-ab8d-461a-a768-cb8d9d7adb04'):
- t = template.loader.get_template('crashreport_stats/tags/device_overview.html')
- return t.render({
- 'uuid': uuid,
- "title": title,
- "element_name": "device_overview"})
@register.simple_tag
-def device_crashreport_table(title = "Crashreports", uuid='e1c0cc95-ab8d-461a-a768-cb8d9d7adb04'):
- t = template.loader.get_template('crashreport_stats/tags/device_crashreport_table.html')
- return t.render({
- 'uuid': uuid,
- "title": title,
- "element_name": "device_crashreport_table"})
-
-@register.simple_tag
-def device_update_history(title = "Update History", uuid='e1c0cc95-ab8d-461a-a768-cb8d9d7adb04'):
- t = template.loader.get_template('crashreport_stats/tags/device_update_history.html')
- return t.render({
- 'uuid': uuid,
- "title": title,
- "element_name": "device_update_statistic"})
-
-@register.simple_tag
-def device_report_history(title = "Report History", uuid='e1c0cc95-ab8d-461a-a768-cb8d9d7adb04'):
- t = template.loader.get_template('crashreport_stats/tags/device_report_history.html')
- return t.render({
- 'uuid': uuid,
- "title": title,
- "element_name": "device_report_history"})
+def device_overview(
+ title="General Information", uuid="e1c0cc95-ab8d-461a-a768-cb8d9d7adb04"
+):
+ t = template.loader.get_template(
+ "crashreport_stats/tags/device_overview.html"
+ )
+ return t.render(
+ {"uuid": uuid, "title": title, "element_name": "device_overview"}
+ )
@register.simple_tag
-def versions_table(title = "FP2 OS Versions", is_official_release="1"):
- t = template.loader.get_template('crashreport_stats/tags/versions_table.html')
- return t.render({
- "title": title,
- "is_official_release":is_official_release,
- "element_name": "versions_overview_table"})
+def device_crashreport_table(
+ title="Crashreports", uuid="e1c0cc95-ab8d-461a-a768-cb8d9d7adb04"
+):
+ t = template.loader.get_template(
+ "crashreport_stats/tags/device_crashreport_table.html"
+ )
+ return t.render(
+ {
+ "uuid": uuid,
+ "title": title,
+ "element_name": "device_crashreport_table",
+ }
+ )
+
@register.simple_tag
-def versions_pie_chart(title = "FP2 Version Distribution", is_official_release="1"):
- t = template.loader.get_template('crashreport_stats/tags/versions_pie_chart.html')
- return t.render({
- "title": title,
- "is_official_release":is_official_release,
- "element_name": "versions_overview_pie_chart"})
+def device_update_history(
+ title="Update History", uuid="e1c0cc95-ab8d-461a-a768-cb8d9d7adb04"
+):
+ t = template.loader.get_template(
+ "crashreport_stats/tags/device_update_history.html"
+ )
+ return t.render(
+ {
+ "uuid": uuid,
+ "title": title,
+ "element_name": "device_update_statistic",
+ }
+ )
+
@register.simple_tag
-def versions_area_chart(title = "FP2 Version Distribution", is_official_release="1"):
- t = template.loader.get_template('crashreport_stats/tags/versions_area_chart.html')
- return t.render({
- "title": title,
- "is_official_release":is_official_release,
- "element_name": "versions_overview_area_chart"})
+def device_report_history(
+ title="Report History", uuid="e1c0cc95-ab8d-461a-a768-cb8d9d7adb04"
+):
+ t = template.loader.get_template(
+ "crashreport_stats/tags/device_report_history.html"
+ )
+ return t.render(
+ {"uuid": uuid, "title": title, "element_name": "device_report_history"}
+ )
+
@register.simple_tag
-def versions_bar_chart(title = "Version Stability", is_official_release="1"):
- t = template.loader.get_template('crashreport_stats/tags/versions_bar_chart.html')
- return t.render({
- "title": title,
- "is_official_release":is_official_release,
- "element_name": "versions_overview_bar_chart"})
+def versions_table(title="FP2 OS Versions", is_official_release="1"):
+ t = template.loader.get_template(
+ "crashreport_stats/tags/versions_table.html"
+ )
+ return t.render(
+ {
+ "title": title,
+ "is_official_release": is_official_release,
+ "element_name": "versions_overview_table",
+ }
+ )
+
+
+@register.simple_tag
+def versions_pie_chart(
+ title="FP2 Version Distribution", is_official_release="1"
+):
+ t = template.loader.get_template(
+ "crashreport_stats/tags/versions_pie_chart.html"
+ )
+ return t.render(
+ {
+ "title": title,
+ "is_official_release": is_official_release,
+ "element_name": "versions_overview_pie_chart",
+ }
+ )
+
+
+@register.simple_tag
+def versions_area_chart(
+ title="FP2 Version Distribution", is_official_release="1"
+):
+ t = template.loader.get_template(
+ "crashreport_stats/tags/versions_area_chart.html"
+ )
+ return t.render(
+ {
+ "title": title,
+ "is_official_release": is_official_release,
+ "element_name": "versions_overview_area_chart",
+ }
+ )
+
+
+@register.simple_tag
+def versions_bar_chart(title="Version Stability", is_official_release="1"):
+ t = template.loader.get_template(
+ "crashreport_stats/tags/versions_bar_chart.html"
+ )
+ return t.render(
+ {
+ "title": title,
+ "is_official_release": is_official_release,
+ "element_name": "versions_overview_bar_chart",
+ }
+ )
diff --git a/crashreport_stats/tests.py b/crashreport_stats/tests.py
index e852700..68f8e82 100644
--- a/crashreport_stats/tests.py
+++ b/crashreport_stats/tests.py
@@ -12,84 +12,91 @@
from rest_framework.test import APITestCase, APIClient
from crashreport_stats.models import (
- Version, VersionDaily, RadioVersion, RadioVersionDaily, StatsMetadata
+ Version,
+ VersionDaily,
+ RadioVersion,
+ RadioVersionDaily,
+ StatsMetadata,
)
from crashreports.models import User, Device, Crashreport, HeartBeat
-class Dummy():
+class Dummy:
"""Class for creating dummy instances for testing."""
# Valid unique entries
- BUILD_FINGERPRINTS = [(
- 'Fairphone/FP2/FP2:5.1/FP2/r4275.1_FP2_gms76_1.13.0:user/release-keys'
- ), (
- 'Fairphone/FP2/FP2:5.1.1/FP2-gms75.1.13.0/FP2-gms75.1.13.0'
- ':user/release-keys'
- ), (
- 'Fairphone/FP2/FP2:6.0.1/FP2-gms-18.04.1/FP2-gms-18.04.1'
- ':user/release-keys'
- ), (
- 'Fairphone/FP2/FP2:7.1.2/18.07.2/gms-7480c31d'
- ':user/release-keys'
- )]
- RADIO_VERSIONS = ['4437.1-FP2-0-07', '4437.1-FP2-0-08',
- '4437.1-FP2-0-09', '4437.1-FP2-0-10']
+ BUILD_FINGERPRINTS = [
+ (
+ "Fairphone/FP2/FP2:5.1/FP2/r4275.1_FP2_gms76_1.13.0:user/release-keys"
+ ),
+ (
+ "Fairphone/FP2/FP2:5.1.1/FP2-gms75.1.13.0/FP2-gms75.1.13.0"
+ ":user/release-keys"
+ ),
+ (
+ "Fairphone/FP2/FP2:6.0.1/FP2-gms-18.04.1/FP2-gms-18.04.1"
+ ":user/release-keys"
+ ),
+ ("Fairphone/FP2/FP2:7.1.2/18.07.2/gms-7480c31d" ":user/release-keys"),
+ ]
+ RADIO_VERSIONS = [
+ "4437.1-FP2-0-07",
+ "4437.1-FP2-0-08",
+ "4437.1-FP2-0-09",
+ "4437.1-FP2-0-10",
+ ]
DATES = [date(2018, 3, 19), date(2018, 3, 26), date(2018, 5, 1)]
DEFAULT_DUMMY_VERSION_VALUES = {
- 'build_fingerprint': BUILD_FINGERPRINTS[0],
- 'first_seen_on': DATES[1],
- 'released_on': DATES[0]
+ "build_fingerprint": BUILD_FINGERPRINTS[0],
+ "first_seen_on": DATES[1],
+ "released_on": DATES[0],
}
- DEFAULT_DUMMY_VERSION_DAILY_VALUES = {
- 'date': DATES[1]
- }
+ DEFAULT_DUMMY_VERSION_DAILY_VALUES = {"date": DATES[1]}
DEFAULT_DUMMY_RADIO_VERSION_VALUES = {
- 'radio_version': RADIO_VERSIONS[0],
- 'first_seen_on': DATES[1],
- 'released_on': DATES[0]
+ "radio_version": RADIO_VERSIONS[0],
+ "first_seen_on": DATES[1],
+ "released_on": DATES[0],
}
- DEFAULT_DUMMY_RADIO_VERSION_DAILY_VALUES = {
- 'date': DATES[1]
- }
+ DEFAULT_DUMMY_RADIO_VERSION_DAILY_VALUES = {"date": DATES[1]}
DEFAULT_DUMMY_STATSMETADATA_VALUES = {
- 'updated_at': datetime(2018, 6, 15, 2, 12, 24, tzinfo=pytz.utc),
+ "updated_at": datetime(2018, 6, 15, 2, 12, 24, tzinfo=pytz.utc)
}
DEFAULT_DUMMY_DEVICE_VALUES = {
- 'board_date': datetime(2015, 12, 15, 1, 23, 45, tzinfo=pytz.utc),
- 'chipset': 'Qualcomm MSM8974PRO-AA',
- 'token': '64111c62d521fb4724454ca6dea27e18f93ef56e'
+ "board_date": datetime(2015, 12, 15, 1, 23, 45, tzinfo=pytz.utc),
+ "chipset": "Qualcomm MSM8974PRO-AA",
+ "token": "64111c62d521fb4724454ca6dea27e18f93ef56e",
}
- DEFAULT_DUMMY_USER_VALUES = {
- 'username': 'testuser'
- }
+ DEFAULT_DUMMY_USER_VALUES = {"username": "testuser"}
DEFAULT_DUMMY_HEARTBEAT_VALUES = {
- 'app_version': 10100,
- 'uptime': (
- 'up time: 16 days, 21:49:56, idle time: 5 days, 20:55:04, '
- 'sleep time: 10 days, 20:46:27'),
- 'build_fingerprint': BUILD_FINGERPRINTS[0],
- 'radio_version': RADIO_VERSIONS[0],
- 'date': datetime(2018, 3, 19, tzinfo=pytz.utc),
+ "app_version": 10100,
+ "uptime": (
+ "up time: 16 days, 21:49:56, idle time: 5 days, 20:55:04, "
+ "sleep time: 10 days, 20:46:27"
+ ),
+ "build_fingerprint": BUILD_FINGERPRINTS[0],
+ "radio_version": RADIO_VERSIONS[0],
+ "date": datetime(2018, 3, 19, tzinfo=pytz.utc),
}
DEFAULT_DUMMY_CRASHREPORT_VALUES = DEFAULT_DUMMY_HEARTBEAT_VALUES.copy()
- DEFAULT_DUMMY_CRASHREPORT_VALUES.update({
- 'is_fake_report': 0,
- 'boot_reason': Crashreport.BOOT_REASON_UNKOWN,
- 'power_on_reason': 'it was powered on',
- 'power_off_reason': 'something happened and it went off',
- })
+ DEFAULT_DUMMY_CRASHREPORT_VALUES.update(
+ {
+ "is_fake_report": 0,
+ "boot_reason": Crashreport.BOOT_REASON_UNKOWN,
+ "power_on_reason": "it was powered on",
+ "power_off_reason": "something happened and it went off",
+ }
+ )
@staticmethod
def update_copy(original, update):
@@ -110,8 +117,9 @@
Returns: The created user instance.
"""
- entity = User(**Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_USER_VALUES, kwargs))
+ entity = User(
+ **Dummy.update_copy(Dummy.DEFAULT_DUMMY_USER_VALUES, kwargs)
+ )
entity.save()
return entity
@@ -128,8 +136,10 @@
Returns: The created device instance.
"""
- entity = Device(user=user, **Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_DEVICE_VALUES, kwargs))
+ entity = Device(
+ user=user,
+ **Dummy.update_copy(Dummy.DEFAULT_DUMMY_DEVICE_VALUES, kwargs)
+ )
entity.save()
return entity
@@ -148,15 +158,25 @@
"""
if report_type == HeartBeat:
- entity = HeartBeat(device=device, **Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_HEARTBEAT_VALUES, kwargs))
+ entity = HeartBeat(
+ device=device,
+ **Dummy.update_copy(
+ Dummy.DEFAULT_DUMMY_HEARTBEAT_VALUES, kwargs
+ )
+ )
elif report_type == Crashreport:
- entity = Crashreport(device=device, **Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_CRASHREPORT_VALUES, kwargs))
+ entity = Crashreport(
+ device=device,
+ **Dummy.update_copy(
+ Dummy.DEFAULT_DUMMY_CRASHREPORT_VALUES, kwargs
+ )
+ )
else:
raise RuntimeError(
- 'No dummy report instance can be created for {}'.format(
- report_type.__name__))
+ "No dummy report instance can be created for {}".format(
+ report_type.__name__
+ )
+ )
entity.save()
return entity
@@ -172,8 +192,9 @@
Returns: The created version instance.
"""
- entity = Version(**Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_VERSION_VALUES, kwargs))
+ entity = Version(
+ **Dummy.update_copy(Dummy.DEFAULT_DUMMY_VERSION_VALUES, kwargs)
+ )
entity.save()
return entity
@@ -189,8 +210,11 @@
Returns: The created radio version instance.
"""
- entity = RadioVersion(**Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_RADIO_VERSION_VALUES, kwargs))
+ entity = RadioVersion(
+ **Dummy.update_copy(
+ Dummy.DEFAULT_DUMMY_RADIO_VERSION_VALUES, kwargs
+ )
+ )
entity.save()
return entity
@@ -206,8 +230,12 @@
Returns: The created daily version instance.
"""
- entity = VersionDaily(version=version, **Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_VERSION_DAILY_VALUES, kwargs))
+ entity = VersionDaily(
+ version=version,
+ **Dummy.update_copy(
+ Dummy.DEFAULT_DUMMY_VERSION_DAILY_VALUES, kwargs
+ )
+ )
entity.save()
return entity
@@ -223,8 +251,12 @@
Returns: The created daily radio version instance.
"""
- entity = RadioVersionDaily(version=version, **Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_RADIO_VERSION_DAILY_VALUES, kwargs))
+ entity = RadioVersionDaily(
+ version=version,
+ **Dummy.update_copy(
+ Dummy.DEFAULT_DUMMY_RADIO_VERSION_DAILY_VALUES, kwargs
+ )
+ )
entity.save()
return entity
@@ -240,8 +272,11 @@
Returns: The created stats metadata instance.
"""
- entity = StatsMetadata(**Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_STATSMETADATA_VALUES, kwargs))
+ entity = StatsMetadata(
+ **Dummy.update_copy(
+ Dummy.DEFAULT_DUMMY_STATSMETADATA_VALUES, kwargs
+ )
+ )
entity.save()
return entity
@@ -251,11 +286,11 @@
# The attribute name characterising the unicity of a stats entry (the
# named identifier)
- unique_entry_name = 'build_fingerprint'
+ unique_entry_name = "build_fingerprint"
# The collection of unique entries to post
unique_entries = Dummy.BUILD_FINGERPRINTS
# The URL to retrieve the stats entries from
- endpoint_url = reverse('hiccup_stats_api_v1_versions')
+ endpoint_url = reverse("hiccup_stats_api_v1_versions")
@classmethod
def setUpTestData(cls): # noqa: N802
@@ -265,7 +300,8 @@
server is stored in self.admin.
"""
admin_user = User.objects.create_superuser(
- 'somebody', 'somebody@example.com', 'thepassword')
+ "somebody", "somebody@example.com", "thepassword"
+ )
cls.admin = APIClient()
cls.admin.force_authenticate(admin_user)
@@ -274,14 +310,14 @@
return Dummy.create_dummy_version(**kwargs)
def _get_with_params(self, url, params):
- return self.admin.get('{}?{}'.format(url, urlencode(params)))
+ return self.admin.get("{}?{}".format(url, urlencode(params)))
def _assert_result_length_is(self, response, count):
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertIn('results', response.data)
- self.assertIn('count', response.data)
- self.assertEqual(response.data['count'], count)
- self.assertEqual(len(response.data['results']), count)
+ self.assertIn("results", response.data)
+ self.assertIn("count", response.data)
+ self.assertEqual(response.data["count"], count)
+ self.assertEqual(len(response.data["results"]), count)
def _assert_device_owner_has_no_get_access(self, entries_url):
# Create a user and device
@@ -290,7 +326,7 @@
# Create authenticated client
user = APIClient()
- user.credentials(HTTP_AUTHORIZATION='Token ' + device.token)
+ user.credentials(HTTP_AUTHORIZATION="Token " + device.token)
# Try getting entries using the client
response = user.get(entries_url)
@@ -302,8 +338,10 @@
# Expect only the single matching result to be returned
self._assert_result_length_is(response, 1)
- self.assertEqual(response.data['results'][0][self.unique_entry_name],
- getattr(expected_result, self.unique_entry_name))
+ self.assertEqual(
+ response.data["results"][0][self.unique_entry_name],
+ getattr(expected_result, self.unique_entry_name),
+ )
class VersionTestCase(_VersionTestCase):
@@ -311,9 +349,7 @@
def _create_version_entities(self):
versions = [
- self._create_dummy_version(
- **{self.unique_entry_name: unique_entry}
- )
+ self._create_dummy_version(**{self.unique_entry_name: unique_entry})
for unique_entry in self.unique_entries
]
return versions
@@ -348,11 +384,11 @@
# List entities with filter
filter_params = {
- self.unique_entry_name: getattr(versions[0],
- self.unique_entry_name)
+ self.unique_entry_name: getattr(versions[0], self.unique_entry_name)
}
- self._assert_filter_result_matches(filter_params,
- expected_result=versions[0])
+ self._assert_filter_result_matches(
+ filter_params, expected_result=versions[0]
+ )
def test_filter_versions_by_release_type(self):
"""Test filtering versions by release type."""
@@ -361,11 +397,15 @@
i = 0
for is_official_release in True, False:
for is_beta_release in True, False:
- versions.append(self._create_dummy_version(**{
- 'is_official_release': is_official_release,
- 'is_beta_release': is_beta_release,
- self.unique_entry_name: self.unique_entries[i]
- }))
+ versions.append(
+ self._create_dummy_version(
+ **{
+ "is_official_release": is_official_release,
+ "is_beta_release": is_beta_release,
+ self.unique_entry_name: self.unique_entries[i],
+ }
+ )
+ )
i += 1
# # Listing all entities should return the correct result length
@@ -375,11 +415,12 @@
# List each of the entities with the matching filter params
for version in versions:
filter_params = {
- 'is_official_release': version.is_official_release,
- 'is_beta_release': version.is_beta_release
+ "is_official_release": version.is_official_release,
+ "is_beta_release": version.is_beta_release,
}
- self._assert_filter_result_matches(filter_params,
- expected_result=version)
+ self._assert_filter_result_matches(
+ filter_params, expected_result=version
+ )
def test_filter_versions_by_first_seen_date(self):
"""Test filtering versions by first seen date."""
@@ -394,18 +435,19 @@
self._assert_result_length_is(response, len(versions))
# Expect the single matching result to be returned
- filter_params = {'first_seen_after': Dummy.DATES[2]}
- self._assert_filter_result_matches(filter_params,
- expected_result=versions[0])
+ filter_params = {"first_seen_after": Dummy.DATES[2]}
+ self._assert_filter_result_matches(
+ filter_params, expected_result=versions[0]
+ )
# pylint: disable=too-many-ancestors
class RadioVersionTestCase(VersionTestCase):
"""Test the RadioVersion REST endpoint."""
- unique_entry_name = 'radio_version'
+ unique_entry_name = "radio_version"
unique_entries = Dummy.RADIO_VERSIONS
- endpoint_url = reverse('hiccup_stats_api_v1_radio_versions')
+ endpoint_url = reverse("hiccup_stats_api_v1_radio_versions")
@staticmethod
def _create_dummy_version(**kwargs):
@@ -415,7 +457,7 @@
class VersionDailyTestCase(_VersionTestCase):
"""Test the VersionDaily REST endpoint."""
- endpoint_url = reverse('hiccup_stats_api_v1_version_daily')
+ endpoint_url = reverse("hiccup_stats_api_v1_version_daily")
@staticmethod
def _create_dummy_daily_version(version, **kwargs):
@@ -423,9 +465,7 @@
def _create_version_entities(self):
versions = [
- self._create_dummy_version(
- **{self.unique_entry_name: unique_entry}
- )
+ self._create_dummy_version(**{self.unique_entry_name: unique_entry})
for unique_entry in self.unique_entries
]
versions_daily = [
@@ -464,12 +504,13 @@
self._assert_result_length_is(response, len(versions))
# List entities with filter
- param_name = 'version__' + self.unique_entry_name
+ param_name = "version__" + self.unique_entry_name
filter_params = {
param_name: getattr(versions[0].version, self.unique_entry_name)
}
- self._assert_filter_result_matches(filter_params,
- expected_result=versions[0].version)
+ self._assert_filter_result_matches(
+ filter_params, expected_result=versions[0].version
+ )
def test_filter_daily_versions_by_date(self):
"""Test filtering daily versions by date."""
@@ -485,22 +526,26 @@
self._assert_result_length_is(response, len(versions))
# Expect the single matching result to be returned
- filter_params = {'date': versions[0].date}
- self._assert_filter_result_matches(filter_params,
- expected_result=versions[0].version)
+ filter_params = {"date": versions[0].date}
+ self._assert_filter_result_matches(
+ filter_params, expected_result=versions[0].version
+ )
class RadioVersionDailyTestCase(VersionDailyTestCase):
"""Test the RadioVersionDaily REST endpoint."""
- unique_entry_name = 'radio_version'
+ unique_entry_name = "radio_version"
unique_entries = Dummy.RADIO_VERSIONS
- endpoint_url = reverse('hiccup_stats_api_v1_radio_version_daily')
+ endpoint_url = reverse("hiccup_stats_api_v1_radio_version_daily")
@staticmethod
def _create_dummy_version(**kwargs):
- entity = RadioVersion(**Dummy.update_copy(
- Dummy.DEFAULT_DUMMY_RADIO_VERSION_VALUES, kwargs))
+ entity = RadioVersion(
+ **Dummy.update_copy(
+ Dummy.DEFAULT_DUMMY_RADIO_VERSION_VALUES, kwargs
+ )
+ )
entity.save()
return entity
@@ -523,20 +568,21 @@
version_class = Version
# The attribute name characterising the unicity of a stats entry (the
# named identifier)
- unique_entry_name = 'build_fingerprint'
+ unique_entry_name = "build_fingerprint"
# The collection of unique entries to post
unique_entries = Dummy.BUILD_FINGERPRINTS
- def _create_reports(self, report_type, unique_entry_name, device,
- number, **kwargs):
+ def _create_reports(
+ self, report_type, unique_entry_name, device, number, **kwargs
+ ):
# Create reports with distinct timestamps
now = datetime.now(pytz.utc)
for i in range(number):
report_date = now - timedelta(milliseconds=i)
report_attributes = {
self.unique_entry_name: unique_entry_name,
- 'device': device,
- 'date': report_date
+ "device": device,
+ "date": report_date,
}
report_attributes.update(**kwargs)
Dummy.create_dummy_report(report_type, **report_attributes)
@@ -551,11 +597,14 @@
get_params = {
self.unique_entry_name: getattr(heartbeat, self.unique_entry_name)
}
- self.assertRaises(self.version_class.DoesNotExist,
- self.version_class.objects.get, **get_params)
+ self.assertRaises(
+ self.version_class.DoesNotExist,
+ self.version_class.objects.get,
+ **get_params
+ )
# Run the command to update the database
- call_command('stats', 'update')
+ call_command("stats", "update")
# Assume that a corresponding Version instance has been created
version = self.version_class.objects.get(**get_params)
@@ -568,7 +617,7 @@
report = Dummy.create_dummy_report(report_type, device=device)
# Run the command to update the database
- call_command('stats', 'update')
+ call_command("stats", "update")
get_params = {
self.unique_entry_name: getattr(report, self.unique_entry_name)
@@ -579,11 +628,12 @@
# Create a new report from an earlier point in time
report_time_2 = report.date - timedelta(weeks=1)
- Dummy.create_dummy_report(report_type, device=device,
- date=report_time_2)
+ Dummy.create_dummy_report(
+ report_type, device=device, date=report_time_2
+ )
# Run the command to update the database
- call_command('stats', 'update')
+ call_command("stats", "update")
# Get the same version object from before
version = self.version_class.objects.get(**get_params)
@@ -608,34 +658,37 @@
self._create_reports(HeartBeat, unique_entry, device, 10)
# Run the command to update the database
- call_command('stats', 'update')
+ call_command("stats", "update")
# Check whether the correct amount of distinct versions have been
# created
versions = self.version_class.objects.all()
for version in versions:
- self.assertIn(getattr(version, self.unique_entry_name),
- self.unique_entries)
+ self.assertIn(
+ getattr(version, self.unique_entry_name), self.unique_entries
+ )
self.assertEqual(len(versions), len(self.unique_entries))
- def _assert_counter_distribution_is_correct(self, report_type, numbers,
- counter_attribute_name,
- **kwargs):
+ def _assert_counter_distribution_is_correct(
+ self, report_type, numbers, counter_attribute_name, **kwargs
+ ):
"""Validate a counter distribution in the database."""
if len(numbers) != len(self.unique_entries):
- raise ValueError('The length of the numbers list must match the '
- 'length of self.unique_entries in the test class'
- '({} != {})'.format(len(numbers),
- len(self.unique_entries)))
+ raise ValueError(
+ "The length of the numbers list must match the "
+ "length of self.unique_entries in the test class"
+ "({} != {})".format(len(numbers), len(self.unique_entries))
+ )
# Create some reports
user = Dummy.create_dummy_user()
device = Dummy.create_dummy_device(user=user)
for unique_entry, num in zip(self.unique_entries, numbers):
- self._create_reports(report_type, unique_entry, device, num,
- **kwargs)
+ self._create_reports(
+ report_type, unique_entry, device, num, **kwargs
+ )
# Run the command to update the database
- call_command('stats', 'update')
+ call_command("stats", "update")
# Check whether the numbers of reports match
for version in self.version_class.objects.all():
@@ -646,50 +699,52 @@
def test_heartbeats_counter(self):
"""Test the calculation of the heartbeats counter."""
numbers = [10, 7, 8, 5]
- counter_attribute_name = 'heartbeats'
- self._assert_counter_distribution_is_correct(HeartBeat, numbers,
- counter_attribute_name)
+ counter_attribute_name = "heartbeats"
+ self._assert_counter_distribution_is_correct(
+ HeartBeat, numbers, counter_attribute_name
+ )
def test_crash_reports_counter(self):
"""Test the calculation of the crashreports counter."""
numbers = [2, 5, 0, 3]
- counter_attribute_name = 'prob_crashes'
- boot_reason_param = {'boot_reason': Crashreport.BOOT_REASON_UNKOWN}
- self._assert_counter_distribution_is_correct(Crashreport, numbers,
- counter_attribute_name,
- **boot_reason_param)
+ counter_attribute_name = "prob_crashes"
+ boot_reason_param = {"boot_reason": Crashreport.BOOT_REASON_UNKOWN}
+ self._assert_counter_distribution_is_correct(
+ Crashreport, numbers, counter_attribute_name, **boot_reason_param
+ )
def test_smpl_reports_counter(self):
"""Test the calculation of the smpl reports counter."""
numbers = [1, 3, 4, 0]
- counter_attribute_name = 'smpl'
- boot_reason_param = {'boot_reason': Crashreport.BOOT_REASON_RTC_ALARM}
- self._assert_counter_distribution_is_correct(Crashreport, numbers,
- counter_attribute_name,
- **boot_reason_param)
+ counter_attribute_name = "smpl"
+ boot_reason_param = {"boot_reason": Crashreport.BOOT_REASON_RTC_ALARM}
+ self._assert_counter_distribution_is_correct(
+ Crashreport, numbers, counter_attribute_name, **boot_reason_param
+ )
def test_other_reports_counter(self):
"""Test the calculation of the other reports counter."""
numbers = [0, 2, 1, 2]
- counter_attribute_name = 'other'
- boot_reason_param = {'boot_reason': "random boot reason"}
- self._assert_counter_distribution_is_correct(Crashreport, numbers,
- counter_attribute_name,
- **boot_reason_param)
+ counter_attribute_name = "other"
+ boot_reason_param = {"boot_reason": "random boot reason"}
+ self._assert_counter_distribution_is_correct(
+ Crashreport, numbers, counter_attribute_name, **boot_reason_param
+ )
- def _assert_duplicates_are_ignored(self, report_type, device,
- counter_attribute_name, **kwargs):
+ def _assert_duplicates_are_ignored(
+ self, report_type, device, counter_attribute_name, **kwargs
+ ):
"""Validate that reports with duplicate timestamps are ignored."""
# Create a report
- report = Dummy.create_dummy_report(report_type, device=device,
- **kwargs)
+ report = Dummy.create_dummy_report(report_type, device=device, **kwargs)
# Create a second report with the same timestamp
- Dummy.create_dummy_report(report_type, device=device,
- date=report.date, **kwargs)
+ Dummy.create_dummy_report(
+ report_type, device=device, date=report.date, **kwargs
+ )
# Run the command to update the database
- call_command('stats', 'update')
+ call_command("stats", "update")
# Get the corresponding version instance from the database
get_params = {
@@ -703,41 +758,46 @@
def test_heartbeat_duplicates_are_ignored(self):
"""Validate that heartbeat duplicates are ignored."""
- counter_attribute_name = 'heartbeats'
+ counter_attribute_name = "heartbeats"
device = Dummy.create_dummy_device(user=Dummy.create_dummy_user())
- self._assert_duplicates_are_ignored(HeartBeat, device,
- counter_attribute_name)
+ self._assert_duplicates_are_ignored(
+ HeartBeat, device, counter_attribute_name
+ )
def test_crash_report_duplicates_are_ignored(self):
"""Validate that crash report duplicates are ignored."""
- counter_attribute_name = 'prob_crashes'
+ counter_attribute_name = "prob_crashes"
device = Dummy.create_dummy_device(user=Dummy.create_dummy_user())
for i, boot_reason in enumerate(Crashreport.CRASH_BOOT_REASONS):
- params = {'boot_reason': boot_reason,
- self.unique_entry_name: self.unique_entries[i]}
- self._assert_duplicates_are_ignored(Crashreport, device,
- counter_attribute_name,
- **params)
+ params = {
+ "boot_reason": boot_reason,
+ self.unique_entry_name: self.unique_entries[i],
+ }
+ self._assert_duplicates_are_ignored(
+ Crashreport, device, counter_attribute_name, **params
+ )
def test_smpl_report_duplicates_are_ignored(self):
"""Validate that smpl report duplicates are ignored."""
- counter_attribute_name = 'smpl'
+ counter_attribute_name = "smpl"
device = Dummy.create_dummy_device(user=Dummy.create_dummy_user())
for i, boot_reason in enumerate(Crashreport.SMPL_BOOT_REASONS):
- params = {'boot_reason': boot_reason,
- self.unique_entry_name: self.unique_entries[i]}
- self._assert_duplicates_are_ignored(Crashreport, device,
- counter_attribute_name,
- **params)
+ params = {
+ "boot_reason": boot_reason,
+ self.unique_entry_name: self.unique_entries[i],
+ }
+ self._assert_duplicates_are_ignored(
+ Crashreport, device, counter_attribute_name, **params
+ )
def test_other_report_duplicates_are_ignored(self):
"""Validate that other report duplicates are ignored."""
- counter_attribute_name = 'other'
- params = {'boot_reason': 'random boot reason'}
+ counter_attribute_name = "other"
+ params = {"boot_reason": "random boot reason"}
device = Dummy.create_dummy_device(user=Dummy.create_dummy_user())
- self._assert_duplicates_are_ignored(Crashreport, device,
- counter_attribute_name,
- **params)
+ self._assert_duplicates_are_ignored(
+ Crashreport, device, counter_attribute_name, **params
+ )
# pylint: disable=too-many-ancestors
@@ -745,7 +805,7 @@
"""Test the generation of RadioVersion stats with the stats command."""
version_class = RadioVersion
- unique_entry_name = 'radio_version'
+ unique_entry_name = "radio_version"
unique_entries = Dummy.RADIO_VERSIONS
@@ -753,17 +813,14 @@
"""Test the reset and update commands debug output."""
# Additional positional arguments to pass to the commands
- _CMD_ARGS = [
- '--no-color',
- '-v 2',
- ]
+ _CMD_ARGS = ["--no-color", "-v 2"]
# The stats models
_STATS_MODELS = [Version, VersionDaily, RadioVersion, RadioVersionDaily]
# The models that will generate an output
_ALL_MODELS = _STATS_MODELS + [StatsMetadata]
- _COUNTER_NAMES = ['heartbeats', 'crashes', 'smpl', 'other']
- _COUNTER_ACTIONS = ['created', 'updated']
+ _COUNTER_NAMES = ["heartbeats", "crashes", "smpl", "other"]
+ _COUNTER_ACTIONS = ["created", "updated"]
def _assert_command_output_matches(self, command, number, facts, models):
"""Validate the debug output of a command.
@@ -772,38 +829,42 @@
the parameters.
"""
buffer = StringIO()
- call_command('stats', command, *self._CMD_ARGS, stdout=buffer)
+ call_command("stats", command, *self._CMD_ARGS, stdout=buffer)
output = buffer.getvalue().splitlines()
- expected_output = '{number} {model} {fact}'
+ expected_output = "{number} {model} {fact}"
for model in models:
for fact in facts:
self.assertIn(
- expected_output.format(number=number,
- model=model.__name__,
- fact=fact),
- output)
+ expected_output.format(
+ number=number, model=model.__name__, fact=fact
+ ),
+ output,
+ )
def test_reset_command_on_empty_db(self):
"""Test the reset command on an empty database.
The reset command should yield nothing on an empty database.
"""
- self._assert_command_output_matches('reset', 0, ['deleted'],
- self._ALL_MODELS)
+ self._assert_command_output_matches(
+ "reset", 0, ["deleted"], self._ALL_MODELS
+ )
def test_update_command_on_empty_db(self):
"""Test the update command on an empty database.
The update command should yield nothing on an empty database.
"""
- pattern = '{action} for counter {counter}'
+ pattern = "{action} for counter {counter}"
facts = [
pattern.format(action=counter_action, counter=counter_name)
for counter_action in self._COUNTER_ACTIONS
- for counter_name in self._COUNTER_NAMES]
- self._assert_command_output_matches('update', 0, facts,
- self._STATS_MODELS)
+ for counter_name in self._COUNTER_NAMES
+ ]
+ self._assert_command_output_matches(
+ "update", 0, facts, self._STATS_MODELS
+ )
def test_reset_command_deletion_of_instances(self):
"""Test the deletion of stats model instances with the reset command.
@@ -820,5 +881,6 @@
Dummy.create_dummy_stats_metadata()
# We expect that the model instances get deleted
- self._assert_command_output_matches('reset', 1, ['deleted'],
- self._ALL_MODELS)
+ self._assert_command_output_matches(
+ "reset", 1, ["deleted"], self._ALL_MODELS
+ )
diff --git a/crashreport_stats/urls.py b/crashreport_stats/urls.py
index 54c70db..cedbd73 100644
--- a/crashreport_stats/urls.py
+++ b/crashreport_stats/urls.py
@@ -4,46 +4,57 @@
urlpatterns = [
- url(r'^device/$',
- views.device_stats,
- name='hiccup_stats_device'),
- url(r'^$',
- views.home,
- name='device'),
- url(r'^versions/$',
- views.versions_overview,
- name='hiccup_stats_versions'),
- url(r'^versions/all/$',
+ url(r"^device/$", views.device_stats, name="hiccup_stats_device"),
+ url(r"^$", views.home, name="device"),
+ url(r"^versions/$", views.versions_overview, name="hiccup_stats_versions"),
+ url(
+ r"^versions/all/$",
views.versions_all_overview,
- name='hiccup_stats_versions_all'),
- url(r'^api/v1/device_overview/(?P<uuid>[a-f0-9-]+)/$',
+ name="hiccup_stats_versions_all",
+ ),
+ url(
+ r"^api/v1/device_overview/(?P<uuid>[a-f0-9-]+)/$",
rest_endpoints.DeviceStat.as_view(),
- name='hiccup_stats_api_v1_device_overview'),
- url(r'^api/v1/status/$',
+ name="hiccup_stats_api_v1_device_overview",
+ ),
+ url(
+ r"^api/v1/status/$",
rest_endpoints.Status.as_view(),
- name='hiccup_stats_api_v1_status'),
- url(r'^api/v1/device_update_history/(?P<uuid>[a-f0-9-]+)/$',
+ name="hiccup_stats_api_v1_status",
+ ),
+ url(
+ r"^api/v1/device_update_history/(?P<uuid>[a-f0-9-]+)/$",
rest_endpoints.DeviceUpdateHistory.as_view(),
- name='hiccup_stats_api_v1_device_update_history'),
- url(r'^api/v1/device_report_history/(?P<uuid>[a-f0-9-]+)/$',
+ name="hiccup_stats_api_v1_device_update_history",
+ ),
+ url(
+ r"^api/v1/device_report_history/(?P<uuid>[a-f0-9-]+)/$",
rest_endpoints.DeviceReportHistory.as_view(),
- name='hiccup_stats_api_v1_device_report_history'),
-
- url(r'^api/v1/logfile_download/(?P<id>[0-9]+)/$',
+ name="hiccup_stats_api_v1_device_report_history",
+ ),
+ url(
+ r"^api/v1/logfile_download/(?P<id>[0-9]+)/$",
rest_endpoints.LogFileDownload.as_view(),
- name='hiccup_stats_api_v1_logfile_download'),
-
- url(r'^api/v1/versions/$',
+ name="hiccup_stats_api_v1_logfile_download",
+ ),
+ url(
+ r"^api/v1/versions/$",
rest_endpoints.VersionListView.as_view(),
- name='hiccup_stats_api_v1_versions'),
- url(r'^api/v1/version_daily/$',
+ name="hiccup_stats_api_v1_versions",
+ ),
+ url(
+ r"^api/v1/version_daily/$",
rest_endpoints.VersionDailyListView.as_view(),
- name='hiccup_stats_api_v1_version_daily'),
-
- url(r'^api/v1/radio_versions/$',
+ name="hiccup_stats_api_v1_version_daily",
+ ),
+ url(
+ r"^api/v1/radio_versions/$",
rest_endpoints.RadioVersionListView.as_view(),
- name='hiccup_stats_api_v1_radio_versions'),
- url(r'^api/v1/radio_version_daily/$',
+ name="hiccup_stats_api_v1_radio_versions",
+ ),
+ url(
+ r"^api/v1/radio_version_daily/$",
rest_endpoints.RadioVersionDailyListView.as_view(),
- name='hiccup_stats_api_v1_radio_version_daily'),
+ name="hiccup_stats_api_v1_radio_version_daily",
+ ),
]
diff --git a/crashreport_stats/util.py b/crashreport_stats/util.py
index 316ab53..a2111f7 100644
--- a/crashreport_stats/util.py
+++ b/crashreport_stats/util.py
@@ -8,18 +8,19 @@
from django.db import transaction
+
def dictfetchall(cursor):
"Returns all rows from a cursor as a dict"
desc = cursor.description
return [
- dict(zip([col[0] for col in desc], row))
- for row in cursor.fetchall()
+ dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall()
]
+
@transaction.atomic
def fill_version_data():
myModels.Version.objects.all().delete()
- query = '''
+ query = """
SELECT fingerprint as build_fingerprint,
( select count(id) from crashreports_crashreport where boot_reason in ("RTC alarm") and crashreports_crashreport.build_fingerprint = fingerprint) as SMPL,
( select count(id) from crashreports_crashreport where boot_reason in ("UNKNOWN", "keyboard power on") and crashreports_crashreport.build_fingerprint = fingerprint) as prob_crashes,
@@ -27,54 +28,61 @@
( select count(id) from crashreports_heartbeat where crashreports_heartbeat.build_fingerprint = fingerprint) as heartbeats,
( select min(crashreports_heartbeat.created_at) from crashreports_heartbeat where crashreports_heartbeat.build_fingerprint = fingerprint) as first_seen
from (select distinct(build_fingerprint) as fingerprint
- from crashreports_heartbeat) group by fingerprint order by heartbeats;'''
+ from crashreports_heartbeat) group by fingerprint order by heartbeats;"""
cursor = connection.cursor()
- cursor.execute(query,[])
+ cursor.execute(query, [])
desc = cursor.description
for row in cursor.fetchall():
i = dict(zip([col[0] for col in desc], row))
version = myModels.Version(
- build_fingerprint = i['build_fingerprint'],
- first_seen_on = i['first_seen'].split()[0],
- released_on = i['first_seen'].split()[0],
- heartbeats= i['heartbeats'],
- prob_crashes = i['prob_crashes'],
- smpl = i['SMPL'],
- other = i['other']
+ build_fingerprint=i["build_fingerprint"],
+ first_seen_on=i["first_seen"].split()[0],
+ released_on=i["first_seen"].split()[0],
+ heartbeats=i["heartbeats"],
+ prob_crashes=i["prob_crashes"],
+ smpl=i["SMPL"],
+ other=i["other"],
)
version.save()
+
@transaction.atomic
def fill_version_daily_data():
myModels.VersionDaily.objects.all().delete()
- query = '''
+ query = """
SELECT build_fingerprint, count(id) as heartbeats,
strftime("%%Y-%%m-%%d",crashreports_heartbeat.date) as date,
( select count(id) from crashreports_crashreport where boot_reason in ("RTC alarm") and crashreports_crashreport.build_fingerprint = crashreports_heartbeat.build_fingerprint and crashreports_crashreport.date >= %s and crashreports_crashreport.date < %s) as SMPL,
( select count(id) from crashreports_crashreport where boot_reason in ("UNKNOWN", "keyboard power on") and crashreports_crashreport.build_fingerprint = crashreports_heartbeat.build_fingerprint and crashreports_crashreport.date >= %s and crashreports_crashreport.date < %s) as prob_crashes,
( select count(id) from crashreports_crashreport where boot_reason not in ("RTC alarm", "UNKNOWN", "keyboard power on") and crashreports_crashreport.build_fingerprint = crashreports_heartbeat.build_fingerprint and crashreports_crashreport.date >= %s and crashreports_crashreport.date < %s) as other
from crashreports_heartbeat where crashreports_heartbeat.date >= %s and crashreports_heartbeat.date < %s
- group by build_fingerprint'''
+ group by build_fingerprint"""
start = date(2016, 8, 1)
- end = date.today() + timedelta(days=5)
+ end = date.today() + timedelta(days=5)
delta = end - start
for d in range(delta.days + 1):
day = start + timedelta(days=d)
print("Getting Stats for " + str(day))
cursor = connection.cursor()
- cursor.execute(query,[str(day), str(day+timedelta(days=1))]*4)
+ cursor.execute(query, [str(day), str(day + timedelta(days=1))] * 4)
desc = cursor.description
for row in cursor.fetchall():
i = dict(zip([col[0] for col in desc], row))
try:
version_daily = myModels.VersionDaily(
- version = myModels.Version.objects.get(build_fingerprint=i['build_fingerprint']),
- heartbeats= i['heartbeats'],
+ version=myModels.Version.objects.get(
+ build_fingerprint=i["build_fingerprint"]
+ ),
+ heartbeats=i["heartbeats"],
date=day,
- prob_crashes = i['prob_crashes'],
- smpl = i['SMPL'],
- other = i['other']
+ prob_crashes=i["prob_crashes"],
+ smpl=i["SMPL"],
+ other=i["other"],
)
except:
- print("Skipping entry for {} {}".format(i['build_fingerprint'],day))
+ print(
+ "Skipping entry for {} {}".format(
+ i["build_fingerprint"], day
+ )
+ )
version_daily.save()
diff --git a/crashreport_stats/views.py b/crashreport_stats/views.py
index 9ec7dc6..54a67e7 100644
--- a/crashreport_stats/views.py
+++ b/crashreport_stats/views.py
@@ -1,5 +1,5 @@
from crashreports.models import *
-from django.http import HttpResponse,Http404,HttpResponseRedirect
+from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import render
from django.shortcuts import render_to_response
from django.template import loader
@@ -11,53 +11,62 @@
from django.contrib import messages
from django.urls import reverse
+
def is_fairphone_staff(user):
- return user.groups.filter(name='FairphoneSoftwareTeam').exists()
+ return user.groups.filter(name="FairphoneSoftwareTeam").exists()
class DeviceUUIDForm(forms.Form):
- uuid = forms.CharField(label='Device UUID:', max_length=100)
+ uuid = forms.CharField(label="Device UUID:", max_length=100)
+
@user_passes_test(is_fairphone_staff)
def device_stats(request):
- template = loader.get_template('crashreport_stats/device.html')
- uuid = request.GET.get('uuid', "NO_UUID")
+ template = loader.get_template("crashreport_stats/device.html")
+ uuid = request.GET.get("uuid", "NO_UUID")
if not Device.objects.filter(uuid=uuid).exists():
raise Http404("Device doesn't exist.")
- return HttpResponse(template.render({'uuid':uuid}, request))
+ return HttpResponse(template.render({"uuid": uuid}, request))
+
@user_passes_test(is_fairphone_staff)
def versions_all_overview(request):
- template = loader.get_template('crashreport_stats/versions.html')
- return HttpResponse(template.render({"is_official_release":"1"}, request))
+ template = loader.get_template("crashreport_stats/versions.html")
+ return HttpResponse(template.render({"is_official_release": "1"}, request))
+
@user_passes_test(is_fairphone_staff)
def versions_overview(request):
- template = loader.get_template('crashreport_stats/versions.html')
- return HttpResponse(template.render({"is_official_release":"2"}, request))
+ template = loader.get_template("crashreport_stats/versions.html")
+ return HttpResponse(template.render({"is_official_release": "2"}, request))
+
@user_passes_test(is_fairphone_staff)
def home(request):
""" The home view allows to search for devices. """
devices = None
- if request.method == 'POST':
+ if request.method == "POST":
# create a form instance and populate it with data from the request:
form = DeviceUUIDForm(request.POST)
if form.is_valid():
- uuid = form.cleaned_data['uuid']
+ uuid = form.cleaned_data["uuid"]
if not Device.objects.filter(uuid=uuid).exists():
devices = Device.objects.filter(uuid__startswith=uuid)
- if len(devices)==1:
+ if len(devices) == 1:
return HttpResponseRedirect(
- reverse("hiccup_stats_device")+'?uuid='+devices[0].uuid)
- elif len(devices)==0:
- messages.warning(request, "No devices found.")
+ reverse("hiccup_stats_device")
+ + "?uuid="
+ + devices[0].uuid
+ )
+ elif len(devices) == 0:
+ messages.warning(request, "No devices found.")
else:
return HttpResponseRedirect(
- reverse("hiccup_stats_device")+'?uuid='+uuid)
+ reverse("hiccup_stats_device") + "?uuid=" + uuid
+ )
else:
form = DeviceUUIDForm()
- template = loader.get_template('crashreport_stats/home.html')
- return HttpResponse(template.render(
- {'form':form, 'devices':devices}
- , request))
+ template = loader.get_template("crashreport_stats/home.html")
+ return HttpResponse(
+ template.render({"form": form, "devices": devices}, request)
+ )
diff --git a/crashreports/admin.py b/crashreports/admin.py
index 7133070..97b32e0 100644
--- a/crashreports/admin.py
+++ b/crashreports/admin.py
@@ -8,15 +8,18 @@
@admin.register(Crashreport)
class CrashreportAdmin(admin.ModelAdmin):
pass
-
+
+
@admin.register(HeartBeat)
class CrashreportAdmin(admin.ModelAdmin):
pass
+
@admin.register(LogFile)
class CrashreportAdmin(admin.ModelAdmin):
pass
+
@admin.register(Device)
class CrashreportAdmin(admin.ModelAdmin):
pass
diff --git a/crashreports/apps.py b/crashreports/apps.py
index d651203..dacae51 100644
--- a/crashreports/apps.py
+++ b/crashreports/apps.py
@@ -4,4 +4,4 @@
class crashreportsConfig(AppConfig):
- name = 'crashreports'
+ name = "crashreports"
diff --git a/crashreports/migrations/0001_initial.py b/crashreports/migrations/0001_initial.py
index effb757..4542b4b 100644
--- a/crashreports/migrations/0001_initial.py
+++ b/crashreports/migrations/0001_initial.py
@@ -16,75 +16,179 @@
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('taggit', '0002_auto_20150616_2121'),
+ ("taggit", "0002_auto_20150616_2121"),
]
operations = [
migrations.CreateModel(
- name='Crashreport',
+ name="Crashreport",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('is_fake_report', models.BooleanField(default=False)),
- ('app_version', models.IntegerField()),
- ('uptime', models.CharField(max_length=200)),
- ('build_fingerprint', models.CharField(max_length=200)),
- ('boot_reason', models.CharField(max_length=200)),
- ('power_on_reason', models.CharField(max_length=200)),
- ('power_off_reason', models.CharField(max_length=200)),
- ('date', models.DateTimeField()),
- ('device_local_id', models.PositiveIntegerField(blank=True)),
- ('next_logfile_key', models.PositiveIntegerField(default=1)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("is_fake_report", models.BooleanField(default=False)),
+ ("app_version", models.IntegerField()),
+ ("uptime", models.CharField(max_length=200)),
+ ("build_fingerprint", models.CharField(max_length=200)),
+ ("boot_reason", models.CharField(max_length=200)),
+ ("power_on_reason", models.CharField(max_length=200)),
+ ("power_off_reason", models.CharField(max_length=200)),
+ ("date", models.DateTimeField()),
+ ("device_local_id", models.PositiveIntegerField(blank=True)),
+ ("next_logfile_key", models.PositiveIntegerField(default=1)),
+ ("created_at", models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
- name='Device',
+ name="Device",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('uuid', models.CharField(default=uuid.uuid4, editable=False, max_length=64, unique=True)),
- ('imei', models.CharField(blank=True, max_length=32, null=True)),
- ('board_date', models.DateTimeField(blank=True, null=True)),
- ('chipset', models.CharField(blank=True, max_length=200, null=True)),
- ('last_heartbeat', models.DateTimeField(blank=True, null=True)),
- ('token', models.CharField(blank=True, max_length=200, null=True)),
- ('next_per_crashreport_key', models.PositiveIntegerField(default=1)),
- ('next_per_heartbeat_key', models.PositiveIntegerField(default=1)),
- ('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
- ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='Hiccup_Device', to=settings.AUTH_USER_MODEL)),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "uuid",
+ models.CharField(
+ default=uuid.uuid4,
+ editable=False,
+ max_length=64,
+ unique=True,
+ ),
+ ),
+ (
+ "imei",
+ models.CharField(blank=True, max_length=32, null=True),
+ ),
+ ("board_date", models.DateTimeField(blank=True, null=True)),
+ (
+ "chipset",
+ models.CharField(blank=True, max_length=200, null=True),
+ ),
+ ("last_heartbeat", models.DateTimeField(blank=True, null=True)),
+ (
+ "token",
+ models.CharField(blank=True, max_length=200, null=True),
+ ),
+ (
+ "next_per_crashreport_key",
+ models.PositiveIntegerField(default=1),
+ ),
+ (
+ "next_per_heartbeat_key",
+ models.PositiveIntegerField(default=1),
+ ),
+ (
+ "tags",
+ taggit.managers.TaggableManager(
+ blank=True,
+ help_text="A comma-separated list of tags.",
+ through="taggit.TaggedItem",
+ to="taggit.Tag",
+ verbose_name="Tags",
+ ),
+ ),
+ (
+ "user",
+ models.OneToOneField(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="Hiccup_Device",
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
],
),
migrations.CreateModel(
- name='HeartBeat',
+ name="HeartBeat",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('app_version', models.IntegerField()),
- ('uptime', models.CharField(max_length=200)),
- ('build_fingerprint', models.CharField(max_length=200)),
- ('date', models.DateTimeField()),
- ('device_local_id', models.PositiveIntegerField(blank=True)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crashreports.Device')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("app_version", models.IntegerField()),
+ ("uptime", models.CharField(max_length=200)),
+ ("build_fingerprint", models.CharField(max_length=200)),
+ ("date", models.DateTimeField()),
+ ("device_local_id", models.PositiveIntegerField(blank=True)),
+ ("created_at", models.DateTimeField(auto_now_add=True)),
+ (
+ "device",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="crashreports.Device",
+ ),
+ ),
],
),
migrations.CreateModel(
- name='LogFile',
+ name="LogFile",
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('logfile_type', models.TextField(default='last_kmsg', max_length=36)),
- ('logfile', models.FileField(max_length=500, upload_to=crashreports.models.crashreport_file_name)),
- ('crashreport_local_id', models.PositiveIntegerField(blank=True)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('crashreport', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crashreports.Crashreport')),
+ (
+ "id",
+ models.AutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "logfile_type",
+ models.TextField(default="last_kmsg", max_length=36),
+ ),
+ (
+ "logfile",
+ models.FileField(
+ max_length=500,
+ upload_to=crashreports.models.crashreport_file_name,
+ ),
+ ),
+ (
+ "crashreport_local_id",
+ models.PositiveIntegerField(blank=True),
+ ),
+ ("created_at", models.DateTimeField(auto_now_add=True)),
+ (
+ "crashreport",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="crashreports.Crashreport",
+ ),
+ ),
],
),
migrations.AddField(
- model_name='crashreport',
- name='device',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crashreports.Device'),
+ model_name="crashreport",
+ name="device",
+ field=models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to="crashreports.Device",
+ ),
),
migrations.AddField(
- model_name='crashreport',
- name='tags',
- field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
+ model_name="crashreport",
+ name="tags",
+ field=taggit.managers.TaggableManager(
+ blank=True,
+ help_text="A comma-separated list of tags.",
+ through="taggit.TaggedItem",
+ to="taggit.Tag",
+ verbose_name="Tags",
+ ),
),
]
diff --git a/crashreports/migrations/0002_auto_20170502_1155.py b/crashreports/migrations/0002_auto_20170502_1155.py
index 38ee8db..e6e520c 100644
--- a/crashreports/migrations/0002_auto_20170502_1155.py
+++ b/crashreports/migrations/0002_auto_20170502_1155.py
@@ -9,64 +9,80 @@
class Migration(migrations.Migration):
- dependencies = [
- ('crashreports', '0001_initial'),
- ]
+ dependencies = [("crashreports", "0001_initial")]
operations = [
migrations.AlterField(
- model_name='crashreport',
- name='boot_reason',
+ model_name="crashreport",
+ name="boot_reason",
field=models.CharField(db_index=True, max_length=200),
),
migrations.AlterField(
- model_name='crashreport',
- name='build_fingerprint',
+ model_name="crashreport",
+ name="build_fingerprint",
field=models.CharField(db_index=True, max_length=200),
),
migrations.AlterField(
- model_name='crashreport',
- name='date',
+ model_name="crashreport",
+ name="date",
field=models.DateTimeField(db_index=True),
),
migrations.AlterField(
- model_name='crashreport',
- name='device',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='crashreports', to='crashreports.Device'),
+ model_name="crashreport",
+ name="device",
+ field=models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="crashreports",
+ to="crashreports.Device",
+ ),
),
migrations.AlterField(
- model_name='crashreport',
- name='power_off_reason',
+ model_name="crashreport",
+ name="power_off_reason",
field=models.CharField(db_index=True, max_length=200),
),
migrations.AlterField(
- model_name='crashreport',
- name='power_on_reason',
+ model_name="crashreport",
+ name="power_on_reason",
field=models.CharField(db_index=True, max_length=200),
),
migrations.AlterField(
- model_name='device',
- name='uuid',
- field=models.CharField(db_index=True, default=uuid.uuid4, editable=False, max_length=64, unique=True),
+ model_name="device",
+ name="uuid",
+ field=models.CharField(
+ db_index=True,
+ default=uuid.uuid4,
+ editable=False,
+ max_length=64,
+ unique=True,
+ ),
),
migrations.AlterField(
- model_name='heartbeat',
- name='build_fingerprint',
+ model_name="heartbeat",
+ name="build_fingerprint",
field=models.CharField(db_index=True, max_length=200),
),
migrations.AlterField(
- model_name='heartbeat',
- name='date',
+ model_name="heartbeat",
+ name="date",
field=models.DateTimeField(db_index=True),
),
migrations.AlterField(
- model_name='heartbeat',
- name='device',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='heartbeats', to='crashreports.Device'),
+ model_name="heartbeat",
+ name="device",
+ field=models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="heartbeats",
+ to="crashreports.Device",
+ ),
),
migrations.AlterField(
- model_name='logfile',
- name='crashreport',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logfiles', to='crashreports.Crashreport'),
+ model_name="logfile",
+ name="crashreport",
+ field=models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="logfiles",
+ to="crashreports.Crashreport",
+ ),
),
]
diff --git a/crashreports/migrations/0003_crashreport_and_heartbeat_with_radio_version.py b/crashreports/migrations/0003_crashreport_and_heartbeat_with_radio_version.py
index 564cc0e..403593e 100644
--- a/crashreports/migrations/0003_crashreport_and_heartbeat_with_radio_version.py
+++ b/crashreports/migrations/0003_crashreport_and_heartbeat_with_radio_version.py
@@ -8,19 +8,17 @@
class Migration(migrations.Migration):
- dependencies = [
- ('crashreports', '0002_auto_20170502_1155'),
- ]
+ dependencies = [("crashreports", "0002_auto_20170502_1155")]
operations = [
migrations.AddField(
- model_name='crashreport',
- name='radio_version',
+ model_name="crashreport",
+ name="radio_version",
field=models.CharField(db_index=True, max_length=200, null=True),
),
migrations.AddField(
- model_name='heartbeat',
- name='radio_version',
+ model_name="heartbeat",
+ name="radio_version",
field=models.CharField(db_index=True, max_length=200, null=True),
),
]
diff --git a/crashreports/models.py b/crashreports/models.py
index 6bf6056..5120322 100644
--- a/crashreports/models.py
+++ b/crashreports/models.py
@@ -9,14 +9,23 @@
class Device(models.Model):
- def __str__( self ):
+ def __str__(self):
return self.uuid
+
# for every device there is a django user
- uuid = models.CharField( db_index=True,max_length=64, unique=True,
- default=uuid.uuid4, editable=False)
+ uuid = models.CharField(
+ db_index=True,
+ max_length=64,
+ unique=True,
+ default=uuid.uuid4,
+ editable=False,
+ )
user = models.OneToOneField(
- User, related_name='Hiccup_Device', on_delete=models.CASCADE,
- unique=True)
+ User,
+ related_name="Hiccup_Device",
+ on_delete=models.CASCADE,
+ unique=True,
+ )
imei = models.CharField(max_length=32, null=True, blank=True)
board_date = models.DateTimeField(null=True, blank=True)
chipset = models.CharField(max_length=200, null=True, blank=True)
@@ -42,27 +51,30 @@
def crashreport_file_name(instance, filename):
- return '/'.join([
- "crashreport_uploads",
- instance.crashreport.device.uuid,
- str(instance.crashreport.id),
- str(instance.crashreport.date),
- filename])
+ return "/".join(
+ [
+ "crashreport_uploads",
+ instance.crashreport.device.uuid,
+ str(instance.crashreport.id),
+ str(instance.crashreport.date),
+ filename,
+ ]
+ )
class Crashreport(models.Model):
- BOOT_REASON_UNKOWN = 'UNKNOWN'
- BOOT_REASON_KEYBOARD_POWER_ON = 'keyboard power on'
- BOOT_REASON_RTC_ALARM = 'RTC alarm'
- CRASH_BOOT_REASONS = [
- BOOT_REASON_UNKOWN,
- BOOT_REASON_KEYBOARD_POWER_ON,
- ]
- SMPL_BOOT_REASONS = [
- BOOT_REASON_RTC_ALARM,
- ]
+ BOOT_REASON_UNKOWN = "UNKNOWN"
+ BOOT_REASON_KEYBOARD_POWER_ON = "keyboard power on"
+ BOOT_REASON_RTC_ALARM = "RTC alarm"
+ CRASH_BOOT_REASONS = [BOOT_REASON_UNKOWN, BOOT_REASON_KEYBOARD_POWER_ON]
+ SMPL_BOOT_REASONS = [BOOT_REASON_RTC_ALARM]
- device = models.ForeignKey(Device, db_index=True, related_name='crashreports', on_delete=models.CASCADE)
+ device = models.ForeignKey(
+ Device,
+ db_index=True,
+ related_name="crashreports",
+ on_delete=models.CASCADE,
+ )
is_fake_report = models.BooleanField(default=False)
app_version = models.IntegerField()
uptime = models.CharField(max_length=200)
@@ -92,12 +104,16 @@
def _get_uuid(self):
"Returns the person's full name."
return self.device.uuid
+
uuid = property(_get_uuid)
-#TODO remove logfile_type or make it meaningful
+
+# TODO remove logfile_type or make it meaningful
class LogFile(models.Model):
logfile_type = models.TextField(max_length=36, default="last_kmsg")
- crashreport = models.ForeignKey(Crashreport,related_name='logfiles', on_delete=models.CASCADE)
+ crashreport = models.ForeignKey(
+ Crashreport, related_name="logfiles", on_delete=models.CASCADE
+ )
logfile = models.FileField(upload_to=crashreport_file_name, max_length=500)
crashreport_local_id = models.PositiveIntegerField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
@@ -109,13 +125,15 @@
class HeartBeat(models.Model):
- device = models.ForeignKey(Device,
+ device = models.ForeignKey(
+ Device,
db_index=True,
- related_name='heartbeats',
- on_delete=models.CASCADE)
+ related_name="heartbeats",
+ on_delete=models.CASCADE,
+ )
app_version = models.IntegerField()
uptime = models.CharField(max_length=200)
- build_fingerprint = models.CharField( db_index=True, max_length=200)
+ build_fingerprint = models.CharField(db_index=True, max_length=200)
radio_version = models.CharField(db_index=True, max_length=200, null=True)
date = models.DateTimeField(db_index=True)
device_local_id = models.PositiveIntegerField(blank=True)
@@ -129,4 +147,5 @@
def _get_uuid(self):
"Returns the person's full name."
return self.device.uuid
+
uuid = property(_get_uuid)
diff --git a/crashreports/permissions.py b/crashreports/permissions.py
index ff79149..c1efe79 100644
--- a/crashreports/permissions.py
+++ b/crashreports/permissions.py
@@ -7,42 +7,49 @@
device = Device.objects.get(user=user)
except:
return False
- if (uuid == device.uuid):
+ if uuid == device.uuid:
return True
return False
def user_is_hiccup_staff(user):
- if (user.groups.filter(name='FairphoneSoftwareTeam').exists()):
+ if user.groups.filter(name="FairphoneSoftwareTeam").exists():
return True
else:
- return user.has_perms([
- # Crashreports
- 'crashreports.add_crashreport', 'crashreports.change_crashreport',
- 'crashreports.del_crashreport',
- # Heartbeats
- 'heartbeat.add_crashreport', 'heartbeat.change_crashreport',
- 'heartbeat.del_crashreport',
- # Logfiles
- 'heartbeat.add_logfile', 'heartbeat.change_logfile',
- 'heartbeat.del_logfile',
- ])
+ return user.has_perms(
+ [
+ # Crashreports
+ "crashreports.add_crashreport",
+ "crashreports.change_crashreport",
+ "crashreports.del_crashreport",
+ # Heartbeats
+ "heartbeat.add_crashreport",
+ "heartbeat.change_crashreport",
+ "heartbeat.del_crashreport",
+ # Logfiles
+ "heartbeat.add_logfile",
+ "heartbeat.change_logfile",
+ "heartbeat.del_logfile",
+ ]
+ )
+
class HasStatsAccess(BasePermission):
def has_permission(self, request, view):
return user_is_hiccup_staff(request.user)
+
class HasRightsOrIsDeviceOwnerDeviceCreation(BasePermission):
def has_permission(self, request, view):
- if (user_is_hiccup_staff(request.user)):
+ if user_is_hiccup_staff(request.user):
return True
# special case:
# user is the owner of a device. in this case creations are allowed.
# we have to check if the device with the supplied uuid indeed
# belongs to the user
- if request.method == 'POST':
- if ('uuid' not in request.data):
+ if request.method == "POST":
+ if "uuid" not in request.data:
return False
return user_owns_uuid(request.user, request.data["uuid"])
return False
diff --git a/crashreports/rest_api_crashreports.py b/crashreports/rest_api_crashreports.py
index b6555fd..df99732 100644
--- a/crashreports/rest_api_crashreports.py
+++ b/crashreports/rest_api_crashreports.py
@@ -10,31 +10,32 @@
class ListCreateView(generics.ListCreateAPIView):
queryset = Crashreport.objects.all()
paginate_by = 20
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
serializer_class = CrashReportSerializer
- filter_fields = ('device', 'build_fingerprint', 'radio_version')
+ filter_fields = ("device", "build_fingerprint", "radio_version")
pass
def dispatch(self, *args, **kwargs):
- if 'uuid' in kwargs:
+ if "uuid" in kwargs:
self.queryset = Crashreport.objects.filter(
- device__uuid=kwargs['uuid'])
+ device__uuid=kwargs["uuid"]
+ )
return generics.ListCreateAPIView.dispatch(self, *args, **kwargs)
def perform_create(self, serializer):
serializer.save()
return Response(
- {
- 'device_local_id': serializer.data['device_local_id']
- }, status.HTTP_200_OK)
+ {"device_local_id": serializer.data["device_local_id"]},
+ status.HTTP_200_OK,
+ )
class RetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
queryset = Crashreport.objects.all()
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
serializer_class = CrashReportSerializer
- multiple_lookup_fields = {'id', 'device__uuid', 'device_local_id'}
+ multiple_lookup_fields = {"id", "device__uuid", "device_local_id"}
def get_object(self):
queryset = self.get_queryset()
diff --git a/crashreports/rest_api_devices.py b/crashreports/rest_api_devices.py
index 17e0d77..96923b2 100644
--- a/crashreports/rest_api_devices.py
+++ b/crashreports/rest_api_devices.py
@@ -17,42 +17,64 @@
from crashreports.response_descriptions import default_desc
-@method_decorator(name='get', decorator=swagger_auto_schema(
- operation_description='List devices'))
-@method_decorator(name='post', decorator=swagger_auto_schema(
- operation_description='Create a device',
- responses=dict([default_desc(ValidationError)])))
+@method_decorator(
+ name="get",
+ decorator=swagger_auto_schema(operation_description="List devices"),
+)
+@method_decorator(
+ name="post",
+ decorator=swagger_auto_schema(
+ operation_description="Create a device",
+ responses=dict([default_desc(ValidationError)]),
+ ),
+)
class ListCreateDevices(generics.ListCreateAPIView):
"""Endpoint for listing devices and creating new devices."""
queryset = Device.objects.all()
paginate_by = 20
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
serializer_class = DeviceSerializer
- filter_fields = ('uuid', 'board_date', 'chipset')
+ filter_fields = ("uuid", "board_date", "chipset")
-@method_decorator(name='get', decorator=swagger_auto_schema(
- operation_description='Get a device',
- responses=dict([default_desc(NotFound)])))
-@method_decorator(name='put', decorator=swagger_auto_schema(
- operation_description='Update a device',
- responses=dict([default_desc(NotFound), default_desc(ValidationError)])))
-@method_decorator(name='patch', decorator=swagger_auto_schema(
- operation_description='Make a partial update for a device',
- responses=dict([default_desc(NotFound), default_desc(ValidationError)])))
-@method_decorator(name='delete', decorator=swagger_auto_schema(
- operation_description='Delete a device',
- responses=dict([default_desc(NotFound)])))
+@method_decorator(
+ name="get",
+ decorator=swagger_auto_schema(
+ operation_description="Get a device",
+ responses=dict([default_desc(NotFound)]),
+ ),
+)
+@method_decorator(
+ name="put",
+ decorator=swagger_auto_schema(
+ operation_description="Update a device",
+ responses=dict([default_desc(NotFound), default_desc(ValidationError)]),
+ ),
+)
+@method_decorator(
+ name="patch",
+ decorator=swagger_auto_schema(
+ operation_description="Make a partial update for a device",
+ responses=dict([default_desc(NotFound), default_desc(ValidationError)]),
+ ),
+)
+@method_decorator(
+ name="delete",
+ decorator=swagger_auto_schema(
+ operation_description="Delete a device",
+ responses=dict([default_desc(NotFound)]),
+ ),
+)
class RetrieveUpdateDestroyDevice(generics.RetrieveUpdateDestroyAPIView):
"""Endpoint for retrieving, updating, patching and deleting devices."""
# pylint: disable=too-many-ancestors
queryset = Device.objects.all()
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
serializer_class = DeviceSerializer
- lookup_field = 'uuid'
+ lookup_field = "uuid"
class DeviceRegisterResponseSchema(DeviceSerializer):
@@ -60,19 +82,26 @@
class Meta: # noqa: D106
model = Device
- fields = ['uuid', 'token']
+ fields = ["uuid", "token"]
@swagger_auto_schema(
- method='post',
+ method="post",
request_body=DeviceCreateSerializer,
- responses=dict([
- default_desc(ValidationError),
- (status.HTTP_200_OK,
- openapi.Response('The device has been successfully registered.',
- DeviceRegisterResponseSchema))
- ]))
-@api_view(http_method_names=['POST'], )
+ responses=dict(
+ [
+ default_desc(ValidationError),
+ (
+ status.HTTP_200_OK,
+ openapi.Response(
+ "The device has been successfully registered.",
+ DeviceRegisterResponseSchema,
+ ),
+ ),
+ ]
+ ),
+)
+@api_view(http_method_names=["POST"])
@permission_classes((AllowAny,))
def register_device(request):
"""Register a new device.
@@ -84,13 +113,13 @@
serializer = DeviceCreateSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
device = Device()
- user = User.objects.create_user("device_" + str(device.uuid), '', None)
- permission = Permission.objects.get(name='Can add crashreport')
+ user = User.objects.create_user("device_" + str(device.uuid), "", None)
+ permission = Permission.objects.get(name="Can add crashreport")
user.user_permissions.add(permission)
user.save()
- device.board_date = serializer.validated_data['board_date']
- device.chipset = serializer.validated_data['chipset']
+ device.board_date = serializer.validated_data["board_date"]
+ device.chipset = serializer.validated_data["chipset"]
device.user = user
device.token = Token.objects.create(user=user).key
device.save()
- return Response({'uuid': device.uuid, 'token': device.token})
+ return Response({"uuid": device.uuid, "token": device.token})
diff --git a/crashreports/rest_api_heartbeats.py b/crashreports/rest_api_heartbeats.py
index 841dec8..f2e19db 100644
--- a/crashreports/rest_api_heartbeats.py
+++ b/crashreports/rest_api_heartbeats.py
@@ -11,23 +11,23 @@
class ListCreateView(generics.ListCreateAPIView):
queryset = HeartBeat.objects.all()
paginate_by = 20
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
serializer_class = HeartBeatSerializer
- filter_fields = ('device', 'build_fingerprint', 'radio_version')
-
+ filter_fields = ("device", "build_fingerprint", "radio_version")
def get(self, *args, **kwargs):
- if 'uuid' in kwargs:
+ if "uuid" in kwargs:
self.queryset = HeartBeat.objects.filter(
- device__uuid=kwargs['uuid'])
+ device__uuid=kwargs["uuid"]
+ )
return generics.ListCreateAPIView.get(self, *args, **kwargs)
class RetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
queryset = HeartBeat.objects.all()
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
serializer_class = HeartBeatSerializer
- multiple_lookup_fields = {'id', 'device__uuid', 'device_local_id'}
+ multiple_lookup_fields = {"id", "device__uuid", "device_local_id"}
def get_object(self):
queryset = self.get_queryset()
diff --git a/crashreports/rest_api_logfiles.py b/crashreports/rest_api_logfiles.py
index 7e02dab..df3bbcb 100644
--- a/crashreports/rest_api_logfiles.py
+++ b/crashreports/rest_api_logfiles.py
@@ -21,28 +21,31 @@
class ListCreateView(generics.ListAPIView):
queryset = LogFile.objects.all()
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
serializer_class = LogFileSerializer
class RetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
queryset = LogFile.objects.all()
- permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation,)
serializer_class = LogFileSerializer
-@api_view(http_method_names=['POST'], )
-@parser_classes([FileUploadParser, ])
-@permission_classes([IsAuthenticated, ])
+@api_view(http_method_names=["POST"])
+@parser_classes([FileUploadParser])
+@permission_classes([IsAuthenticated])
def logfile_put(request, uuid, device_local_id, filename):
try:
- crashreport = Crashreport.objects.get(device__uuid=uuid,
- device_local_id=device_local_id)
+ crashreport = Crashreport.objects.get(
+ device__uuid=uuid, device_local_id=device_local_id
+ )
except:
raise NotFound(detail="Crashreport does not exist.")
- if (not (user_owns_uuid(request.user, crashreport.device.uuid)
- or user_is_hiccup_staff(request.user))):
+ if not (
+ user_owns_uuid(request.user, crashreport.device.uuid)
+ or user_is_hiccup_staff(request.user)
+ ):
raise PermissionDenied(detail="Not allowed.")
f = request.data["file"]
logfile = LogFile(crashreport=crashreport, logfile=f)
diff --git a/crashreports/serializers.py b/crashreports/serializers.py
index 732e595..af71a70 100644
--- a/crashreports/serializers.py
+++ b/crashreports/serializers.py
@@ -5,9 +5,7 @@
from rest_framework.exceptions import NotFound
from rest_framework import permissions
-from crashreports.models import (
- Crashreport, Device, HeartBeat, LogFile
-)
+from crashreports.models import Crashreport, Device, HeartBeat, LogFile
from crashreports.permissions import user_is_hiccup_staff
@@ -23,7 +21,7 @@
Given the *outgoing* object instance, return the primitive value
that should be used for this field.
"""
- if user_is_hiccup_staff(self.context['request'].user):
+ if user_is_hiccup_staff(self.context["request"].user):
return super(PrivateField, self).get_attribute(instance)
return -1
@@ -33,10 +31,8 @@
permission_classes = (permissions.AllowAny,)
logfiles = serializers.HyperlinkedRelatedField(
- read_only=True,
- many=True,
- view_name='api_v1_logfiles_by_id',
- )
+ read_only=True, many=True, view_name="api_v1_logfiles_by_id"
+ )
uuid = serializers.CharField(max_length=64)
id = PrivateField()
device_local_id = serializers.IntegerField(required=False)
@@ -44,7 +40,7 @@
class Meta: # noqa: D106
model = Crashreport
- exclude = ('device',)
+ exclude = ("device",)
def create(self, validated_data):
"""Create a crashreport.
@@ -56,10 +52,10 @@
"""
try:
- device = Device.objects.get(uuid=validated_data['uuid'])
+ device = Device.objects.get(uuid=validated_data["uuid"])
except ObjectDoesNotExist:
raise NotFound(detail="uuid does not exist")
- validated_data.pop('uuid', None)
+ validated_data.pop("uuid", None)
report = Crashreport(**validated_data)
report.device = device
report.save()
@@ -77,7 +73,7 @@
class Meta: # noqa: D106
model = HeartBeat
- exclude = ('device',)
+ exclude = ("device",)
def create(self, validated_data):
"""Create a heartbeat report.
@@ -89,10 +85,10 @@
"""
try:
- device = Device.objects.get(uuid=validated_data['uuid'])
+ device = Device.objects.get(uuid=validated_data["uuid"])
except ObjectDoesNotExist:
raise NotFound(detail="uuid does not exist")
- validated_data.pop('uuid', None)
+ validated_data.pop("uuid", None)
heartbeat = HeartBeat(**validated_data)
heartbeat.device = device
heartbeat.save()
@@ -106,7 +102,7 @@
class Meta: # noqa: D106
model = LogFile
- fields = '__all__'
+ fields = "__all__"
class DeviceSerializer(serializers.ModelSerializer):
@@ -118,7 +114,7 @@
class Meta: # noqa: D106
model = Device
- fields = '__all__'
+ fields = "__all__"
class DeviceCreateSerializer(DeviceSerializer):
@@ -126,8 +122,8 @@
class Meta: # noqa: D106
model = Device
- fields = ('board_date', 'chipset')
+ fields = ("board_date", "chipset")
extra_kwargs = {
- 'board_date': {'required': True},
- 'chipset': {'required': True},
+ "board_date": {"required": True},
+ "chipset": {"required": True},
}
diff --git a/crashreports/tests.py b/crashreports/tests.py
index 2267a79..354b330 100644
--- a/crashreports/tests.py
+++ b/crashreports/tests.py
@@ -30,42 +30,47 @@
crash_type: The invalid crash type.
"""
super(InvalidCrashTypeError, self).__init__(
- '{} is not a valid crash type'.format(crash_type))
+ "{} is not a valid crash type".format(crash_type)
+ )
-class Dummy():
+class Dummy:
"""Dummy values for devices, heartbeats and crashreports."""
DEFAULT_DUMMY_DEVICE_REGISTER_VALUES = {
- 'board_date': '2015-12-15T01:23:45Z',
- 'chipset': 'Qualcomm MSM8974PRO-AA',
+ "board_date": "2015-12-15T01:23:45Z",
+ "chipset": "Qualcomm MSM8974PRO-AA",
}
DEFAULT_DUMMY_HEARTBEAT_VALUES = {
- 'uuid': None,
- 'app_version': 10100,
- 'uptime': (
- 'up time: 16 days, 21:49:56, idle time: 5 days, 20:55:04, '
- 'sleep time: 10 days, 20:46:27'),
- 'build_fingerprint': (
- 'Fairphone/FP2/FP2:6.0.1/FP2-gms-18.03.1/FP2-gms-18.03.1:user/'
- 'release-keys'),
- 'radio_version': '4437.1-FP2-0-08',
- 'date': '2018-03-19T09:58:30.386Z',
+ "uuid": None,
+ "app_version": 10100,
+ "uptime": (
+ "up time: 16 days, 21:49:56, idle time: 5 days, 20:55:04, "
+ "sleep time: 10 days, 20:46:27"
+ ),
+ "build_fingerprint": (
+ "Fairphone/FP2/FP2:6.0.1/FP2-gms-18.03.1/FP2-gms-18.03.1:user/"
+ "release-keys"
+ ),
+ "radio_version": "4437.1-FP2-0-08",
+ "date": "2018-03-19T09:58:30.386Z",
}
DEFAULT_DUMMY_CRASHREPORTS_VALUES = DEFAULT_DUMMY_HEARTBEAT_VALUES.copy()
- DEFAULT_DUMMY_CRASHREPORTS_VALUES.update({
- 'is_fake_report': 0,
- 'boot_reason': 'why?',
- 'power_on_reason': 'it was powered on',
- 'power_off_reason': 'something happened and it went off',
- })
+ DEFAULT_DUMMY_CRASHREPORTS_VALUES.update(
+ {
+ "is_fake_report": 0,
+ "boot_reason": "why?",
+ "power_on_reason": "it was powered on",
+ "power_off_reason": "something happened and it went off",
+ }
+ )
CRASH_TYPE_TO_BOOT_REASON_MAP = {
- 'crash': Crashreport.BOOT_REASON_KEYBOARD_POWER_ON,
- 'smpl': Crashreport.BOOT_REASON_RTC_ALARM,
- 'other': 'whatever',
+ "crash": Crashreport.BOOT_REASON_KEYBOARD_POWER_ON,
+ "smpl": Crashreport.BOOT_REASON_RTC_ALARM,
+ "other": "whatever",
}
@staticmethod
@@ -83,7 +88,8 @@
from `Dummy.DEFAULT_DUMMY_DEVICE_REGISTER_VALUES`.
"""
return Dummy._update_copy(
- Dummy.DEFAULT_DUMMY_DEVICE_REGISTER_VALUES, kwargs)
+ Dummy.DEFAULT_DUMMY_DEVICE_REGISTER_VALUES, kwargs
+ )
@staticmethod
def heartbeat_data(**kwargs):
@@ -108,12 +114,14 @@
keyword arguments already.
"""
data = Dummy._update_copy(
- Dummy.DEFAULT_DUMMY_CRASHREPORTS_VALUES, kwargs)
- if report_type and 'boot_reason' not in kwargs:
+ Dummy.DEFAULT_DUMMY_CRASHREPORTS_VALUES, kwargs
+ )
+ if report_type and "boot_reason" not in kwargs:
if report_type not in Dummy.CRASH_TYPE_TO_BOOT_REASON_MAP:
raise InvalidCrashTypeError(report_type)
- data['boot_reason'] = Dummy.CRASH_TYPE_TO_BOOT_REASON_MAP.get(
- report_type)
+ data["boot_reason"] = Dummy.CRASH_TYPE_TO_BOOT_REASON_MAP.get(
+ report_type
+ )
return data
@@ -129,7 +137,8 @@
server is stored in self.admin.
"""
admin_user = User.objects.create_superuser(
- 'somebody', 'somebody@example.com', 'thepassword')
+ "somebody", "somebody@example.com", "thepassword"
+ )
self.admin = APIClient()
self.admin.force_authenticate(admin_user)
@@ -148,10 +157,10 @@
response = self.client.post(reverse(self.REGISTER_DEVICE_URL), data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- uuid = response.data['uuid']
- token = response.data['token']
+ uuid = response.data["uuid"]
+ token = response.data["token"]
user = APIClient()
- user.credentials(HTTP_AUTHORIZATION='Token ' + token)
+ user.credentials(HTTP_AUTHORIZATION="Token " + token)
return uuid, user, token
@@ -161,8 +170,9 @@
def test_register(self):
"""Test registration of devices."""
- response = self.client.post(reverse(self.REGISTER_DEVICE_URL),
- Dummy.device_register_data())
+ response = self.client.post(
+ reverse(self.REGISTER_DEVICE_URL), Dummy.device_register_data()
+ )
self.assertTrue("token" in response.data)
self.assertTrue("uuid" in response.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -175,21 +185,21 @@
def test_create_missing_board_date(self):
"""Test registration with missing board date."""
data = Dummy.device_register_data()
- data.pop('board_date')
+ data.pop("board_date")
response = self.client.post(reverse(self.REGISTER_DEVICE_URL), data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_create_missing_chipset(self):
"""Test registration with missing chipset."""
data = Dummy.device_register_data()
- data.pop('chipset')
+ data.pop("chipset")
response = self.client.post(reverse(self.REGISTER_DEVICE_URL), data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_create_invalid_board_date(self):
"""Test registration with invalid board date."""
data = Dummy.device_register_data()
- data['board_date'] = 'not_a_valid_date'
+ data["board_date"] = "not_a_valid_date"
response = self.client.post(reverse(self.REGISTER_DEVICE_URL), data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
@@ -204,7 +214,7 @@
data = Dummy.device_register_data()
# In 2017, the Netherlands changed from CET to CEST on March,
# 26 at 02:00
- data['board_date'] = '2017-03-26 02:34:56'
+ data["board_date"] = "2017-03-26 02:34:56"
response = self.client.post(reverse(self.REGISTER_DEVICE_URL), data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -219,7 +229,7 @@
data = Dummy.device_register_data()
# In 2017, the Netherlands changed from CEST to CET on October,
# 29 at 03:00
- data['board_date'] = '2017-10-29 02:34:56'
+ data["board_date"] = "2017-10-29 02:34:56"
response = self.client.post(reverse(self.REGISTER_DEVICE_URL), data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -234,15 +244,14 @@
"""Test registration of 2 devices."""
number_of_devices = 2
uuids = [
- str(self._register_device()[0])
- for _ in range(number_of_devices)
+ str(self._register_device()[0]) for _ in range(number_of_devices)
]
response = self.admin.get(reverse(self.LIST_CREATE_URL), {})
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data['results']), number_of_devices)
- for result in response.data['results']:
- self.assertIn(result['uuid'], uuids)
+ self.assertEqual(len(response.data["results"]), number_of_devices)
+ for result in response.data["results"]:
+ self.assertIn(result["uuid"], uuids)
def test_device_list_unauth(self):
"""Test listing devices without authentication."""
@@ -254,8 +263,8 @@
uuid, _, token = self._register_device()
response = self.admin.get(reverse(self.RETRIEVE_URL, args=[uuid]), {})
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['uuid'], str(uuid))
- self.assertEqual(response.data['token'], token)
+ self.assertEqual(response.data["uuid"], str(uuid))
+ self.assertEqual(response.data["token"], token)
def test_retrieve_device_unauth(self):
"""Test retrieval of devices without authentication."""
@@ -288,14 +297,15 @@
def _post_multiple(self, client, data, count):
return [
client.post(reverse(self.LIST_CREATE_URL), data)
- for _ in range(count)]
+ for _ in range(count)
+ ]
def _retrieve_single(self, user):
count = 5
response = self._post_multiple(self.admin, self.data, count)
self.assertEqual(len(response), count)
self.assertEqual(response[0].status_code, status.HTTP_201_CREATED)
- url = reverse(self.RETRIEVE_URL, args=[response[0].data['id']])
+ url = reverse(self.RETRIEVE_URL, args=[response[0].data["id"]])
request = user.get(url)
return request.status_code
@@ -304,8 +314,10 @@
response = self._post_multiple(self.user, self.data, count)
self.assertEqual(len(response), count)
self.assertEqual(response[0].status_code, status.HTTP_201_CREATED)
- url = reverse(self.RETRIEVE_BY_UUID_URL, args=[
- self.uuid, response[0].data['device_local_id']])
+ url = reverse(
+ self.RETRIEVE_BY_UUID_URL,
+ args=[self.uuid, response[0].data["device_local_id"]],
+ )
request = user.get(url)
return request.status_code
@@ -318,36 +330,37 @@
def test_create_no_auth(self):
"""Test creation without authentication."""
noauth_client = APIClient()
- response = noauth_client.post(
- reverse(self.LIST_CREATE_URL), self.data)
+ response = noauth_client.post(reverse(self.LIST_CREATE_URL), self.data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_create_as_admin(self):
"""Test creation as admin."""
response = self.admin.post(reverse(self.LIST_CREATE_URL), self.data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertTrue(response.data['id'] > 0)
+ self.assertTrue(response.data["id"] > 0)
def test_create_as_admin_not_existing_device(self):
"""Test creation of heartbeat on non-existing device."""
response = self.admin.post(
- reverse(self.LIST_CREATE_URL), self._create_dummy_data())
+ reverse(self.LIST_CREATE_URL), self._create_dummy_data()
+ )
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_create_as_uuid_owner(self):
"""Test creation as owner."""
response = self.user.post(
reverse(self.LIST_CREATE_URL),
- self._create_dummy_data(uuid=self.uuid))
+ self._create_dummy_data(uuid=self.uuid),
+ )
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(response.data['id'], -1)
+ self.assertEqual(response.data["id"], -1)
def test_create_as_uuid_not_owner(self):
"""Test creation as non-owner."""
uuid, _, _ = self._register_device()
response = self.user.post(
- reverse(self.LIST_CREATE_URL),
- self._create_dummy_data(uuid=uuid))
+ reverse(self.LIST_CREATE_URL), self._create_dummy_data(uuid=uuid)
+ )
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_list(self):
@@ -356,42 +369,45 @@
self._post_multiple(self.user, self.data, count)
response = self.admin.get(reverse(self.LIST_CREATE_URL))
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data['results']), count)
+ self.assertEqual(len(response.data["results"]), count)
def test_retrieve_single_admin(self):
"""Test retrieval as admin."""
- self.assertEqual(
- self._retrieve_single(self.admin), status.HTTP_200_OK)
+ self.assertEqual(self._retrieve_single(self.admin), status.HTTP_200_OK)
def test_retrieve_single_device_owner(self):
"""Test retrieval as device owner."""
self.assertEqual(
- self._retrieve_single(self.user), status.HTTP_403_FORBIDDEN)
+ self._retrieve_single(self.user), status.HTTP_403_FORBIDDEN
+ )
def test_retrieve_single_noauth(self):
"""Test retrieval without authentication."""
noauth_client = APIClient()
self.assertEqual(
- self._retrieve_single(noauth_client),
- status.HTTP_401_UNAUTHORIZED)
+ self._retrieve_single(noauth_client), status.HTTP_401_UNAUTHORIZED
+ )
def test_retrieve_single_by_device_admin(self):
"""Test retrieval by device as admin."""
self.assertEqual(
- self._retrieve_single_by_device(self.admin), status.HTTP_200_OK)
+ self._retrieve_single_by_device(self.admin), status.HTTP_200_OK
+ )
def test_retrieve_single_by_device_device_owner(self):
"""Test retrieval by device as owner."""
self.assertEqual(
self._retrieve_single_by_device(self.user),
- status.HTTP_403_FORBIDDEN)
+ status.HTTP_403_FORBIDDEN,
+ )
def test_retrieve_single_by_device_noauth(self):
"""Test retrieval by device without authentication."""
noauth_client = APIClient()
self.assertEqual(
self._retrieve_single_by_device(noauth_client),
- status.HTTP_401_UNAUTHORIZED)
+ status.HTTP_401_UNAUTHORIZED,
+ )
def test_list_by_uuid(self):
"""Test listing of devices by UUID."""
@@ -399,11 +415,12 @@
uuid, _, _ = self._register_device()
self._post_multiple(self.user, self.data, count)
self._post_multiple(
- self.admin, self._create_dummy_data(uuid=uuid), count)
+ self.admin, self._create_dummy_data(uuid=uuid), count
+ )
url = reverse(self.LIST_CREATE_BY_UUID_URL, args=[self.uuid])
response = self.admin.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data['results']), count)
+ self.assertEqual(len(response.data["results"]), count)
def test_list_noauth(self):
"""Test listing of devices without authentication."""
@@ -423,14 +440,14 @@
def test_no_radio_version(self):
"""Test creation and retrieval without radio version."""
data = self._create_dummy_data(uuid=self.uuid)
- data.pop('radio_version')
+ data.pop("radio_version")
response = self.user.post(reverse(self.LIST_CREATE_URL), data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
url = reverse(self.LIST_CREATE_BY_UUID_URL, args=[self.uuid])
response = self.admin.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data['results']), 1)
- self.assertIsNone(response.data['results'][0]['radio_version'])
+ self.assertEqual(len(response.data["results"]), 1)
+ self.assertIsNone(response.data["results"][0]["radio_version"])
def test_radio_version_field(self):
"""Test retrieval of radio version field."""
@@ -439,9 +456,11 @@
url = reverse(self.LIST_CREATE_BY_UUID_URL, args=[self.uuid])
response = self.admin.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(len(response.data['results']), 1)
- self.assertEqual(response.data['results'][0]['radio_version'],
- self.data['radio_version'])
+ self.assertEqual(len(response.data["results"]), 1)
+ self.assertEqual(
+ response.data["results"][0]["radio_version"],
+ self.data["radio_version"],
+ )
def test_send_non_existent_time(self):
"""Test sending of heartbeat with non existent time.
@@ -453,7 +472,7 @@
data = self._create_dummy_data(uuid=self.uuid)
# In 2017, the Netherlands changed from CET to CEST on March,
# 26 at 02:00
- data['date'] = '2017-03-26 02:34:56'
+ data["date"] = "2017-03-26 02:34:56"
response = self.user.post(reverse(self.LIST_CREATE_URL), data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
@@ -467,7 +486,7 @@
data = self._create_dummy_data(uuid=self.uuid)
# In 2017, the Netherlands changed from CEST to CET on October,
# 29 at 03:00
- data['date'] = '2017-10-29 02:34:56'
+ data["date"] = "2017-10-29 02:34:56"
response = self.user.post(reverse(self.LIST_CREATE_URL), data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
@@ -506,8 +525,8 @@
data = Dummy.crashreport_data(uuid=uuid)
response = user.post(reverse(self.LIST_CREATE_URL), data)
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
- self.assertTrue('device_local_id' in response.data)
- device_local_id = response.data['device_local_id']
+ self.assertTrue("device_local_id" in response.data)
+ device_local_id = response.data["device_local_id"]
return device_local_id
@@ -516,13 +535,16 @@
device_local_id = self._upload_crashreport(user, uuid)
# Upload a logfile for the crashreport
- logfile = tempfile.NamedTemporaryFile('w+', suffix=".log", delete=True)
+ logfile = tempfile.NamedTemporaryFile("w+", suffix=".log", delete=True)
logfile.write(u"blihblahblub")
response = user.post(
- reverse(self.PUT_LOGFILE_URL, args=[
- uuid, device_local_id, os.path.basename(logfile.name)
- ]),
- {'file': logfile}, format="multipart")
+ reverse(
+ self.PUT_LOGFILE_URL,
+ args=[uuid, device_local_id, os.path.basename(logfile.name)],
+ ),
+ {"file": logfile},
+ format="multipart",
+ )
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
def test_logfile_upload_as_user(self):
diff --git a/crashreports/urls.py b/crashreports/urls.py
index d86c976..b876aaf 100644
--- a/crashreports/urls.py
+++ b/crashreports/urls.py
@@ -7,54 +7,80 @@
urlpatterns = [
# crashreports
- url(r'^api/v1/crashreports/$',
+ url(
+ r"^api/v1/crashreports/$",
rest_api_crashreports.ListCreateView.as_view(),
- name='api_v1_crashreports'),
- url(r'^api/v1/devices/(?P<uuid>[a-f0-9-]+)/crashreports/$',
+ name="api_v1_crashreports",
+ ),
+ url(
+ r"^api/v1/devices/(?P<uuid>[a-f0-9-]+)/crashreports/$",
rest_api_crashreports.ListCreateView.as_view(),
- name='api_v1_crashreports_by_uuid'),
- url(r'^api/v1/crashreports/(?P<id>[0-9]+)/$',
+ name="api_v1_crashreports_by_uuid",
+ ),
+ url(
+ r"^api/v1/crashreports/(?P<id>[0-9]+)/$",
rest_api_crashreports.RetrieveUpdateDestroyView.as_view(),
- name='api_v1_crashreport'),
- url(r'^api/v1/devices/(?P<device__uuid>[a-f0-9-]+)/crashreports/' +
- '(?P<device_local_id>[0-9]+)/$',
+ name="api_v1_crashreport",
+ ),
+ url(
+ r"^api/v1/devices/(?P<device__uuid>[a-f0-9-]+)/crashreports/"
+ + "(?P<device_local_id>[0-9]+)/$",
rest_api_crashreports.RetrieveUpdateDestroyView.as_view(),
- name='api_v1_crashreport_by_uuid'),
-
+ name="api_v1_crashreport_by_uuid",
+ ),
# logfiles
- url(r'^api/v1/devices/(?P<uuid>[a-f0-9-]+)/crashreports/' +
- '(?P<device_local_id>[0-9]+)/logfile_put/(?P<filename>[^/]+)/$',
+ url(
+ r"^api/v1/devices/(?P<uuid>[a-f0-9-]+)/crashreports/"
+ + "(?P<device_local_id>[0-9]+)/logfile_put/(?P<filename>[^/]+)/$",
rest_api_logfiles.logfile_put,
- name='api_v1_putlogfile_for_device_id'),
-
- url(r'^api/v1/logfiles/$',
- rest_api_logfiles.ListCreateView.as_view(),
- name='api_v1_logfiles'),
- url(r'^api/v1/logfiles/(?P<pk>[0-9]+)/$',
+ name="api_v1_putlogfile_for_device_id",
+ ),
+ url(
+ r"^api/v1/logfiles/$",
+ rest_api_logfiles.ListCreateView.as_view(),
+ name="api_v1_logfiles",
+ ),
+ url(
+ r"^api/v1/logfiles/(?P<pk>[0-9]+)/$",
rest_api_logfiles.RetrieveUpdateDestroyView.as_view(),
- name='api_v1_logfiles_by_id'),
-
+ name="api_v1_logfiles_by_id",
+ ),
# heartbeats
- url(r'^api/v1/heartbeats/$',
+ url(
+ r"^api/v1/heartbeats/$",
rest_api_heartbeats.ListCreateView.as_view(),
- name='api_v1_heartbeats'),
- url(r'^api/v1/devices/(?P<uuid>[a-f0-9-]+)/heartbeats/$',
+ name="api_v1_heartbeats",
+ ),
+ url(
+ r"^api/v1/devices/(?P<uuid>[a-f0-9-]+)/heartbeats/$",
rest_api_heartbeats.ListCreateView.as_view(),
- name='api_v1_heartbeats_by_uuid'),
- url(r'^api/v1/heartbeats/(?P<id>[0-9]+)/$',
+ name="api_v1_heartbeats_by_uuid",
+ ),
+ url(
+ r"^api/v1/heartbeats/(?P<id>[0-9]+)/$",
rest_api_heartbeats.RetrieveUpdateDestroyView.as_view(),
- name='api_v1_heartbeat'),
- url(r'^api/v1/devices/(?P<uuid>[a-f0-9-]+)/heartbeats/' +
- '(?P<device_local_id>[0-9]+)/$',
+ name="api_v1_heartbeat",
+ ),
+ url(
+ r"^api/v1/devices/(?P<uuid>[a-f0-9-]+)/heartbeats/"
+ + "(?P<device_local_id>[0-9]+)/$",
rest_api_heartbeats.RetrieveUpdateDestroyView.as_view(),
- name='api_v1_heartbeat_by_uuid'),
-
+ name="api_v1_heartbeat_by_uuid",
+ ),
# devices
- url(r'^api/v1/devices/$', rest_api_devices.ListCreateDevices.as_view(),
- name='api_v1_list_devices'),
- url(r'^api/v1/devices/(?P<uuid>[a-f0-9-]+)/$',
+ url(
+ r"^api/v1/devices/$",
+ rest_api_devices.ListCreateDevices.as_view(),
+ name="api_v1_list_devices",
+ ),
+ url(
+ r"^api/v1/devices/(?P<uuid>[a-f0-9-]+)/$",
rest_api_devices.RetrieveUpdateDestroyDevice.as_view(),
- name='api_v1_retrieve_device'),
- url(r'^api/v1/devices/register/$', rest_api_devices.register_device,
- name='api_v1_register_device'),
+ name="api_v1_retrieve_device",
+ ),
+ url(
+ r"^api/v1/devices/register/$",
+ rest_api_devices.register_device,
+ name="api_v1_register_device",
+ ),
]
diff --git a/hiccup/allauth_adapters.py b/hiccup/allauth_adapters.py
index 268d590..4eb3a06 100644
--- a/hiccup/allauth_adapters.py
+++ b/hiccup/allauth_adapters.py
@@ -3,27 +3,29 @@
from django.core.exceptions import PermissionDenied
from django.contrib.auth.models import Group
+
class FairphoneAccountAdapter(DefaultSocialAccountAdapter):
-
def is_open_for_signup(self, request, sociallogin):
return True
-
+
def save_user(self, request, sociallogin, form=None):
- u =DefaultSocialAccountAdapter.save_user(self, request, sociallogin, form=None)
- if u.email.split('@')[1] == "fairphone.com":
- g = Group.objects.get(name='FairphoneSoftwareTeam')
+ u = DefaultSocialAccountAdapter.save_user(
+ self, request, sociallogin, form=None
+ )
+ if u.email.split("@")[1] == "fairphone.com":
+ g = Group.objects.get(name="FairphoneSoftwareTeam")
g.user_set.add(u)
return u
-
- def populate_user(self,
- request,
- sociallogin,
- data):
- u = DefaultSocialAccountAdapter.populate_user(self,request,sociallogin,data)
- if not u.email.split('@')[1] == "fairphone.com":
- raise PermissionDenied()
+
+ def populate_user(self, request, sociallogin, data):
+ u = DefaultSocialAccountAdapter.populate_user(
+ self, request, sociallogin, data
+ )
+ if not u.email.split("@")[1] == "fairphone.com":
+ raise PermissionDenied()
return u
+
class FormAccountAdapter(DefaultAccountAdapter):
def is_open_for_signup(self, request):
return False
diff --git a/hiccup/apps.py b/hiccup/apps.py
index d651203..dacae51 100644
--- a/hiccup/apps.py
+++ b/hiccup/apps.py
@@ -4,4 +4,4 @@
class crashreportsConfig(AppConfig):
- name = 'crashreports'
+ name = "crashreports"
diff --git a/hiccup/settings.py b/hiccup/settings.py
index f20c3c8..a401d04 100644
--- a/hiccup/settings.py
+++ b/hiccup/settings.py
@@ -20,85 +20,84 @@
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = '7u+#ha3hk!x+*)clhs46%n*)w1q+5s4+yoc#1!nm0b%fwwrud@'
+SECRET_KEY = "7u+#ha3hk!x+*)clhs46%n*)w1q+5s4+yoc#1!nm0b%fwwrud@"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
-ALLOWED_HOSTS = ['*']
+ALLOWED_HOSTS = ["*"]
# Application definition
INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'rest_framework',
- 'rest_framework.authtoken',
- 'crashreports',
- 'crashreport_stats',
- 'taggit',
- 'crispy_forms',
- 'bootstrap3',
- 'bootstrapform',
- 'django_extensions',
- 'django_filters',
- 'djfrontend',
- 'djfrontend.skeleton',
- 'allauth',
- 'allauth.account',
- 'allauth.socialaccount',
- 'allauth.socialaccount.providers.google',
- 'drf_yasg'
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.sites",
+ "django.contrib.messages",
+ "django.contrib.staticfiles",
+ "rest_framework",
+ "rest_framework.authtoken",
+ "crashreports",
+ "crashreport_stats",
+ "taggit",
+ "crispy_forms",
+ "bootstrap3",
+ "bootstrapform",
+ "django_extensions",
+ "django_filters",
+ "djfrontend",
+ "djfrontend.skeleton",
+ "allauth",
+ "allauth.account",
+ "allauth.socialaccount",
+ "allauth.socialaccount.providers.google",
+ "drf_yasg",
]
MIDDLEWARE_CLASSES = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ "django.middleware.security.SecurityMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.auth.middleware.SessionAuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
]
-ROOT_URLCONF = 'hiccup.urls'
+ROOT_URLCONF = "hiccup.urls"
TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.Loader',
- 'django.template.loaders.app_directories.Loader',
+ "django.template.loaders.filesystem.Loader",
+ "django.template.loaders.app_directories.Loader",
)
TEMPLATES = [
{
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [os.path.join(BASE_DIR, 'hiccup', 'templates')],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [os.path.join(BASE_DIR, "hiccup", "templates")],
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": [
+ "django.template.context_processors.debug",
+ "django.template.context_processors.request",
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
# `allauth` needs this from django
- 'django.template.context_processors.request',
- ],
+ "django.template.context_processors.request",
+ ]
},
- },
+ }
]
AUTHENTICATION_BACKENDS = (
# Needed to login by username in Django admin, regardless of `allauth`
- 'django.contrib.auth.backends.ModelBackend',
-
+ "django.contrib.auth.backends.ModelBackend",
# `allauth` specific authentication methods, such as login by e-mail
- 'allauth.account.auth_backends.AuthenticationBackend',
+ "allauth.account.auth_backends.AuthenticationBackend",
)
# WSGI_APPLICATION = 'hiccup_server.wsgi.application'
@@ -108,9 +107,9 @@
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+ "default": {
+ "ENGINE": "django.db.backends.sqlite3",
+ "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
@@ -120,46 +119,46 @@
AUTH_PASSWORD_VALIDATORS = [
{
- 'NAME': 'django.contrib.auth.password_validation'
- '.UserAttributeSimilarityValidator',
+ "NAME": "django.contrib.auth.password_validation"
+ ".UserAttributeSimilarityValidator"
},
{
- 'NAME': 'django.contrib.auth.password_validation'
- '.MinimumLengthValidator',
+ "NAME": "django.contrib.auth.password_validation"
+ ".MinimumLengthValidator"
},
{
- 'NAME': 'django.contrib.auth.password_validation'
- '.CommonPasswordValidator',
+ "NAME": "django.contrib.auth.password_validation"
+ ".CommonPasswordValidator"
},
{
- 'NAME': 'django.contrib.auth.password_validation'
- '.NumericPasswordValidator',
+ "NAME": "django.contrib.auth.password_validation"
+ ".NumericPasswordValidator"
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
-LANGUAGE_CODE = 'en-us'
-TIME_ZONE = 'Europe/Amsterdam'
+LANGUAGE_CODE = "en-us"
+TIME_ZONE = "Europe/Amsterdam"
USE_I18N = True
USE_L10N = True
USE_TZ = True
REST_FRAMEWORK = {
- 'DEFAULT_AUTHENTICATION_CLASSES': (
- 'rest_framework.authentication.BasicAuthentication',
- 'rest_framework.authentication.SessionAuthentication',
- 'rest_framework.authentication.TokenAuthentication',
+ "DEFAULT_AUTHENTICATION_CLASSES": (
+ "rest_framework.authentication.BasicAuthentication",
+ "rest_framework.authentication.SessionAuthentication",
+ "rest_framework.authentication.TokenAuthentication",
),
- 'DEFAULT_PERMISSION_CLASSES': (
- 'rest_framework.permissions.IsAuthenticated',
+ "DEFAULT_PERMISSION_CLASSES": (
+ "rest_framework.permissions.IsAuthenticated",
),
- 'DEFAULT_FILTER_BACKENDS':
- ('django_filters.rest_framework.DjangoFilterBackend',),
- 'DEFAULT_PAGINATION_CLASS':
- 'rest_framework.pagination.LimitOffsetPagination',
- 'PAGE_SIZE': 100
+ "DEFAULT_FILTER_BACKENDS": (
+ "django_filters.rest_framework.DjangoFilterBackend",
+ ),
+ "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
+ "PAGE_SIZE": 100,
}
SITE_ID = 1
@@ -172,44 +171,35 @@
ACCOUNT_LOGOUT_REDIRECT_URL = "/accounts/login/"
SOCIALACCOUNT_PROVIDERS = {
- 'google': {
- 'SCOPE': [
- 'profile',
- 'email',
- ],
- 'AUTH_PARAMS': {
- 'access_type': 'online',
- }
+ "google": {
+ "SCOPE": ["profile", "email"],
+ "AUTH_PARAMS": {"access_type": "online"},
}
}
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
-STATIC_URL = '/static/'
+STATIC_URL = "/static/"
-STATIC_ROOT = os.path.join(BASE_DIR, 'static')
+STATIC_ROOT = os.path.join(BASE_DIR, "static")
# Logging
# https://docs.djangoproject.com/en/2.0/topics/logging/#examples
LOGGING = {
- 'version': 1,
- 'disable_existing_loggers': False,
- 'handlers': {
- 'file': {
- 'level': 'DEBUG',
- 'class': 'logging.FileHandler',
- 'filename': os.path.join(BASE_DIR, 'debug.log'),
- },
+ "version": 1,
+ "disable_existing_loggers": False,
+ "handlers": {
+ "file": {
+ "level": "DEBUG",
+ "class": "logging.FileHandler",
+ "filename": os.path.join(BASE_DIR, "debug.log"),
+ }
},
- 'loggers': {
- 'django': {
- 'handlers': ['file'],
- 'level': 'DEBUG',
- 'propagate': True,
- },
+ "loggers": {
+ "django": {"handlers": ["file"], "level": "DEBUG", "propagate": True}
},
}
@@ -217,9 +207,7 @@
# Automatic documentation generation
# https://drf-yasg.readthedocs.io/en/stable/index.html
-SWAGGER_SETTINGS = {
- 'DEFAULT_INFO': 'hiccup.urls.api_info',
-}
+SWAGGER_SETTINGS = {"DEFAULT_INFO": "hiccup.urls.api_info"}
try:
from local_settings import * # noqa: F403, F401
diff --git a/hiccup/urls.py b/hiccup/urls.py
index 3a6cf76..83fb1e0 100644
--- a/hiccup/urls.py
+++ b/hiccup/urls.py
@@ -4,14 +4,11 @@
from django.contrib import admin
from drf_yasg import openapi
-api_info = openapi.Info(
- title="Hiccup API",
- default_version='v1',
-)
+api_info = openapi.Info(title="Hiccup API", default_version="v1")
urlpatterns = [
- url(r'^hiccup/admin/', admin.site.urls),
- url(r'^hiccup/', include('crashreports.urls')),
- url(r'^hiccup_stats/', include('crashreport_stats.urls')),
- url(r'^accounts/', include('allauth.urls')),
+ url(r"^hiccup/admin/", admin.site.urls),
+ url(r"^hiccup/", include("crashreports.urls")),
+ url(r"^hiccup_stats/", include("crashreport_stats.urls")),
+ url(r"^accounts/", include("allauth.urls")),
]
diff --git a/hiccup/wsgi.py b/hiccup/wsgi.py
index 7f02368..025bc4e 100644
--- a/hiccup/wsgi.py
+++ b/hiccup/wsgi.py
@@ -6,7 +6,9 @@
"""
import os
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "hiccup.settings")
from django.core.wsgi import get_wsgi_application
+
application = get_wsgi_application()
diff --git a/tools/bench.py b/tools/bench.py
index 6d43672..4e40194 100644
--- a/tools/bench.py
+++ b/tools/bench.py
@@ -4,47 +4,51 @@
clients = []
-BASE_URL = 'http://127.0.0.1:8000/hiccup/'
+BASE_URL = "http://127.0.0.1:8000/hiccup/"
+
def create_client():
ret_client = {}
- register_url = 'api/v1/devices/register/'
- params = {
- 'board_date': "2017-01-01",
- 'chipset': "HICCUPBENCH"
- }
- resp = requests.post(BASE_URL+register_url, params)
+ register_url = "api/v1/devices/register/"
+ params = {"board_date": "2017-01-01", "chipset": "HICCUPBENCH"}
+ resp = requests.post(BASE_URL + register_url, params)
data = json.loads(resp.text)
- ret_client['token'] = data['token']
- ret_client['uuid'] = data['uuid']
+ ret_client["token"] = data["token"]
+ ret_client["uuid"] = data["uuid"]
return ret_client
def send_heartbeat(client):
- heartbeat_url = 'api/v1/heartbeats/'
+ heartbeat_url = "api/v1/heartbeats/"
params = {
- 'uuid': client['uuid'],
- 'build_fingerprint': 'HICCUPBENCH',
- 'uptime': "string",
- 'date': "1984-06-02T19:05:00.000Z",
- 'app_version' : 2000000
+ "uuid": client["uuid"],
+ "build_fingerprint": "HICCUPBENCH",
+ "uptime": "string",
+ "date": "1984-06-02T19:05:00.000Z",
+ "app_version": 2000000,
}
- resp = requests.post(BASE_URL + heartbeat_url, params,
- headers = {'Authorization': 'Token '+ client['token']})
+ resp = requests.post(
+ BASE_URL + heartbeat_url,
+ params,
+ headers={"Authorization": "Token " + client["token"]},
+ )
return resp
+
import time
+
def bench(client):
start_time = time.time()
for i in range(50):
send_heartbeat(client)
end_time = time.time()
- print(end_time-start_time)
+ print(end_time - start_time)
+
clients = []
-for i in range(20):
- clients = clients + [create_client()]
+for i in range(20):
+ clients = clients + [create_client()]
with Pool(20) as p:
p.map(bench, clients)