Add version overview page
diff --git a/crashreport_stats/admin.py b/crashreport_stats/admin.py
index 8c38f3f..777a09e 100644
--- a/crashreport_stats/admin.py
+++ b/crashreport_stats/admin.py
@@ -1,3 +1,11 @@
from django.contrib import admin
+from crashreport_stats.models import *
-# Register your models here.
+@admin.register(Version)
+class VersionAdmin(admin.ModelAdmin):
+ pass
+
+@admin.register(VersionDaily)
+class VersionDailyAdmin(admin.ModelAdmin):
+ list_display=('version','date')
+ pass
diff --git a/crashreport_stats/migrations/0001_initial.py b/crashreport_stats/migrations/0001_initial.py
new file mode 100644
index 0000000..b87395e
--- /dev/null
+++ b/crashreport_stats/migrations/0001_initial.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.2 on 2017-05-09 13:01
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+from django.db import connection
+from datetime import date, timedelta
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ 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()),
+ ],
+ ),
+ migrations.CreateModel(
+ 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')),
+ ],
+ ),
+ ]
diff --git a/crashreport_stats/models.py b/crashreport_stats/models.py
index 71a8362..04f38ab 100644
--- a/crashreport_stats/models.py
+++ b/crashreport_stats/models.py
@@ -1,3 +1,76 @@
from django.db import models
+from crashreports.models import *
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+import datetime
-# Create your models here.
+
+def getVersion(build_fingerprint):
+ v= None
+ try:
+ v = Version.objects.get(build_fingerprint=build_fingerprint)
+ except Version.DoesNotExist:
+ v =Version(build_fingerprint=build_fingerprint,
+ first_seen_on=datetime.date.today(),
+ released_on=datetime.date.today(),
+ heartbeats=0, prob_crashes=0, smpl=0, other=0)
+ v.save()
+ return v
+
+def getVersionDaily(version,day):
+ try:
+ v = VersionDaily.objects.get(version=version, date=day)
+ except VersionDaily.DoesNotExist:
+ v =VersionDaily(version=version, date=day,
+ heartbeats=0, prob_crashes=0, smpl=0, other=0)
+ return v
+
+@receiver(post_save, sender=Crashreport)
+def on_crashreport_create(sender, **kwargs):
+ crashreport = kwargs.get('instance')
+ v= getVersion(crashreport.build_fingerprint)
+ vd = getVersionDaily(v, crashreport.date.date())
+ if crashreport.boot_reason == "RTC alarm":
+ v.smpl = v.smpl + 1
+ vd.smpl = vd.smpl + 1
+ elif crashreport.boot_reason in ["UNKNOWN", "keyboard power on"]:
+ v.prob_crashes = v.prob_crashes + 1
+ vd.prob_crashes = vd.prob_crashes + 1
+ else:
+ v.other = v.other + 1
+ vd.other = vd.other + 1
+ v.save()
+ vd.save()
+
+@receiver(post_save, sender=HeartBeat)
+def on_heartbeat_create(sender, **kwargs):
+ hb = kwargs.get('instance')
+ v = getVersion(hb.build_fingerprint)
+ vd = getVersionDaily(v, hb.date)
+ v.heartbeats = v.heartbeats + 1
+ vd.heartbeats = vd.heartbeats + 1
+ v.save()
+ vd.save()
+
+
+class Version(models.Model):
+ 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()
+ def __str__(self):
+ return self.build_fingerprint
+
+
+class VersionDaily(models.Model):
+ version = models.ForeignKey(Version, db_index=True, related_name='daily_stats', on_delete=models.CASCADE)
+ date = models.DateField()
+ heartbeats = models.IntegerField()
+ prob_crashes = models.IntegerField()
+ smpl = models.IntegerField()
+ other = models.IntegerField()
diff --git a/crashreport_stats/rest_endpoints.py b/crashreport_stats/rest_endpoints.py
index e4f9ae8..b7c07dc 100644
--- a/crashreport_stats/rest_endpoints.py
+++ b/crashreport_stats/rest_endpoints.py
@@ -5,8 +5,12 @@
from crashreports.permissions import HasRightsOrIsDeviceOwnerDeviceCreation
from django.db import connection
from . import raw_querys
+from crashreport_stats.models import *
import zipfile
from crashreports.models import *
+from django.db.models.expressions import F
+import django_filters
+from rest_framework import filters
def dictfetchall(cursor):
"Returns all rows from a cursor as a dict"
@@ -74,3 +78,51 @@
fo = zf.open(f)
ret[f.filename] = fo.read()
return Response(ret)
+
+
+class VersionFilter(filters.FilterSet):
+ first_seen_before = django_filters.DateFilter(name="first_seen_on", lookup_expr='lte')
+ first_seen_after = django_filters.DateFilter(name="first_seen_on", lookup_expr='gte')
+ released_before = django_filters.DateFilter(name="released_on", lookup_expr='lte')
+ released_after = django_filters.DateFilter(name="released_on", lookup_expr='gte')
+ class Meta:
+ model = Version
+
+class VersionSerializer(serializers.ModelSerializer):
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ class Meta:
+ model = Version
+
+class VersionListView(generics.ListAPIView):
+
+ queryset = Version.objects.all().order_by('-heartbeats')
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ serializer_class = VersionSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_class = (VersionFilter)
+
+
+class VersionDailyFilter(filters.FilterSet):
+ date_start = django_filters.DateFilter(name="date", lookup_expr='gte')
+ date_end = django_filters.DateFilter(name="date", lookup_expr='lte')
+ version__build_fingerprint = django_filters.CharFilter()
+ version__is_official_release = django_filters.BooleanFilter()
+ version__is_beta_release = django_filters.BooleanFilter()
+ class Meta:
+ model = VersionDaily
+
+
+class VersionDailySerializer(serializers.ModelSerializer):
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ build_fingerprint = serializers.CharField()
+ class Meta:
+ model = VersionDaily
+
+
+class VersionDailyListView(generics.ListAPIView):
+ queryset = VersionDaily.objects.annotate(build_fingerprint=F('version__build_fingerprint')).all()
+ permission_classes = (HasRightsOrIsDeviceOwnerDeviceCreation, )
+ filter_backends = (filters.DjangoFilterBackend,)
+ serializer_class = VersionDailySerializer
+ filter_class = (VersionDailyFilter)
+ filter_fields = ('version__build_fingerprint','version__is_official_release','version__is_beta_release',)
diff --git a/crashreport_stats/templates/crashreport_stats/tags/versions_area_chart.html b/crashreport_stats/templates/crashreport_stats/tags/versions_area_chart.html
new file mode 100644
index 0000000..8935239
--- /dev/null
+++ b/crashreport_stats/templates/crashreport_stats/tags/versions_area_chart.html
@@ -0,0 +1,68 @@
+<div id = "{{element_name}}" class="panel line_chart"> </div>
+<script>
+google.charts.setOnLoadCallback(drawChartReportPerDay);
+
+var groupByDate = function(xs) {
+ key = 'date'
+ return xs.reduce(function(rv, x) {
+ (rv[x[key]] = rv[x[key]] || []).push(x);
+ return rv;
+ }, {});
+};
+
+function drawChartReportPerDay(element) {
+ element = "heartbeats"
+ var chart = new google.visualization.AreaChart(document.getElementById("{{element_name}}"));
+ var data = new google.visualization.DataTable();
+ var options = {
+ title: "{{ title }}",
+ isStacked: 'relative',
+ chartArea: {
+ left:0,
+ right:0,
+ top:50,
+ bottom:50,
+ },
+ legend : {
+ position:'top',
+ }
+ };
+ $.getJSON( "{% url 'hiccup_stats_api_v1_version_daily' %}",
+ {
+ version__is_official_release: "{{ is_official_release }}",
+ version__is_beta_release: "{{ is_beta_release }}",
+ limit:10000,
+ date_start: "2017-01-01",
+ date_end: new Date(new Date().setDate(new Date().getDate())).toISOString().split('T')[0],
+ },
+ function( json_response ) {
+ res = [];
+ if (json_response.results)
+ res = json_response.results;
+ else
+ res = json_response;
+ tmp = {}
+ res.forEach(function(n){
+ tmp[n.date]={}
+ res.forEach(function(m){
+ tmp[n.date][m.build_fingerprint] = 0;
+ });
+ });
+ res.forEach(function(n){
+ all_fingerprints= Object.keys(tmp[n.date]);
+ tmp[n.date][n.build_fingerprint] = n.heartbeats;
+ });
+ reformated_array = []
+ all_dates = Object.keys(tmp);
+ all_dates.forEach(function(date) {
+ reformated_array.push([new Date(date),].concat(all_fingerprints.map(function(o){return tmp[date][o]})));
+ });
+ data.addColumn('date', 'date');
+ all_fingerprints.forEach(function(fingerprint){
+ data.addColumn('number', fingerprint.split('/')[4]);
+ });
+ data.addRows(reformated_array);
+ chart.draw(data, options);
+ });
+}
+</script>
diff --git a/crashreport_stats/templates/crashreport_stats/tags/versions_bar_chart.html b/crashreport_stats/templates/crashreport_stats/tags/versions_bar_chart.html
new file mode 100644
index 0000000..d90df30
--- /dev/null
+++ b/crashreport_stats/templates/crashreport_stats/tags/versions_bar_chart.html
@@ -0,0 +1,49 @@
+<div id = "{{element_name}}" class="panel line_chart"> </div>
+<script>
+google.charts.setOnLoadCallback(drawChartReportPerDay);
+
+var groupByDate = function(xs) {
+ key = 'date'
+ return xs.reduce(function(rv, x) {
+ (rv[x[key]] = rv[x[key]] || []).push(x);
+ return rv;
+ }, {});
+};
+
+function drawChartReportPerDay(element) {
+ element = "heartbeats"
+ var chart = new google.visualization.BarChart(document.getElementById("{{element_name}}"));
+ var data = new google.visualization.DataTable();
+ var options = {
+ title: "{{ title }}",
+ chartArea: {
+ left:100,
+ right:0,
+ top:50,
+ bottom:50,
+ },
+ legend : {
+ position:'top',
+ }
+ };
+ $.getJSON( "{% url 'hiccup_stats_api_v1_versions' %}",
+ { limit: 5,
+ is_official_release: "{{ is_official_release }}",
+ is_beta_release: "{{ is_beta_release }}"
+ }, function( json_response ) {
+ res = [];
+ if (json_response.results)
+ res = json_response.results;
+ else
+ res = json_response;
+ reformated_array = res.map(function(obj) {
+ ret = [obj.build_fingerprint.split('/')[4], obj.prob_crashes/(obj.heartbeats*1.0)];
+ return ret;
+ });
+ data.addColumn('string', 'Version');
+ data.addColumn('number', 'Prob. Crashes');
+ data.addRows(reformated_array);
+ chart.draw(data, options);
+ });
+}
+</script>
diff --git a/crashreport_stats/templates/crashreport_stats/tags/versions_pie_chart.html b/crashreport_stats/templates/crashreport_stats/tags/versions_pie_chart.html
new file mode 100644
index 0000000..1108c58
--- /dev/null
+++ b/crashreport_stats/templates/crashreport_stats/tags/versions_pie_chart.html
@@ -0,0 +1,44 @@
+<div id = "{{element_name}}" class="panel line_chart"> </div>
+<script>
+google.charts.setOnLoadCallback(drawChartReportPerDay);
+
+function drawChartReportPerDay(element) {
+ element = "heartbeats"
+ var chart = new google.visualization.PieChart(document.getElementById("{{element_name}}"));
+ var data = new google.visualization.DataTable();
+ var options = {
+ title: "{{ title }}",
+ pieHole: 0.4,
+ sliceVisibilityThreshold: .01,
+ legend: {
+ position: "labeled",
+ },
+ chartArea: {
+ left:0,
+ right:0,
+ top:50,
+ bottom:0,
+ },
+ };
+ $.getJSON( "{% url 'hiccup_stats_api_v1_version_daily' %}",
+ {
+ date: new Date(new Date().setDate(new Date().getDate()-1)).toISOString().split('T')[0],
+ version__is_official_release: "{{ is_official_release }}",
+ version__is_beta_release: "{{ is_beta_release }}",
+ }, function( json_response ) {
+ res = [];
+ if (json_response.results)
+ res = json_response.results;
+ else
+ res = json_response;
+ reformated_array = res.map(function(obj) {
+ ret = [obj.build_fingerprint.split('/')[4], obj.heartbeats];
+ return ret;
+ });
+ data.addColumn('string', 'Version');
+ data.addColumn('number', 'Heartbeats');
+ data.addRows(reformated_array);
+ chart.draw(data, options);
+ });
+}
+</script>
diff --git a/crashreport_stats/templates/crashreport_stats/tags/versions_table.html b/crashreport_stats/templates/crashreport_stats/tags/versions_table.html
new file mode 100644
index 0000000..5767aba
--- /dev/null
+++ b/crashreport_stats/templates/crashreport_stats/tags/versions_table.html
@@ -0,0 +1,47 @@
+<div class="panel datatable" >
+ <h2>{{ title }}</h2>
+ <table id = "{{element_name}}" class="display table table-striped" ></table>
+ </div>
+<script>
+
+$(document).ready(function() {
+ console.log("Generating report table");
+ $.getJSON( "{% url 'hiccup_stats_api_v1_versions' %}",
+ {
+ limit:200,
+ is_official_release: "{{ is_official_release }}",
+ is_beta_release: "{{ is_beta_release }}",
+ }, function(json_response) {
+ console.log("Generating update history table");
+ console.log(json_response);
+ dataSet = json_response.results.map(function(obj) {
+ ret = [
+ obj.build_fingerprint.split('/')[4],
+ obj.heartbeats,
+ (obj.prob_crashes/(obj.heartbeats * 1.0)),
+ (obj.smpl/(obj.heartbeats * 1.0)),
+ ];
+ return ret;
+ });
+
+ $('#{{element_name}}').DataTable( {
+ data: dataSet,
+ columns: [
+ { title: "Version" },
+ { title: "HB"},
+ { title: "PC",
+ render: function(n) {
+ return n.toFixed(2);
+ }
+ },
+ { title: "SMPLs",
+ render: function(n) {
+ return n.toFixed(2);
+ }
+ },
+ ]
+ } );
+});
+});
+
+</script>
diff --git a/crashreport_stats/templates/crashreport_stats/versions.html b/crashreport_stats/templates/crashreport_stats/versions.html
new file mode 100644
index 0000000..3863b2c
--- /dev/null
+++ b/crashreport_stats/templates/crashreport_stats/versions.html
@@ -0,0 +1,21 @@
+{% extends "base.html" %}
+ {% block content %}
+ {% load crashreport_stats_tags %}
+ <h1> Versions Overview </h1>
+ <div class="row">
+ <div class="col-md-6">
+ {% versions_table is_official_release=is_official_release%}
+ </div>
+ <div class="col-md-6">
+ {% versions_pie_chart is_official_release=is_official_release%}
+ </div>
+ <div class="col-md-6">
+ {% versions_bar_chart is_official_release=is_official_release%}
+ </div>
+ <div class="col-md-6">
+ </div>
+ </div>
+ <div class="col-md-12">
+ {% versions_area_chart is_official_release=is_official_release%}
+ </div>
+ {% endblock content %}
diff --git a/crashreport_stats/templatetags/crashreport_stats_tags.py b/crashreport_stats/templatetags/crashreport_stats_tags.py
index c653f9c..235175d 100644
--- a/crashreport_stats/templatetags/crashreport_stats_tags.py
+++ b/crashreport_stats/templatetags/crashreport_stats_tags.py
@@ -34,3 +34,36 @@
'uuid': uuid,
"title": title,
"element_name": "device_report_history"})
+
+
+@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"})
+
+@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/urls.py b/crashreport_stats/urls.py
index de8c55b..a80425c 100644
--- a/crashreport_stats/urls.py
+++ b/crashreport_stats/urls.py
@@ -10,6 +10,12 @@
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-]+)/$',
rest_endpoints.DeviceStat.as_view(),
name='hiccup_stats_api_v1_device_overview'),
@@ -19,7 +25,16 @@
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]+)/$',
rest_endpoints.LogFileDownload.as_view(),
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/$',
+ rest_endpoints.VersionDailyListView.as_view(),
+ name='hiccup_stats_api_v1_version_daily'),
]
diff --git a/crashreport_stats/util.py b/crashreport_stats/util.py
new file mode 100644
index 0000000..316ab53
--- /dev/null
+++ b/crashreport_stats/util.py
@@ -0,0 +1,80 @@
+from django.db import migrations, models
+import django.db.models.deletion
+
+from django.db import connection
+from datetime import date, timedelta
+
+from . import models as myModels
+
+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()
+ ]
+
+@transaction.atomic
+def fill_version_data():
+ myModels.Version.objects.all().delete()
+ 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,
+ ( select count(id) from crashreports_crashreport where boot_reason not in ("RTC alarm", "UNKNOWN", "keyboard power on") and crashreports_crashreport.build_fingerprint = fingerprint) as other,
+ ( 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;'''
+ cursor = connection.cursor()
+ 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']
+ )
+ version.save()
+
+@transaction.atomic
+def fill_version_daily_data():
+ myModels.VersionDaily.objects.all().delete()
+ 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'''
+ start = date(2016, 8, 1)
+ 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)
+ 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'],
+ date=day,
+ prob_crashes = i['prob_crashes'],
+ smpl = i['SMPL'],
+ other = i['other']
+ )
+ except:
+ 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 193133f..49313eb 100644
--- a/crashreport_stats/views.py
+++ b/crashreport_stats/views.py
@@ -25,7 +25,17 @@
if not Device.objects.filter(uuid=uuid).exists():
raise Http404("Device doesn't exist.")
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))
+
+@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))
+
@user_passes_test(is_fairphone_staff)
def home(request):
if request.method == 'POST':
diff --git a/crashreports/admin.py b/crashreports/admin.py
index a507218..7133070 100644
--- a/crashreports/admin.py
+++ b/crashreports/admin.py
@@ -5,7 +5,6 @@
from crashreports.models import LogFile
-
@admin.register(Crashreport)
class CrashreportAdmin(admin.ModelAdmin):
pass