WIP: add logfile and local IDs
diff --git a/crashreports/admin.py b/crashreports/admin.py
index 1894034..66e2994 100644
--- a/crashreports/admin.py
+++ b/crashreports/admin.py
@@ -1,6 +1,7 @@
 from django.contrib import admin
 from crashreports.models import Crashreport
 
+
 @admin.register(Crashreport)
 class CrashreportAdmin(admin.ModelAdmin):
     pass
diff --git a/crashreports/migrations/0001_initial.py b/crashreports/migrations/0001_initial.py
index 203f616..78c5144 100644
--- a/crashreports/migrations/0001_initial.py
+++ b/crashreports/migrations/0001_initial.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Generated by Django 1.10.2 on 2016-10-11 14:04
+# Generated by Django 1.10.2 on 2016-10-13 09:07
 from __future__ import unicode_literals
 
 import crashreports.models
@@ -32,16 +32,20 @@
                 ('power_on_reason', models.CharField(max_length=200)),
                 ('power_off_reason', models.CharField(max_length=200)),
                 ('date', models.DateTimeField()),
+                ('device_local_id', models.IntegerField()),
             ],
         ),
         migrations.CreateModel(
             name='Device',
             fields=[
-                ('uuid', models.CharField(default=uuid.uuid4, editable=False, max_length=32, primary_key=True, serialize=False)),
-                ('iemi', models.CharField(blank=True, max_length=32, null=True)),
+                ('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_device_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)),
             ],
@@ -61,10 +65,9 @@
             name='LogFile',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('logfile_type', models.TextField(max_length=36)),
-                ('crashreport_file', models.FileField(upload_to=crashreports.models.crashreport_file_name)),
+                ('logfile_type', models.TextField(default='last_kmsg', max_length=36)),
+                ('logfile', models.FileField(upload_to=crashreports.models.crashreport_file_name)),
                 ('crashreport', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crashreports.Crashreport')),
-                ('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='crashreports.Device')),
             ],
         ),
         migrations.AddField(
diff --git a/crashreports/models.py b/crashreports/models.py
index 449db3c..96507b0 100644
--- a/crashreports/models.py
+++ b/crashreports/models.py
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 from django.db import models
+from django.db import transaction
 
 from django.contrib.auth.models import User
 from taggit.managers import TaggableManager
@@ -20,6 +21,22 @@
     tags = TaggableManager(blank=True)
     last_heartbeat = models.DateTimeField(null=True, blank=True)
     token = models.CharField(max_length=200, null=True, blank=True)
+    next_per_crashreport_key = models.PositiveIntegerField(default=1)
+    next_per_heartbeat_key = models.PositiveIntegerField(default=1)
+
+    @transaction.atomic
+    def get_crashreport_key(self):
+        ret = self.next_per_crashreport_key
+        self.next_per_crashreport_key = self.next_per_crashreport_key + 1
+        self.save()
+        return ret
+
+    @transaction.atomic
+    def get_heartbeat_key(self):
+        ret = self.next_per_heartbeat_key
+        self.next_per_crashreport_key = self.next_per_heartbeat_key + 1
+        self.save()
+        return ret
 
 
 def crashreport_file_name(instance, filename):
@@ -42,6 +59,20 @@
     power_off_reason = models.CharField(max_length=200)
     date = models.DateTimeField()
     tags = TaggableManager(blank=True)
+    device_local_id = models.PositiveIntegerField(blank=True)
+    next_crashreport_key = models.PositiveIntegerField(default=1)
+
+    @transaction.atomic
+    def get_logfile_key(self):
+        ret = self.next_logfile_key
+        self.next_logfile_key = self.next_logfile_key + 1
+        self.save()
+        return ret
+
+    def save(self, *args, **kwargs):
+        if not self.device_local_id:
+            self.device_local_id = self.device.get_crashreport_key()
+        super(Crashreport, self).save(*args, **kwargs)
 
     def _get_uuid(self):
         "Returns the person's full name."
@@ -53,6 +84,12 @@
     logfile_type = models.TextField(max_length=36, default="last_kmsg")
     crashreport = models.ForeignKey(Crashreport, on_delete=models.CASCADE)
     logfile = models.FileField(upload_to=crashreport_file_name)
+    crashreport_local_id = models.PositiveIntegerField(blank=True)
+
+    def save(self, *args, **kwargs):
+        if not self.device_local_id:
+            self.device_local_id = self.crashreport.next_logfile_key()
+        super(LogFile, self).save(*args, **kwargs)
 
 
 class HeartBeat(models.Model):
@@ -61,6 +98,12 @@
     uptime = models.CharField(max_length=200)
     build_fingerprint = models.CharField(max_length=200)
     date = models.DateTimeField()
+    device_local_id = models.PositiveIntegerField(blank=True)
+
+    def save(self, *args, **kwargs):
+        if not self.device_local_id:
+            self.device_local_id = self.device.get_heartbeat_key()
+        super(HeartBeat, self).save(*args, **kwargs)
 
     def _get_uuid(self):
         "Returns the person's full name."
diff --git a/crashreports/serializers.py b/crashreports/serializers.py
index 1ef8dbd..99d3e43 100644
--- a/crashreports/serializers.py
+++ b/crashreports/serializers.py
@@ -1,15 +1,31 @@
 from rest_framework import serializers
+from rest_framework.utils.serializer_helpers import ReturnDict
 
 from rest_framework.exceptions import NotFound
 from crashreports.models import Crashreport
 from crashreports.models import Device
 from crashreports.models import HeartBeat
 from rest_framework import permissions
+from crashreports.permissions import user_is_hiccup_staff
+
+
+class PrivateField(serializers.ReadOnlyField):
+
+    def get_attribute(self, instance):
+        """
+        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):
+            return super(PrivateField, self).get_attribute(instance)
+        return -1
 
 
 class CrashReportSerializer(serializers.ModelSerializer):
     permission_classes = (permissions.AllowAny,)
     uuid = serializers.CharField(max_length=64)
+    id = PrivateField()
+    device_local_id = serializers.IntegerField(required=False)
 
     class Meta:
         model = Crashreport
@@ -30,7 +46,9 @@
 class HeartBeatSerializer(serializers.ModelSerializer):
     permission_classes = (permissions.AllowAny,)
     uuid = serializers.CharField(max_length=64)
-
+    id = PrivateField()
+    device_local_id = serializers.IntegerField(required=False)
+    
     class Meta:
         model = HeartBeat
         exclude = ('device',)
diff --git a/crashreports/tests.py b/crashreports/tests.py
index 43acf1d..53bdfcf 100644
--- a/crashreports/tests.py
+++ b/crashreports/tests.py
@@ -80,19 +80,31 @@
 class HeartbeatListTestCase(APITestCase):
 
     def setUp(self):
+        self.setup_users()
+        self.data = self.create_dummy_data(self.uuid)
+        self.url = "/hiccup/api/v1/heartbeats/"
+        
+
+    def setup_users(self):
         self.password = "test"
-        self.admin = User.objects.create_superuser(
-            'myuser', 'myemail@test.com', self.password)
-        # we need a device
         request = self.client.post("/hiccup/api/v1/devices/register/", {})
         self.uuid = request.data['uuid']
         self.token = request.data['token']
         request = self.client.post("/hiccup/api/v1/devices/register/", {})
         self.other_uuid = request.data['uuid']
         self.other_token = request.data['token']
-        self.data = self.create_dummy_data(self.uuid)
-        self.url = "/hiccup/api/v1/heartbeats/"
+        self.admin = User.objects.create_superuser(
+            'myuser', 'myemail@test.com', self.password)
+        self.admin = APIClient()
+        self.admin.login(username='myuser', password='test')
+        self.user = APIClient()
+        self.other_user = APIClient()
+        self.user.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
+        self.other_user.credentials(HTTP_AUTHORIZATION='Token '
+                                    + self.other_token)
+        self.noauth_client = APIClient()
 
+                                    
     def create_dummy_data(self, uuid="not set"):
         return {
             'uuid': uuid,
@@ -103,99 +115,111 @@
         }
 
     def test_create_no_auth(self):
-        client = APIClient()
-        request = client.post(self.url, self.data)
+        request = self.noauth_client.post(self.url, self.data)
         self.assertEqual(request.status_code, 401)
 
     def test_create_as_admin(self):
-        client = APIClient()
-        client.login(username='myuser', password='test')
-        request = client.post(self.url, self.data)
+        request = self.admin.post(self.url, self.data)
         self.assertEqual(request.status_code, 201)
-        client.logout()
+        self.assertTrue(request.data['id'] > 0)
 
     def test_create_as_admin_not_existing_device(self):
-        client = APIClient()
-        client.login(username='myuser', password='test')
-        request = client.post(self.url,
-                              self.create_dummy_data())
+        request = self.admin.post(self.url,
+                                  self.create_dummy_data())
         self.assertEqual(request.status_code, 404)
-        client.logout()
 
     def test_create_as_uuid_owner(self):
-        client = APIClient()
-        client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
-        request = client.post(self.url,
-                              self.create_dummy_data(self.uuid))
+        request = self.user.post(self.url,
+                                 self.create_dummy_data(self.uuid))
         self.assertEqual(request.status_code, 201)
-        client.credentials()
+        self.assertTrue(request.data['id'] == -1)
 
     def test_create_as_uuid_not_owner(self):
-        client = APIClient()
-        client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
-        request = client.post(self.url,
-                              self.create_dummy_data(self.other_uuid))
+        request = self.user.post(self.url,
+                                 self.create_dummy_data(self.other_uuid))
         self.assertEqual(request.status_code, 403)
-        client.credentials()
 
-    def post_heartbeats(self, client, heartbeat, count=5):
+    def post_multiple(self, client, data, count=5):
         for i in range(count):
-            client.post(self.url, heartbeat)
+            client.post(self.url, data)
 
     def test_list(self):
         count = 5
-        client = APIClient()
-        client.login(username='myuser', password='test')
-        self.post_heartbeats(client, self.data, count)
-        request = client.get(self.url)
+        self.post_multiple(self.user, self.data, count)
+        request = self.admin.get(self.url)
         self.assertEqual(request.status_code, 200)
         self.assertTrue(len(request.data) >= count)
-        client.logout()
-
+        
     def test_list_noauth(self):
         count = 5
-        client = APIClient()
-        client.login(username='myuser', password='test')
-        self.post_heartbeats(client, self.data, count)
-        client.logout()
-        request = client.get(self.url)
+        self.post_multiple(self.user, self.data, count)
+        request = self.noauth_client.get(self.url)
         self.assertEqual(request.status_code, 401)
 
     def test_list_device_owner(self):
         count = 5
-        client = APIClient()
-        client.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
-        self.post_heartbeats(client, self.data, count)
-        request = client.get(self.url)
-        client.logout()
+        self.post_multiple(self.user, self.data, count)
+        request = self.user.get(self.url)
         self.assertEqual(request.status_code, 403)
 
 
+def create_crashreport(uuid="not set"):
+    return {
+        'uuid': uuid,
+        'is_fake_report': 0,
+        'app_version': 2,
+        'uptime': "2 Hours",
+        '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': str(datetime.datetime(year=2016, month=1, day=1))
+    }
+
+
 class CrashreportListTestCase(HeartbeatListTestCase):
 
     def setUp(self):
-        self.password = "test"
-        self.admin = User.objects.create_superuser(
-            'myuser', 'myemail@test.com', self.password)
+        self.setup_users()
+        self.data = self.create_dummy_data(self.uuid)
+        self.url = "/hiccup/api/v1/crashreports/"
+
+
+    def create_dummy_data(self, uuid="not set"):
+        return create_crashreport(uuid)
+
+
+class LogfileUploadTest(APITestCase):
+    def setUp(self):
+        self.setup_users()
         # we need a device
+        self.user.post("/hiccup/api/v1/crashreports/",
+                       create_crashreport(self.uuid))
+        self.user.post("/hiccup/api/v1/crashreports/",
+                       create_crashreport(self.uuid))
+        self.user.post("/hiccup/api/v1/crashreports/",
+                       create_crashreport(self.other_uuid))
+        self.user.post("/hiccup/api/v1/crashreports/",
+                       create_crashreport(self.other_uuid))
+
+    def setup_users(self):
+        self.password = "test"
         request = self.client.post("/hiccup/api/v1/devices/register/", {})
         self.uuid = request.data['uuid']
         self.token = request.data['token']
         request = self.client.post("/hiccup/api/v1/devices/register/", {})
         self.other_uuid = request.data['uuid']
         self.other_token = request.data['token']
-        self.data = self.create_dummy_data(self.uuid)
-        self.url = "/hiccup/api/v1/crashreports/"
+        self.admin = User.objects.create_superuser(
+            'myuser', 'myemail@test.com', self.password)
+        self.admin = APIClient()
+        self.admin.login(username='myuser', password='test')
+        self.user = APIClient()
+        self.other_user = APIClient()
+        self.user.credentials(HTTP_AUTHORIZATION='Token ' + self.token)
+        self.other_user.credentials(HTTP_AUTHORIZATION='Token '
+                                    + self.other_token)
 
-    def create_dummy_data(self, uuid="not set"):
-        return {
-            'uuid': uuid,
-            'is_fake_report': 0,
-            'app_version': 2,
-            'uptime': "2 Hours",
-            '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': str(datetime.datetime(year=2016, month=1, day=1))
-        }
+    def test_Logfile_upload_as_admin(self):
+        client = APIClient()
+        client.login(username='myuser', password='test')