diff --git a/ofu_app/apps/bug_report/__init__.py b/ofu_app/apps/bug_report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ofu_app/apps/bug_report/admin.py b/ofu_app/apps/bug_report/admin.py new file mode 100644 index 0000000..2b0c83f --- /dev/null +++ b/ofu_app/apps/bug_report/admin.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.contrib import admin +from apps.bug_report.models import BugMsg + +admin.site.register(BugMsg) diff --git a/ofu_app/apps/bug_report/api/__init__.py b/ofu_app/apps/bug_report/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ofu_app/apps/bug_report/api/v1_2/__init__.py b/ofu_app/apps/bug_report/api/v1_2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ofu_app/apps/bug_report/api/v1_2/serializers.py b/ofu_app/apps/bug_report/api/v1_2/serializers.py new file mode 100644 index 0000000..a5a8008 --- /dev/null +++ b/ofu_app/apps/bug_report/api/v1_2/serializers.py @@ -0,0 +1,73 @@ +from apps.bug_report.models import BugMsg +from rest_framework import serializers + + +class BugMsgCategoriesSerializer(serializers.Serializer): + """Your data serializer, define your fields here.""" + + def create(self, validated_data): + pass + + def update(self, instance, validated_data): + pass + + id = serializers.CharField() + short = serializers.CharField() + name = serializers.CharField() + + +class BugMsgStatesSerializer(serializers.Serializer): + """Your data serializer, define your fields here.""" + + def create(self, validated_data): + pass + + def update(self, instance, validated_data): + pass + + id = serializers.CharField() + name = serializers.CharField() + + +class BugMsgPrioritiesSerializer(serializers.Serializer): + """Your data serializer, define your fields here.""" + + def create(self, validated_data): + pass + + def update(self, instance, validated_data): + pass + + id = serializers.CharField() + name = serializers.CharField() + + +class BugMsgSerializer(serializers.HyperlinkedModelSerializer): + category = serializers.ChoiceField(choices=BugMsg.CATEGORY_CHOICES) + status = serializers.ChoiceField(choices=BugMsg.STATE_CHOICES) + + # def create(self, validated_data): + # title = validated_data.pop('title') + # description = validated_data.pop('description') + # category = validated_data.pop('category') + # if 'status' in validated_data: + # status = validated_data.pop('status') + # else: + # status = BugMsg.REGISTERED + # bugMsg, _ = BugMsg.objects.get_or_create(title=title, description=description, category=category, status=status) + # return bugMsg + + class Meta: + model = BugMsg + fields = ('id', 'title', 'status', 'description', 'category') + + +class BugMsgDetailSerializer(serializers.HyperlinkedModelSerializer): + category = serializers.ChoiceField(choices=BugMsg.CATEGORY_CHOICES) + priority = serializers.ChoiceField(choices=BugMsg.PRIORITY_CHOICES) + status = serializers.ChoiceField(choices=BugMsg.STATE_CHOICES) + registration_date = serializers.DateField(format='iso-8601') + + class Meta: + model = BugMsg + fields = ('id', 'registration_date', 'title', 'description', 'category', 'priority', 'status') diff --git a/ofu_app/apps/bug_report/api/v1_2/urls.py b/ofu_app/apps/bug_report/api/v1_2/urls.py new file mode 100644 index 0000000..3a45bc0 --- /dev/null +++ b/ofu_app/apps/bug_report/api/v1_2/urls.py @@ -0,0 +1,26 @@ +"""ofu_app URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.11/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.urls import path +from apps.bug_report.api.v1_2 import views as api_views + +urlpatterns = [ + # API Version 1.2 + path('bug-report/reports/', api_views.ApiBugMsgs.as_view(), name='bug-reports'), + path('bug-report/reports//', api_views.ApiBugMsgUpdate.as_view(), name='bug-report'), + path('bug-report/priorities/', api_views.ApiBugPriorities.as_view(), name='bug-priorities'), + path('bug-report/categories/', api_views.ApiBugCategories.as_view(), name='bug-categories'), + path('bug-report/states/', api_views.ApiBugStates.as_view(), name='bug-states'), +] diff --git a/ofu_app/apps/bug_report/api/v1_2/views.py b/ofu_app/apps/bug_report/api/v1_2/views.py new file mode 100644 index 0000000..e819579 --- /dev/null +++ b/ofu_app/apps/bug_report/api/v1_2/views.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from apps.bug_report.api.v1_2.serializers import BugMsgSerializer, BugMsgCategoriesSerializer, \ + BugMsgPrioritiesSerializer, BugMsgStatesSerializer, BugMsgDetailSerializer +from apps.bug_report.models import BugMsg +from rest_framework import generics, views, status +from rest_framework.decorators import permission_classes +from rest_framework.permissions import AllowAny +from rest_framework.response import Response + + +@permission_classes((AllowAny,)) +class ApiBugMsgs(generics.ListCreateAPIView): + serializer_class = BugMsgSerializer + queryset = BugMsg.objects.order_by('-registration_date') + + +@permission_classes((AllowAny,)) +class ApiBugMsgUpdate(generics.RetrieveUpdateAPIView): + serializer_class = BugMsgDetailSerializer + queryset = BugMsg.objects.all() + + +@permission_classes((AllowAny,)) +class ApiBugPriorities(views.APIView): + + def get(self, request): + data = BugMsg.API_Priorities + results = BugMsgPrioritiesSerializer(data, many=True).data + return Response(results, status=status.HTTP_200_OK) + + +@permission_classes((AllowAny,)) +class ApiBugCategories(views.APIView): + + def get(self, request): + data = BugMsg.API_CATEGORIES + results = BugMsgCategoriesSerializer(data, many=True).data + return Response(results, status=status.HTTP_200_OK) + + +@permission_classes((AllowAny,)) +class ApiBugStates(views.APIView): + + def get(self, request): + data = BugMsg.API_STATES + results = BugMsgStatesSerializer(data, many=True).data + return Response(results, status=status.HTTP_200_OK) diff --git a/ofu_app/apps/bug_report/apps.py b/ofu_app/apps/bug_report/apps.py new file mode 100644 index 0000000..9ba2000 --- /dev/null +++ b/ofu_app/apps/bug_report/apps.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class FoodConfig(AppConfig): + name = 'apps.food' diff --git a/ofu_app/apps/bug_report/models.py b/ofu_app/apps/bug_report/models.py new file mode 100644 index 0000000..6db7b03 --- /dev/null +++ b/ofu_app/apps/bug_report/models.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import os +from io import BytesIO +import uuid +from _datetime import datetime + +from PIL import Image +from django.conf import settings +from django.contrib.auth.models import User +from django.core.files.uploadedfile import SimpleUploadedFile +from django.db import models +from django.utils import timezone +from django.core.validators import MaxValueValidator, MinValueValidator + +MAX_LENGTH = 256 +MAX_TITLE_LENGTH = 128 +MAX_DESCRIPTION_LENGTH = 1024 +MAX_PRIORITY_LENGTH = 32 +MAX_CATEGORY_LENGTH = 32 +MAX_STATE_LENGTH = 64 + + +# Create your models here. +class BugMsg(models.Model): + # Priorities + HIGH = 'HIGH' + LOW = 'LOW' + + # Categories + FOOD = 'FOOD' + EVENTS = 'Events' + DONAR = 'DONAR' + OTHER = 'Other' + + # State + REGISTERED = 'REGISTERED' + TODO = 'TODO' + IN_PROGRESS = 'IN_PROGRESS' + DONE = 'DONE' + REJECTED = 'REJECTED' + + PRIORITY_CHOICES = ( + (HIGH, 'High'), (LOW, 'Low') + ) + + CATEGORY_CHOICES = ( + (FOOD, 'Food App'), (EVENTS, 'Event App'), (DONAR, 'Donar App'), (OTHER, 'Other') + ) + + STATE_CHOICES = ( + (REGISTERED, 'registered'), (TODO, 'todo'), (IN_PROGRESS, 'in progress'), (DONE, 'done'), (REJECTED, 'rejected') + ) + + # Api priorities data + API_Priorities = [{'id': HIGH, 'name': 'High'}, + {'id': LOW, 'name': 'Low'}, ] + + # Api categories data + API_CATEGORIES = [{'id': FOOD, 'name': 'Food App', 'short': 'Food'}, + {'id': EVENTS, 'name': 'Event App', 'short': 'Events'}, + {'id': DONAR, 'name': 'Donar App', 'short': 'Donar'}, + {'id': OTHER, 'name': 'Other', 'short': 'Other'}, ] + + # Api state data + API_STATES = [{'id': REGISTERED, 'name': 'Registered'}, + {'id': TODO, 'name': 'todo'}, + {'id': IN_PROGRESS, 'name': 'in progress'}, + {'id': DONE, 'name': 'done'}, + {'id': REJECTED, 'name': 'rejected'}, + ] + + id = models.AutoField(primary_key=True) + title = models.CharField(max_length=MAX_TITLE_LENGTH, unique=True) + description = models.CharField(max_length=MAX_DESCRIPTION_LENGTH) + priority = models.CharField(max_length=MAX_PRIORITY_LENGTH, choices=PRIORITY_CHOICES) + category = models.CharField(max_length=MAX_CATEGORY_LENGTH, choices=CATEGORY_CHOICES) + status = models.CharField(max_length=MAX_STATE_LENGTH, choices=STATE_CHOICES) + registration_date = models.DateField(default=timezone.now) + + def __str__(self): + return "%s - %s - %s - %s" % (self.registration_date.strftime("%d.%m.%Y"), self.title, self.priority, self.status) diff --git a/ofu_app/apps/bug_report/tests/__init__.py b/ofu_app/apps/bug_report/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ofu_app/apps/bug_report/tests/tests_api.py b/ofu_app/apps/bug_report/tests/tests_api.py new file mode 100644 index 0000000..10a562b --- /dev/null +++ b/ofu_app/apps/bug_report/tests/tests_api.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.test import TestCase +from django.urls import reverse +from apps.food.models import SingleFood, Menu +from apps.food.api.v1_1.serializers import MenuSerializer +from rest_framework import status + +from datetime import datetime + + +# Create your tests here. + +class SingleFood_Scope(TestCase): + def setUp(self): + self.singlefood_1 = SingleFood.objects.create(name="testfood1") + self.singlefood_2 = SingleFood.objects.create(name="testfood2") + self.singlefood_3 = SingleFood.objects.create(name="testfood3") + self.singlefood_4 = SingleFood.objects.create(name="testfood4") + self.singlefood_5 = SingleFood.objects.create(name="testfood5") + self.singlefood_6 = SingleFood.objects.create(name="testfood6") + self.today_menu = Menu.objects.create(date='2017-01-15', location=Menu.ERBA) + self.today_menu.menu.add(self.singlefood_1) + self.today_menu.menu.add(self.singlefood_2) + self.today_menu.menu.add(self.singlefood_3) + self.menu_2 = Menu.objects.create(date='2017-01-10', location=Menu.FEKI) + self.menu_2.menu.add(self.singlefood_4) + self.menu_2.menu.add(self.singlefood_5) + self.menu_2.menu.add(self.singlefood_6) + + def test_get_food_root(self): + """ + All menus in response + """ + menus = Menu.objects.all() + serializer = MenuSerializer(menus, many=True) + response = self.client.get(reverse('api-v1_1-food-all')) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data, serializer.data) + + def test_get_food_date_param(self): + """ + Menu for a given date is in the response + """ + serializer = MenuSerializer(self.menu_2, many=False) + response_1 = self.client.get(reverse('api-v1_1-food-date', kwargs={'year': '2017', 'month': '01', 'day': '10'})) + self.assertEqual(response_1.status_code, 200) + self.assertEqual(response_1.data, [serializer.data]) + + serializer_2 = MenuSerializer(self.today_menu, many=False) + response_2 = self.client.get(reverse('api-v1_1-food-date', kwargs={'year': '2017', 'month': '01', 'day': '15'})) + self.assertEqual(response_2.status_code, 200) + self.assertEqual(response_2.data, [serializer_2.data]) + + def test_get_food_date_param_not_in_set(self): + """ + Empty response if no object with request date in set + """ + response_1 = self.client.get(reverse('api-v1_1-food-date', kwargs={'year': '2017', 'month': '01', 'day': '11'})) + self.assertEqual(response_1.status_code, 200) + self.assertEqual(response_1.data, []) + + def test_get_food_location_param(self): + """ + Menu for a given location is in the response + """ + serializer_1 = MenuSerializer(self.today_menu, many=False) + response_1 = self.client.get(reverse('api-v1_1-food-location', kwargs={'location': Menu.ERBA})) + self.assertEqual(response_1.status_code, 200) + self.assertEqual(response_1.data, [serializer_1.data]) + + serializer_2 = MenuSerializer(self.menu_2, many=False) + response_2 = self.client.get(reverse('api-v1_1-food-location', kwargs={'location': Menu.FEKI})) + self.assertEqual(response_2.status_code, 200) + self.assertEqual(response_2.data, [serializer_2.data]) + + def test_get_food_location_param_not_in_set(self): + """ + Empty response if no object with request location in set + """ + response_1 = self.client.get(reverse('api-v1_1-food-location', kwargs={'location': Menu.AUSTRASSE})) + self.assertEqual(response_1.status_code, 200) + self.assertEqual(response_1.data, []) + + def test_get_food_location_date_param(self): + """ + Menu for request location and date is in the response + """ + + serializer = MenuSerializer(self.menu_2, many=False) + response = self.client.get(reverse('api-v1_1-food-location-date', + kwargs={'year': '2017', 'month': '01', 'day': '10', + 'location': Menu.FEKI})) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, [serializer.data]) + + def test_get_food_location_date_param_not_in_set(self): + """ + Empty response if no object with request date and location in set + """ + response_1 = self.client.get(reverse('api-v1_1-food-location-date', + kwargs={'year': '2017', 'month': '01', 'day': '11', + 'location': Menu.FEKI})) + self.assertEqual(response_1.status_code, 200) + self.assertEqual(response_1.data, []) + + response_1 = self.client.get(reverse('api-v1_1-food-location-date', + kwargs={'year': '2017', 'month': '01', 'day': '10', + 'location': Menu.AUSTRASSE})) + self.assertEqual(response_1.status_code, 200) + self.assertEqual(response_1.data, []) + + def test_get_food_today_param(self): + """ + Menu for request today is in the response + """ + self.singlefood = SingleFood.objects.create(name="testfood") + self.today_menu = Menu.objects.create(date=datetime.today().strftime('%Y-%m-%d'), location=Menu.ERBA) + self.today_menu.menu.add(self.singlefood) + + serializer = MenuSerializer(self.today_menu, many=False) + response = self.client.get(reverse('api-v1_1-food-today')) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, [serializer.data]) + + def test_get_food_today_param_not_in_set(self): + """ + Empty response if no object with date 'today' is in set + """ + response = self.client.get(reverse('api-v1_1-food-today')) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data, []) diff --git a/ofu_app/apps/bug_report/tests/tests_model.py b/ofu_app/apps/bug_report/tests/tests_model.py new file mode 100644 index 0000000..7c2aa3b --- /dev/null +++ b/ofu_app/apps/bug_report/tests/tests_model.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.test import TestCase + +from apps.food.models import SingleFood + + +# Create your tests here. + +class SingleFood_Scope(TestCase): + def setUp(self): + SingleFood.objects.create(name="testfood") + + def test_food_created_just_with_name(self): + """Animals that can speak are correctly identified""" + food = SingleFood.objects.get(name="testfood") + self.assertEqual(food.name, 'testfood') + self.assertIsNone(food.allergens.all().first(), []) + self.assertIsNone(food.price_employee) + self.assertIsNone(food.price_guest) + self.assertIsNone(food.price_student) + self.assertIsNone(food.image) + self.assertEqual(food.rating, 0.0) diff --git a/ofu_app/apps/food/api/v1_2/serializers/user_serializers.py b/ofu_app/apps/food/api/v1_2/serializers/user_serializers.py index 032cf7f..aa49661 100644 --- a/ofu_app/apps/food/api/v1_2/serializers/user_serializers.py +++ b/ofu_app/apps/food/api/v1_2/serializers/user_serializers.py @@ -4,6 +4,9 @@ from django.contrib.auth.models import User from rest_framework import validators from rest_framework import serializers from django.db.utils import IntegrityError +import logging + +logger = logging.getLogger(__name__) class UserFoodImageSerializer(serializers.HyperlinkedModelSerializer): @@ -90,6 +93,8 @@ class UserFoodImageSerializer(serializers.ModelSerializer): image = validated_data.pop('image') food_image = FoodImage.objects.create(image=image) food_image.save() + logger.info('New Image: {}\nFood: {}'.format(food_image.image.url, food.name)) + logger.error('New Image: {}\nFood: {}'.format(food_image.image.url, food.name)) try: user_food_image = UserFoodImage.objects.create(user=user, food=food, image=food_image) user_food_image.save() diff --git a/ofu_app/apps/food/api/v1_2/views/main_views.py b/ofu_app/apps/food/api/v1_2/views/main_views.py index c5cf761..333f469 100644 --- a/ofu_app/apps/food/api/v1_2/views/main_views.py +++ b/ofu_app/apps/food/api/v1_2/views/main_views.py @@ -56,7 +56,7 @@ class ApiMenus(generics.ListAPIView): except ValueError as e: # TODO: return Exception return [] - + queryset.order_by('date') return queryset diff --git a/ofu_app/core/settings.py b/ofu_app/core/settings.py index 62acafb..d529acc 100755 --- a/ofu_app/core/settings.py +++ b/ofu_app/core/settings.py @@ -101,6 +101,7 @@ INSTALLED_APPS = [ 'apps.events', 'apps.donar', 'apps.registration', + 'apps.bug_report', 'rest_framework', 'rest_framework.authtoken', 'djoser', @@ -235,6 +236,10 @@ LOGGING = { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler', }, + 'mail_admins_image_upload': { + 'level': 'INFO', + 'class': 'django.utils.log.AdminEmailHandler', + }, }, 'loggers': { 'apps.food.utils': { @@ -245,5 +250,9 @@ LOGGING = { 'handlers': ['console', 'file', 'mail_admins'], 'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'), }, + 'apps.food.api.v1_2.serializers.user_serializers': { + 'handlers': ['mail_admins_image_upload', 'console'], + 'level': 'INFO', + }, }, } diff --git a/ofu_app/core/urls.py b/ofu_app/core/urls.py index c48cc98..fafb986 100755 --- a/ofu_app/core/urls.py +++ b/ofu_app/core/urls.py @@ -57,6 +57,7 @@ urlpatterns = [ # -- Version 1.2 url(r'^api/v1.2/', include('apps.food.api.v1_2.urls')), url(r'^api/v1.2/', include('apps.registration.api.urls')), + url(r'^api/v1.2/', include('apps.bug_report.api.v1_2.urls')), # -- Third Party APIs url(r'^api/auth/', include('rest_framework.urls', namespace='rest_framework')),