From 69af967ae675993a2a772998172c267c301b1c10 Mon Sep 17 00:00:00 2001 From: Oliver Marks Date: Sun, 9 Apr 2017 09:57:23 +0100 Subject: [PATCH] Initial rfid models, to think about any thoughts ? remove mapping model on base on @scollins suggestion Updated models, enabled it in the admin backup to get an idea of the structure. will need to rebuild, updating restframework current state of experiment into rfid api Change docs url started looking into web tokens Refactor models More experimenting Custom view now works, needs tiding up and data to test --- README.org | 3 +- config/settings/common.py | 8 ++- config/urls.py | 12 ++++ mhackspace/rfid/__init__.py | 0 mhackspace/rfid/admin.py | 14 +++++ mhackspace/rfid/migrations/0001_initial.py | 47 +++++++++++++++ .../migrations/0002_auto_20170420_0730.py | 39 ++++++++++++ mhackspace/rfid/migrations/__init__.py | 0 mhackspace/rfid/models.py | 60 +++++++++++++++++++ mhackspace/rfid/serializers.py | 32 ++++++++++ mhackspace/rfid/tests/__init__.py | 0 mhackspace/rfid/tests/tests.py | 58 ++++++++++++++++++ mhackspace/rfid/views.py | 32 ++++++++++ mhackspace/static/sass/project.scss | 6 ++ requirements/base.txt | 3 + 15 files changed, 310 insertions(+), 4 deletions(-) create mode 100644 mhackspace/rfid/__init__.py create mode 100644 mhackspace/rfid/admin.py create mode 100644 mhackspace/rfid/migrations/0001_initial.py create mode 100644 mhackspace/rfid/migrations/0002_auto_20170420_0730.py create mode 100644 mhackspace/rfid/migrations/__init__.py create mode 100644 mhackspace/rfid/models.py create mode 100644 mhackspace/rfid/serializers.py create mode 100644 mhackspace/rfid/tests/__init__.py create mode 100644 mhackspace/rfid/tests/tests.py create mode 100644 mhackspace/rfid/views.py diff --git a/README.org b/README.org index 4531215..a7a55f0 100644 --- a/README.org +++ b/README.org @@ -5,7 +5,6 @@ Repository for the maidstone hackspace website, feel free to fork this site for your own Hackspace. - ** Requirements Before getting started make sure you have git, docker and docker-compose installed on your machine. The simplest way to setup this site is to use docker-compose so please install that from this site @@ -47,7 +46,7 @@ docker-compose -f dev.yml run --rm django python manage.py makemigrations docker-compose -f dev.yml run --rm django python manage.py migrate #+END_SRC *** Create the admin user. -Once created you can login at http://127.0.0.1:8180/admin +Once created you can login at http://127.0.0.1:8180/trustee #+BEGIN_SRC sh docker-compose -f dev.yml run --rm django python manage.py createsuperuser #+END_SRC diff --git a/config/settings/common.py b/config/settings/common.py index 1ac9c19..f954c7a 100644 --- a/config/settings/common.py +++ b/config/settings/common.py @@ -80,6 +80,7 @@ THIRD_PARTY_APPS = ( 'whitenoise.runserver_nostatic', 'stdimage', 'rest_framework', + 'django_filters', 'draceditor', 'haystack', 'djconfig', @@ -138,6 +139,7 @@ LOCAL_APPS = ( 'mhackspace.blog', 'mhackspace.core', 'mhackspace.requests', + 'mhackspace.rfid', ) # See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps @@ -446,10 +448,12 @@ REST_FRAMEWORK = { 'rest_framework.filters.OrderingFilter' ), 'DEFAULT_PERMISSION_CLASSES': ( - 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', + # 'rest_framework.permissions.IsAuthenticated', + # 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly', ), 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework.authentication.BasicAuthentication', + # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', + # 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ), diff --git a/config/urls.py b/config/urls.py index 4595b9d..e078d3e 100644 --- a/config/urls.py +++ b/config/urls.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals + from django.conf import settings from django.conf.urls import include, url from django.conf.urls.static import static @@ -9,6 +10,7 @@ from django.views import defaults as default_views from django.contrib.auth import views as auth_views from django.contrib.sitemaps.views import sitemap from rest_framework.routers import DefaultRouter +from rest_framework.documentation import include_docs_urls from mhackspace.contact.views import contact from mhackspace.members.views import MemberListView @@ -20,21 +22,28 @@ from mhackspace.blog.views import PostViewSet, CategoryViewSet, BlogPost, PostLi from mhackspace.blog.sitemaps import PostSitemap, CategorySitemap from mhackspace.feeds.views import FeedViewSet, ArticleViewSet from mhackspace.requests.views import RequestsForm, RequestsList +from mhackspace.rfid.views import DeviceViewSet, AuthUserWithDeviceViewSet + from wiki.urls import get_pattern as get_wiki_pattern from django_nyt.urls import get_pattern as get_nyt_pattern +from rest_framework_jwt.views import obtain_jwt_token router = DefaultRouter() router.register(r'posts', PostViewSet) router.register(r'categories', CategoryViewSet) router.register(r'feeds', FeedViewSet) router.register(r'articles', ArticleViewSet) +router.register(r'rfid', DeviceViewSet) +router.register(r'rfidAuth', AuthUserWithDeviceViewSet, base_name='device') + sitemaps = { 'posts': PostSitemap, 'category': CategorySitemap, } + urlpatterns = [ url(r'^$', TemplateView.as_view(template_name='pages/home.html'), name='home'), url(r'^about/$', TemplateView.as_view(template_name='pages/about.html'), name='about'), @@ -68,6 +77,9 @@ urlpatterns = [ # Django Admin, use {% url 'admin:index' %} url(r'{}'.format(settings.ADMIN_URL), admin.site.urls), + url(r'^api-token-auth/', obtain_jwt_token), + url(r'^api/docs/', include_docs_urls(title='Hackspace API')), + # User management url(r'^users/', include('mhackspace.users.urls', namespace='users')), url(r'^accounts/', include('allauth.urls')), diff --git a/mhackspace/rfid/__init__.py b/mhackspace/rfid/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mhackspace/rfid/admin.py b/mhackspace/rfid/admin.py new file mode 100644 index 0000000..0a8621d --- /dev/null +++ b/mhackspace/rfid/admin.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +from django.contrib import admin +from django.contrib.admin import ModelAdmin + +from mhackspace.rfid.models import Device, Rfid + + +@admin.register(Device) +class DeviceAdmin(ModelAdmin): + list_display = ('name',) + +@admin.register(Rfid) +class RfidAdmin(ModelAdmin): + list_display = ('code',) diff --git a/mhackspace/rfid/migrations/0001_initial.py b/mhackspace/rfid/migrations/0001_initial.py new file mode 100644 index 0000000..fb37b39 --- /dev/null +++ b/mhackspace/rfid/migrations/0001_initial.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-04-14 21:15 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Device', + fields=[ + ('identifier', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255, verbose_name='Device name')), + ('description', models.CharField(blank=True, max_length=255, verbose_name='Short description of what the device does')), + ('added_date', models.DateTimeField(default=django.utils.timezone.now, editable=False)), + ], + ), + migrations.CreateModel( + name='DeviceAuth', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('device', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='device', to='rfid.Device')), + ('user', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_auth', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Rfid', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.PositiveIntegerField()), + ('description', models.CharField(blank=True, max_length=255, verbose_name='Short rfid description')), + ('user', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='rfid_user', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/mhackspace/rfid/migrations/0002_auto_20170420_0730.py b/mhackspace/rfid/migrations/0002_auto_20170420_0730.py new file mode 100644 index 0000000..da8ec64 --- /dev/null +++ b/mhackspace/rfid/migrations/0002_auto_20170420_0730.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-04-20 07:30 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('rfid', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='deviceauth', + name='device', + ), + migrations.RemoveField( + model_name='deviceauth', + name='user', + ), + migrations.AddField( + model_name='device', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='rfid', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.DeleteModel( + name='DeviceAuth', + ), + ] diff --git a/mhackspace/rfid/migrations/__init__.py b/mhackspace/rfid/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mhackspace/rfid/models.py b/mhackspace/rfid/models.py new file mode 100644 index 0000000..3b727ed --- /dev/null +++ b/mhackspace/rfid/models.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals, absolute_import +import uuid + +from django.utils import timezone +from django.conf import settings +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +# just brainstorming so we can start playing with this, +# be nice to make this a 3rd party django installable app ? + + +# users rfid card to user mapping, user can have more than one card +class Rfid(models.Model): + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=True, blank=True, + # related_name='rfid_user' + ) + code = models.PositiveIntegerField() + description = models.CharField(_('Short rfid description'), blank=True, max_length=255) + + def __str__(self): + return self.description + + +# description of a device like door, print, laser cutter +class Device(models.Model): + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=True, blank=True, + # related_name='rfid_user' + ) + # user = models.ManyToMany(settings.AUTH_USER_MODEL) + identifier = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + + name = models.CharField(_('Device name'), max_length=255) + description = models.CharField(_('Short description of what the device does'), blank=True, max_length=255) + added_date = models.DateTimeField(default=timezone.now, editable=False) + + def __str__(self): + return self.name + + +# many to many, lookup user from rfid model then get there user_id and the device to check if auth allowed +# class DeviceAuth(models.Model): +# user = models.ForeignKey( +# settings.AUTH_USER_MODEL, +# null=True, blank=True, +# default=None, +# related_name='user_auth' +# ) + +# device = models.ForeignKey( +# Device, +# null=True, blank=True, +# default=None, +# related_name='device' +# ) diff --git a/mhackspace/rfid/serializers.py b/mhackspace/rfid/serializers.py new file mode 100644 index 0000000..f5db4ff --- /dev/null +++ b/mhackspace/rfid/serializers.py @@ -0,0 +1,32 @@ +from rest_framework import serializers + +from mhackspace.rfid.models import Device + + +class Task(object): + def __init__(self, **kwargs): + for field in ('id', 'name', 'owner', 'status'): + setattr(self, field, kwargs.get(field, None)) + + +class DeviceSerializer(serializers.ModelSerializer): + class Meta: + model = Device + fields = ('name', ) + + +class AuthSerializer(serializers.Serializer): + name = serializers.CharField(max_length=255) + rfid = serializers.CharField(max_length=255) + device_id = serializers.CharField(max_length=255) + + # def create(self, validated_data): + # return Task(id=None, **validated_data) + + # def update(self, instance, validated_data): + # for field, value in validated_data.items(): + # setattr(instance, field, value) + # return instance + + # class Meta: + # fields = ('name', ) diff --git a/mhackspace/rfid/tests/__init__.py b/mhackspace/rfid/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mhackspace/rfid/tests/tests.py b/mhackspace/rfid/tests/tests.py new file mode 100644 index 0000000..d9537ef --- /dev/null +++ b/mhackspace/rfid/tests/tests.py @@ -0,0 +1,58 @@ +import sys +import requests + +from io import StringIO +from django.core.management import call_command +from test_plus.test import TestCase +from rest_framework.test import APIRequestFactory +from rest_framework.test import RequestsClient + +from mhackspace.rfid.models import Device +from mhackspace.user.models import User + + +# http://www.django-rest-framework.org/api-guide/testing/ + +class MigrationTestCase(TestCase): + + def testRollback(self): + out = StringIO() + sys.stout = out + call_command('migrate', 'rfid', 'zero', stdout=out) + call_command('migrate', 'rfid', stdout=out) + self.assertIn("... OK\n", out.getvalue()) + +class ApiTests(TestCase): + def setUp(self): + Device(name='device01').save() + User(name='User01').save() + Rfid(code=1, user=1).save() + + def testAuth(self): + factory = APIRequestFactory() + request = factory.get('/rfid/') + + def testSamsMadness(self): + client = RequestsClient() + response = client.post( + 'http://127.0.0.1:8180/api/v1/rfidAuth/?format=json', + data={'rfid':'1', 'device': '1'}) + print(response) + assert response.status_code == 200 + self.assertEquals(response.json().get('results'), [{'name': 'device01'}]) + + + def testAuthUserWithDevice(self): + client = RequestsClient() + response = client.get('http://127.0.0.1:8180/api/v1/rfid/?format=json') + assert response.status_code == 200 + self.assertEquals(response.json().get('results'), [{'name': 'device01'}]) + + + def testFetchDeviceList(self): + client = RequestsClient() + response = client.get('http://127.0.0.1:8180/api/v1/rfid/?format=json') + assert response.status_code == 200 + self.assertEquals(response.json().get('results'), [{'name': 'device01'}]) + + diff --git a/mhackspace/rfid/views.py b/mhackspace/rfid/views.py new file mode 100644 index 0000000..e1648af --- /dev/null +++ b/mhackspace/rfid/views.py @@ -0,0 +1,32 @@ +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework import viewsets +from mhackspace.rfid.models import Device, Rfid +from mhackspace.rfid.serializers import DeviceSerializer, AuthSerializer +from django.shortcuts import get_list_or_404, get_object_or_404 + + +class DeviceViewSet(viewsets.ModelViewSet): + queryset = Device.objects.all() + serializer_class = DeviceSerializer + + +#https://medium.com/django-rest-framework/django-rest-framework-viewset-when-you-don-t-have-a-model-335a0490ba6f +class AuthUserWithDeviceViewSet(viewsets.ViewSet): + # http_method_names = ['get', 'post', 'head'] + serializer_class = AuthSerializer + + def list(self, request): + serializer = AuthSerializer(instance={'name': '1','rfid': '1', 'device_id': '1'}) + return Response(serializer.data) + + def post(self, request, format=None): + rfid = Rfid.objects.get(code=request.GET.get('rfid_id')) + + print(rfid.user.device__set(device=request.GET.get('rfid_id'))) + # = get_object_or_404(Disease, pk=disease_id) + + + # Device(rfid, device) + serializer = AuthSerializer(instance={'name': '1', 'rfid': '1', 'device_id': '1'}) + return Response(serializer.data) diff --git a/mhackspace/static/sass/project.scss b/mhackspace/static/sass/project.scss index 6aceb3b..6e7afd3 100644 --- a/mhackspace/static/sass/project.scss +++ b/mhackspace/static/sass/project.scss @@ -21,3 +21,9 @@ [hidden][style="display: block;"] { display: block !important; } + +.imgfit { + width:100%; + height: auto; + overflow: hidden; +} \ No newline at end of file diff --git a/requirements/base.txt b/requirements/base.txt index 22a23a7..353bb77 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -61,7 +61,10 @@ git+https://github.com/olymk2/scaffold.git git+git://github.com/django-wiki/django-wiki.git djangorestframework==3.6.3 +djangorestframework-jwt django-filter==1.0.2 +coreapi +# api libraries end draceditor==1.1.8 # django-spirit