diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..351a8fc4f8ce629c8efb58d0780121f3ee4a14d6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: python +python: + - "3.4" + - "3.5" + - "3.5-dev" # 3.5 development branch + - "3.6" + - "3.6-dev" # 3.6 development branch + - "3.7-dev" # 3.7 development branch + - "nightly" +services: + - postgresql +# command to install dependencies +install: + - pip install -r requirements/development.txt + +before_script: + - psql -c 'create database travis_ci_test;' -U postgres +env: + - DATABASE_URL="postgresql://postgres:postgres@postgres:5432/travis_ci_test" +# command to run tests +script: + - flake8 --max-line-length=125 --exclude=src/kszkepzes,migrations + - python src/manage.py migrate + - python src/manage.py test diff --git a/README.md b/README.md index f8cd16343a15f139b07ace346c28286271244997..38d506ef0bdc041ae74ccf9d5f01de0ca5fac7d7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # KSZKĂŠpzĂŠs Honlap -[](https://git.sch.bme.hu/kszk/devteam/kszkepzes/commits/master) -[](https://git.sch.bme.hu/kszk/devteam/kszkepzes/commits/master) +[](https://travis-ci.org/DevTeamSCH/kszkepzes) ## KĂśvetelmĂŠnyek diff --git a/src/account/admin.py b/src/account/admin.py index ced8aebf3fec2c3ed4dd60e9c4369c86d1770c59..b9e5817dfac078d71887a8d90fdda504257bb88a 100644 --- a/src/account/admin.py +++ b/src/account/admin.py @@ -1,13 +1,16 @@ from django.contrib import admin -from .models import Profile +from . import models -@admin.register(Profile) +@admin.register(models.Profile) class ProfileAdmin(admin.ModelAdmin): - list_display = ('user_username', 'join_date', 'pref_group') + list_display = ('user_username', 'join_date') def user_username(self, obj): return obj.user.username user_username.admin_order_field = 'user__username' + + # Register your models here. +admin.site.register(models.GroupChoice) diff --git a/src/account/migrations/0003_profile_nick.py b/src/account/migrations/0003_profile_nick.py new file mode 100644 index 0000000000000000000000000000000000000000..7a3542787808c4e28493012327718f79731e9f3c --- /dev/null +++ b/src/account/migrations/0003_profile_nick.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-12-23 11:06 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0002_auto_20171114_2144'), + ] + + operations = [ + migrations.AddField( + model_name='profile', + name='nick', + field=models.CharField(blank=True, max_length=15, null=True), + ), + ] diff --git a/src/account/migrations/0004_profile_signed.py b/src/account/migrations/0004_profile_signed.py new file mode 100644 index 0000000000000000000000000000000000000000..424407361c1f04b2068daab365199eb7d5aa9e2b --- /dev/null +++ b/src/account/migrations/0004_profile_signed.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-12-23 11:10 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0003_profile_nick'), + ] + + operations = [ + migrations.AddField( + model_name='profile', + name='signed', + field=models.BooleanField(default=False), + ), + ] diff --git a/src/account/migrations/0005_auto_20171223_1455.py b/src/account/migrations/0005_auto_20171223_1455.py new file mode 100644 index 0000000000000000000000000000000000000000..d5e5fb83b2f2deeaf85dba89cb35de502a01ea4e --- /dev/null +++ b/src/account/migrations/0005_auto_20171223_1455.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-12-23 13:55 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0004_profile_signed'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='join_date', + field=models.DateField(auto_now=True), + ), + ] diff --git a/src/account/migrations/0006_auto_20180123_1713.py b/src/account/migrations/0006_auto_20180123_1713.py new file mode 100644 index 0000000000000000000000000000000000000000..035ce31da185ae28746fa3674f1670e1491e671f --- /dev/null +++ b/src/account/migrations/0006_auto_20180123_1713.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2018-01-23 16:13 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0005_auto_20171223_1455'), + ] + + operations = [ + migrations.CreateModel( + name='GroupChoice', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('pref_group', models.CharField(choices=[('DT', 'DevTeam'), ('NET', 'NeTeam'), ('ST', 'SecurITeam'), ('SYS', 'SysAdmin'), ('HAT', 'HallgatĂłi TudĂĄsbĂĄzis'), ('N', 'None')], default='None', max_length=10)), + ], + ), + migrations.RemoveField( + model_name='profile', + name='pref_group', + ), + migrations.AddField( + model_name='groupchoice', + name='profile', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='preferd_groups', to='account.Profile'), + ), + migrations.AlterUniqueTogether( + name='groupchoice', + unique_together=set([('pref_group', 'profile')]), + ), + ] diff --git a/src/account/migrations/0007_auto_20180123_1723.py b/src/account/migrations/0007_auto_20180123_1723.py new file mode 100644 index 0000000000000000000000000000000000000000..27a4efe8c61f7cb19bb1f349a8c21b6e66f1e60e --- /dev/null +++ b/src/account/migrations/0007_auto_20180123_1723.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2018-01-23 16:23 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0006_auto_20180123_1713'), + ] + + operations = [ + migrations.RenameField( + model_name='groupchoice', + old_name='pref_group', + new_name='choice', + ), + migrations.AlterUniqueTogether( + name='groupchoice', + unique_together=set([('choice', 'profile')]), + ), + ] diff --git a/src/account/migrations/0008_auto_20180124_1821.py b/src/account/migrations/0008_auto_20180124_1821.py new file mode 100644 index 0000000000000000000000000000000000000000..fd3af443879f22344cc51f1334eec41e445948ad --- /dev/null +++ b/src/account/migrations/0008_auto_20180124_1821.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2018-01-24 17:21 +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 = [ + ('account', '0007_auto_20180123_1723'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='user', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/src/account/migrations/0009_profile_motivation.py b/src/account/migrations/0009_profile_motivation.py new file mode 100644 index 0000000000000000000000000000000000000000..c9d1e0a2e2aa16abd91adb4970ac860e5e991ffc --- /dev/null +++ b/src/account/migrations/0009_profile_motivation.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2018-01-24 17:43 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0008_auto_20180124_1821'), + ] + + operations = [ + migrations.AddField( + model_name='profile', + name='motivation', + field=models.TextField(null=True), + ), + ] diff --git a/src/account/migrations/0010_auto_20180124_1929.py b/src/account/migrations/0010_auto_20180124_1929.py new file mode 100644 index 0000000000000000000000000000000000000000..2a9b50d5d2e04d41c68b1aba0135774b1bd97bc3 --- /dev/null +++ b/src/account/migrations/0010_auto_20180124_1929.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2018-01-24 18:29 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0009_profile_motivation'), + ] + + operations = [ + migrations.AddField( + model_name='profile', + name='groups', + field=models.ManyToManyField(related_name='profiles', to='account.GroupChoice'), + ), + migrations.AlterField( + model_name='groupchoice', + name='choice', + field=models.CharField(choices=[('DT', 'DevTeam'), ('NET', 'NeTeam'), ('ST', 'SecurITeam'), ('SYS', 'SysAdmin'), ('HAT', 'HallgatĂłi TudĂĄsbĂĄzis'), ('N', 'None')], default='N', max_length=10, unique=True), + ), + migrations.AlterUniqueTogether( + name='groupchoice', + unique_together=set([]), + ), + migrations.RemoveField( + model_name='groupchoice', + name='profile', + ), + ] diff --git a/src/account/models.py b/src/account/models.py index 6b0bb79c09d62f3d045e97ef37b976931cc2cb2d..9c9f6825eabda79f1ca0363ced2c56c691959299 100644 --- a/src/account/models.py +++ b/src/account/models.py @@ -1,12 +1,8 @@ from django.db import models from django.contrib.auth.models import User -from django.core.exceptions import ValidationError -import datetime -class Profile(models.Model): - join_date = models.DateField() - user = models.OneToOneField(User, on_delete=models.CASCADE) +class GroupChoice(models.Model): TEAMS = ( ('DT', 'DevTeam'), ('NET', 'NeTeam'), @@ -15,12 +11,20 @@ class Profile(models.Model): ('HAT', 'HallgatĂłi TudĂĄsbĂĄzis'), ('N', 'None'), ) - pref_group = models.CharField(max_length=10, choices=TEAMS, default='None') + choice = models.CharField(max_length=10, choices=TEAMS, default='N', unique=True) + + def __str__(self): + return self.choice + + +class Profile(models.Model): + join_date = models.DateField(auto_now=True) + user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE) + motivation = models.TextField(null=True) + nick = models.CharField(max_length=15, blank=True, null=True) + signed = models.BooleanField(default=False, null=False) + groups = models.ManyToManyField(GroupChoice, related_name='profiles') # Homeworks=models.ForeignKey(Homework) def __str__(self): return self.user.username - - def clean(self): - if self.join_date > datetime.date.today() or self.join_date < datetime.date(2015, 1, 1): - raise ValidationError('Invalid date') diff --git a/src/account/serializers.py b/src/account/serializers.py index 62021e9bfca6fed293f5b93d010c95a36150ad7a..92a0828d55c5b8cd3a575ba0d16cb9f4711f87c8 100644 --- a/src/account/serializers.py +++ b/src/account/serializers.py @@ -1,8 +1,16 @@ from rest_framework import serializers -from . import models +from account import models + + +class ChoiceSerializer(serializers.ModelSerializer): + class Meta: + model = models.GroupChoice + fields = ('choice', 'profile') class ProfileSerializer(serializers.ModelSerializer): + groups = serializers.SlugRelatedField(many=True, slug_field="choice", queryset=models.GroupChoice.objects.all()) + class Meta: model = models.Profile - fields = '__all__' + fields = ('id', 'join_date', 'user', 'nick', 'motivation', 'signed', 'groups') diff --git a/src/account/urls.py b/src/account/urls.py index 96decfda8d97835f067de97638c465ff3481d11b..ebf85f3d5bdf9885beb1ad7f28d1bce0e6624575 100644 --- a/src/account/urls.py +++ b/src/account/urls.py @@ -1,8 +1,7 @@ from rest_framework import routers from . import views -router = routers.DefaultRouter(trailing_slash=False) - -router.register(r'profiles', views.ProfileViewSet) +router = routers.DefaultRouter() +router.register(r'profiles', views.ProfileViewSet, base_name='profile') urlpatterns = router.urls diff --git a/src/account/views.py b/src/account/views.py index 50a0c059173f7d140f2a930f979cd8f6814abbd0..58c405059a1c21a80a62d1b1de6e658277a0f350 100644 --- a/src/account/views.py +++ b/src/account/views.py @@ -1,4 +1,7 @@ from rest_framework import viewsets +from rest_framework import permissions +from rest_framework.response import Response +from rest_framework.decorators import list_route from . import models from . import serializers @@ -6,4 +9,14 @@ from . import serializers class ProfileViewSet(viewsets.ModelViewSet): serializer_class = serializers.ProfileSerializer - queryset = models.Profile.objects.all() + permission_classes = (permissions.IsAuthenticated, ) + + def get_queryset(self): + user = self.request.user + if user.has_perm(permissions.IsAdminUser): + return models.Profile.objects.all() + + @list_route(methods=['get']) + def me(self, request): + serializer = self.serializer_class(request.user.profile) + return Response(serializer.data) diff --git a/src/homework/__init__.py b/src/homework/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/homework/admin.py b/src/homework/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..38fed616d8ac56d7290d795fd12ada5d18b4fadd --- /dev/null +++ b/src/homework/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin +from . import models + +# Register your models here. +admin.site.register(models.Solution) +admin.site.register(models.Task) \ No newline at end of file diff --git a/src/homework/apps.py b/src/homework/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..98901a15dfa44243cd9894dc15652b9634c738fd --- /dev/null +++ b/src/homework/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class HomeworkConfig(AppConfig): + name = 'homework' diff --git a/src/homework/migrations/0001_initial.py b/src/homework/migrations/0001_initial.py new file mode 100644 index 0000000000000000000000000000000000000000..57b9c1f5cea623dae04423523f427ff85e27bdc3 --- /dev/null +++ b/src/homework/migrations/0001_initial.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2018-01-24 12:09 +from __future__ import unicode_literals + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import homework.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Solution', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True)), + ('ready', models.BooleanField()), + ('files', models.FileField(blank=True, upload_to='', validators=[django.core.validators.FileExtensionValidator('image/png', 'image/jpeg', 'application/zip')])), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Student', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('homework', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='homework.Solution')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Task', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=150)), + ('date', models.DateTimeField(auto_now_add=True)), + ('deadline', models.DateTimeField(validators=[homework.models.validate_deadline])), + ('text', models.TextField()), + ('files', models.FileField(blank=True, upload_to='', validators=[django.core.validators.FileExtensionValidator('image/png', 'image/jpeg', 'application/zip')])), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='solution', + name='task', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='homework.Task'), + ), + ] diff --git a/src/homework/migrations/0002_auto_20180124_1453.py b/src/homework/migrations/0002_auto_20180124_1453.py new file mode 100644 index 0000000000000000000000000000000000000000..597c83e833b380d48452111a0e35eafaaa16969a --- /dev/null +++ b/src/homework/migrations/0002_auto_20180124_1453.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2018-01-24 13:53 +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 = [ + ('homework', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='student', + name='homework', + ), + migrations.RemoveField( + model_name='student', + name='user', + ), + migrations.AddField( + model_name='solution', + name='accepted', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='solution', + name='created_by', + field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='solution', + name='ready', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='solution', + name='task', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='task_solution', to='homework.Task'), + ), + migrations.AlterField( + model_name='task', + name='created_by', + field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.DeleteModel( + name='Student', + ), + ] diff --git a/src/homework/migrations/0003_auto_20180124_1818.py b/src/homework/migrations/0003_auto_20180124_1818.py new file mode 100644 index 0000000000000000000000000000000000000000..1fcd873a015a4518dc233d5a7071279bd0fe7165 --- /dev/null +++ b/src/homework/migrations/0003_auto_20180124_1818.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2018-01-24 17:18 +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 = [ + ('homework', '0002_auto_20180124_1453'), + ] + + operations = [ + migrations.AlterField( + model_name='task', + name='created_by', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/src/homework/migrations/__init__.py b/src/homework/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/homework/models.py b/src/homework/models.py new file mode 100644 index 0000000000000000000000000000000000000000..0e94c27c497efe4e6dd8eed001356a8b5b0a27b5 --- /dev/null +++ b/src/homework/models.py @@ -0,0 +1,69 @@ +from django.db import models +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError +from django.utils.translation import ugettext_lazy as _ +from django.utils import timezone +from django.core import validators +# from . import myfields + + +# 5MB - 5242880 +__MAX_UPLOAD_SIZE = 5242880 + + +def validate_deadline(date): + if date <= timezone.now(): + raise ValidationError(_('Date must be greater than now'), code='invalid') + + +# def validate_file_size(file): +# if file._size > __MAX_UPLOAD_SIZE: +# raise ValidationError(_('Please keep filesize under' + __MAX_UPLOAD_SIZE)) + +class Task(models.Model): + title = models.CharField(max_length=150, ) + date = models.DateTimeField(auto_now_add=True, editable=False, ) + deadline = models.DateTimeField(validators=[validate_deadline], ) + text = models.TextField() + created_by = models.ForeignKey(User, ) + files = models.FileField( + validators=[validators.FileExtensionValidator( + 'image/png', + 'image/jpeg', + 'application/zip', + )], + blank=True, + ) + # files = myfields.RestrictedFileField( + # content_types=['image/png', 'image/jpeg', 'application/zip'], + # max_upload_size=MAX_UPLOAD_SIZE, + # blank=True, + # null=True, + # ) +# solution_file = models.BooleanField() +# +# def clean(self): +# if self.deadline <= timezone.now(): +# raise ValidationError(_('Invalid date'), code='invalid') + + +class Solution(models.Model): + task = models.ForeignKey(Task, related_name='task_solution', on_delete=models.CASCADE, ) + # student = models.ForeignKey(account.models.Profile, related_name='student_solution', on_delete=models.CASCADE) + date = models.DateTimeField(auto_now_add=True, editable=False, ) + ready = models.BooleanField(default=False, ) #if(Soulution.date <= Task.deadline) + accepted = models.BooleanField(default=False, ) + files = models.FileField( + validators=[validators.FileExtensionValidator( + 'image/png', + 'image/jpeg', + 'application/zip', + )], + blank=True, + ) + created_by = models.ForeignKey(User, ) + # files = myfields.RestrictedFileField( + # content_types=['image/png', 'image/jpeg', 'application/zip'], + # max_upload_size=MAX_UPLOAD_SIZE, + # blank=True, + # ) diff --git a/src/homework/myfields.py b/src/homework/myfields.py new file mode 100644 index 0000000000000000000000000000000000000000..c343daa73b1aa8407ad21dfdab6f997d51c2a021 --- /dev/null +++ b/src/homework/myfields.py @@ -0,0 +1,41 @@ +from django import forms +from django.template.defaultfilters import filesizeformat +from django.utils.translation import ugettext_lazy as _ +from django.core.exceptions import ValidationError + + +class RestrictedFileField(forms.FileField): + """ + Same as FileField, but you can specify: + * content_types - list containing allowed content_types. Example: ['application/pdf', 'image/jpeg'] + * max_upload_size - a number indicating the maximum file size allowed for upload. + 2.5MB - 2621440 + 5MB - 5242880 + 10MB - 10485760 + 20MB - 20971520 + 50MB - 5242880 + 100MB - 104857600 + 250MB - 214958080 + 500MB - 429916160 +""" + + def __init__(self, *args, **kwargs): + self.content_types = kwargs.pop("content_types") + self.max_upload_size = kwargs.pop("max_upload_size") + super().__init__(*args, **kwargs) + + def clean(self, data, initial=None): + file = super().clean(data, initial) + + try: + content_type = file.content_type + if content_type in self.content_types: + if file._size > self.max_upload_size: + raise ValidationError(_('Please keep filesize under %s. Current filesize %s') % ( + filesizeformat(self.max_upload_size), filesizeformat(file._size))) + else: + raise ValidationError(_('Filetype not supported.')) + except AttributeError: + pass + + return data diff --git a/src/homework/permissions.py b/src/homework/permissions.py new file mode 100644 index 0000000000000000000000000000000000000000..762faedb33fa88dc0dbe620325847a39bd1809ce --- /dev/null +++ b/src/homework/permissions.py @@ -0,0 +1,37 @@ +from rest_framework.permissions import BasePermission +from rest_framework.permissions import SAFE_METHODS + + +class IsStaffOrReadOnly(BasePermission): + """ + The request is authenticated as a staff, or is a read-only request. + """ + + def has_permission(self, request, view): + return ( + request.method in SAFE_METHODS or + request.user and + request.user.is_staff + ) + + +class IsAuthenticatedOrReadOnly(BasePermission): + """ + The request is authenticated as a user, or is a read-only request. + """ + + def has_permission(self, request, view): + return ( + request.method in SAFE_METHODS or + request.user and + request.user.is_authenticated + ) + + +class IsStaffOrReadOnlyForAuthenticated(BasePermission): + + def has_object_permission(self, request, view, obj): + return (request.user.is_staff or + request.method in SAFE_METHODS and + request.user.is_authenticated + ) diff --git a/src/homework/serializers.py b/src/homework/serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..d27ebf8cbaaded2b210af704aa9ec7a27b4f0444 --- /dev/null +++ b/src/homework/serializers.py @@ -0,0 +1,18 @@ +from rest_framework import serializers +from . import models + + +class TaskSerializer(serializers.ModelSerializer): + class Meta: + model = models.Task + read_only_fields = ('created_by', 'date') + extra_kwargs = {'created_by': {'default': serializers.CurrentUserDefault()}} + fields = '__all__' + + +class SolutionSerializer(serializers.ModelSerializer): + class Meta: + model = models.Solution + read_only_fields = ('created_by', 'date' 'ready') + extra_kwargs = {'created_by': {'default': serializers.CurrentUserDefault()}} + fields = '__all__' diff --git a/src/homework/tests.py b/src/homework/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/src/homework/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/src/homework/urls.py b/src/homework/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..b049f63b55b713070f71fd581696af0c728649ee --- /dev/null +++ b/src/homework/urls.py @@ -0,0 +1,8 @@ +from rest_framework import routers +from . import views + +router = routers.DefaultRouter() +router.register(r'tasks', views.TasksViewSet) +router.register(r'solutions', views.SolutionViewSet) + +urlpatterns = router.urls diff --git a/src/homework/views.py b/src/homework/views.py new file mode 100644 index 0000000000000000000000000000000000000000..2d0344bd9b35109ba97288633535d3bfa4c0921e --- /dev/null +++ b/src/homework/views.py @@ -0,0 +1,28 @@ +from rest_framework import viewsets + + +from . import serializers +from . import models +from . import permissions +from rest_framework.response import Response +from rest_framework import status + + +class TasksViewSet(viewsets.ModelViewSet): + serializer_class = serializers.TaskSerializer + queryset = models.Task.objects.all() + permission_classes = (permissions.IsStaffOrReadOnly, ) + + +class SolutionViewSet(viewsets.ModelViewSet): + serializer_class = serializers.SolutionSerializer + queryset = models.Solution.objects.all() + permission_classes = (permissions.SolutionPermission, ) + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.validated_data['accepted'] = False + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) diff --git a/src/kszkepzes/settings.py b/src/kszkepzes/settings.py index e40e7e80fe851697b1e28f38f02753195c82e1a0..db85da936cb6bb127570816efbae7d436ec7eaa1 100644 --- a/src/kszkepzes/settings.py +++ b/src/kszkepzes/settings.py @@ -41,6 +41,7 @@ INSTALLED_APPS = [ 'rest_framework', 'social_django', 'authsch', + 'homework', 'account', 'stats', 'news', diff --git a/src/kszkepzes/urls.py b/src/kszkepzes/urls.py index 8c50beb39fbaf4fc39fc467be473e1b76701c70a..cb9bbcdc434a5db23b90606ebf69b6744b115a65 100644 --- a/src/kszkepzes/urls.py +++ b/src/kszkepzes/urls.py @@ -19,6 +19,7 @@ from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url('', include('social_django.urls', namespace='social')), + url(r'^api/v1/homework/', include('homework.urls')), url(r'^api/v1/', include('stats.urls')), - url(r'^api/v1/', include('news.urls')), + url(r'^api/v1/', include('account.urls')), ] diff --git a/src/news/serializers.py b/src/news/serializers.py index 4aa15a19e523d52e99102531debfac4675b35fdc..ced9a8a2bce991d678e1cac1a7d3b8a83c0c4b20 100644 --- a/src/news/serializers.py +++ b/src/news/serializers.py @@ -5,18 +5,4 @@ from rest_framework import serializers class ArticleListSerializer(serializers.ModelSerializer): class Meta: model = Article - fields = [ - 'author', - 'title', - ] - - -class ArticleDetailSerializer(serializers.ModelSerializer): - class Meta: - model = Article - fields = [ - 'author', - 'title', - 'text', - 'date', - ] + fields = '__all__' diff --git a/src/news/urls.py b/src/news/urls.py index 5ac1acbe0d5aa6adc40cea421967e2387ea80db4..b08ebdd72a8c2303635cac63862bb8d0320e419e 100644 --- a/src/news/urls.py +++ b/src/news/urls.py @@ -1,12 +1,9 @@ from rest_framework import routers -from django.conf.urls import url -from news.views import NewsListView, NewsDetailView, NewsUpdateView +from news import views -router = routers.DefaultRouter() -urlpatterns = [ - url(r'^news/$', NewsListView.as_view()), - url(r'^news/(?P<pk>\d+)/$', NewsDetailView.as_view()), - url(r'^news/edit/(?P<pk>\d+)/$', NewsUpdateView.as_view()), -] -urlpatterns += router.urls +router = routers.DefaultRouter(trailing_slash=False) + +router.register(r'news', views.NewsViewSet) + +urlpatterns = router.urls diff --git a/src/news/views.py b/src/news/views.py index db05e3b74e27fd0140f36628a29d8538abd259f5..ab6f5f78958686a8070bf004faa4877061213d4f 100644 --- a/src/news/views.py +++ b/src/news/views.py @@ -1,21 +1,10 @@ from rest_framework.permissions import IsAdminUser, IsAuthenticated -from rest_framework import generics +from rest_framework import viewsets from news.models import Article -from news.serializers import ArticleListSerializer, ArticleDetailSerializer +from news.serializers import ArticleListSerializer -class NewsListView(generics.ListAPIView): - queryset = Article.objects.all() +class NewsViewSet(viewsets.ModelViewSet): serializer_class = ArticleListSerializer - - -class NewsDetailView(generics.RetrieveAPIView): - queryset = Article.objects.all() - serializer_class = ArticleDetailSerializer permission_classes = [IsAuthenticated] - - -class NewsUpdateView(generics.RetrieveUpdateAPIView): queryset = Article.objects.all() - serializer_class = ArticleDetailSerializer - permission_classes = [IsAdminUser]