diff --git a/.gitignore b/.gitignore index d347aa28bc0a0b7a489ac077f7b66e6c54324346..ffb3de3832416b870bf800ac19d59da9db49bf56 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,6 @@ ENV/ environment.sh environment.ps1 environment.bat + +#PyCharm +.idea/ \ No newline at end of file diff --git a/requirements/development.txt b/requirements/development.txt index 23ec9249da5d8fb5961e4ee464cf3b766be8de7c..91118f565509bfebb4e633fd2f19702300e03c0c 100644 --- a/requirements/development.txt +++ b/requirements/development.txt @@ -14,7 +14,7 @@ django-extensions==1.9.9 django-import-export==1.0.0 django-social-authsch==0.1 django-solo==1.1.3 -django==2.0.1 +django>=2.0.10 djangorestframework==3.7.7 et-xmlfile==1.0.1 # via openpyxl first==2.0.1 # via pip-tools @@ -38,10 +38,10 @@ pyjwt==1.5.3 # via social-auth-core python-language-server==0.13.0 python3-openid==3.1.0 # via social-auth-core pytz==2017.2 # via django -pyyaml==3.12 # via tablib +pyyaml>=4.2b1 # via tablib requests-oauthlib==0.8.0 # via social-auth-core -requests==2.18.4 # via requests-oauthlib, social-auth-core -rope==0.10.7 # via python-language-server +requests>=2.20.0 # via requests-oauthlib, social-auth-core +rope>=0.11.0 # via python-language-server six==1.10.0 # via django-extensions, pip-tools, pydocstyle, social-auth-app-django, social-auth-core snowballstemmer==1.2.1 # via pydocstyle social-auth-app-django==2.0.0 # via django-social-authsch @@ -49,7 +49,7 @@ social-auth-core==1.5.0 # via django-social-authsch, social-auth-app-django tablib==0.12.1 # via django-import-export typing==3.6.4 # via django-extensions unicodecsv==0.14.1 # via tablib -urllib3==1.22 # via requests +urllib3>=1.23 # via requests xlrd==1.1.0 # via tablib xlwt==1.3.0 # via tablib yapf==0.20.1 # via python-language-server diff --git a/src/account/auth_pipeline.py b/src/account/auth_pipeline.py index b1918c536d09e7b1de94f3c63c249ed6eff9e5f2..34ca0180920054311bbad8835511cac3ae367065 100644 --- a/src/account/auth_pipeline.py +++ b/src/account/auth_pipeline.py @@ -1,4 +1,5 @@ from django.core import exceptions +from common import email from . import models @@ -9,3 +10,5 @@ def create_profile(backend, user, response, *args, **kwargs): user.profile except exceptions.ObjectDoesNotExist: models.Profile.objects.create(user=user) + if user.email is not None: + email.registration(user) diff --git a/src/account/migrations/0001_initial.py b/src/account/migrations/0001_initial.py index 8c4b5267abb550ca5a08c3e50a2b3f0f72a54d57..8550f16222d06151f89e7dbf62fb44cc7233e753 100644 --- a/src/account/migrations/0001_initial.py +++ b/src/account/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-11 14:13 -from __future__ import unicode_literals +# Generated by Django 2.0.1 on 2019-01-17 15:06 from django.conf import settings from django.db import migrations, models @@ -16,13 +14,37 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='Deadline', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('deadline', models.DateTimeField(null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='GroupChoice', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('choice', 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.CreateModel( name='Profile', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('join_date', models.DateField()), - ('pref_group', models.CharField(choices=[('DT', 'DevTeam'), ('NET', 'NeTeam'), ('ST', 'SecurITeam'), ('SYS', 'SysAdmin'), ('N', 'None')], default='None', max_length=10)), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('join_date', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('motivation_about', models.TextField(blank=True, default='')), + ('motivation_profession', models.TextField(blank=True, default='')), + ('motivation_exercise', models.TextField(blank=True, default='')), + ('nick', models.CharField(blank=True, default='', max_length=15)), + ('signed', models.BooleanField(default=False)), + ('role', models.CharField(choices=[('Staff', 'Staff'), ('Applicant', 'Applicant'), ('Student', 'Student')], default='Applicant', max_length=10)), + ('groups', models.ManyToManyField(blank=True, related_name='profiles', to='account.GroupChoice')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)), ], ), ] diff --git a/src/account/migrations/0002_auto_20171114_2144.py b/src/account/migrations/0002_auto_20171114_2144.py deleted file mode 100644 index fc76cef337448de84b568817d4fe13dd023c0f13..0000000000000000000000000000000000000000 --- a/src/account/migrations/0002_auto_20171114_2144.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-14 20:44 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='profile', - name='pref_group', - field=models.CharField(choices=[('DT', 'DevTeam'), ('NET', 'NeTeam'), ('ST', 'SecurITeam'), ('SYS', 'SysAdmin'), ('HAT', 'Hallgatói Tudásbázis'), ('N', 'None')], default='None', max_length=10), - ), - ] diff --git a/src/account/migrations/0002_auto_20190122_1341.py b/src/account/migrations/0002_auto_20190122_1341.py new file mode 100644 index 0000000000000000000000000000000000000000..e9ee7b662af95a01f09815050e8650ab04603b30 --- /dev/null +++ b/src/account/migrations/0002_auto_20190122_1341.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.1 on 2019-01-22 12:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='role', + field=models.CharField(choices=[('Staff', 'Staff'), ('Applicant', 'Applicant'), ('Student', 'Student'), ('Denied', 'Denied')], default='Applicant', max_length=10), + ), + ] diff --git a/src/account/migrations/0003_profile_nick.py b/src/account/migrations/0003_profile_nick.py deleted file mode 100644 index 7a3542787808c4e28493012327718f79731e9f3c..0000000000000000000000000000000000000000 --- a/src/account/migrations/0003_profile_nick.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- 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 deleted file mode 100644 index 424407361c1f04b2068daab365199eb7d5aa9e2b..0000000000000000000000000000000000000000 --- a/src/account/migrations/0004_profile_signed.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- 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 deleted file mode 100644 index d5e5fb83b2f2deeaf85dba89cb35de502a01ea4e..0000000000000000000000000000000000000000 --- a/src/account/migrations/0005_auto_20171223_1455.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- 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 deleted file mode 100644 index 035ce31da185ae28746fa3674f1670e1491e671f..0000000000000000000000000000000000000000 --- a/src/account/migrations/0006_auto_20180123_1713.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- 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 deleted file mode 100644 index 27a4efe8c61f7cb19bb1f349a8c21b6e66f1e60e..0000000000000000000000000000000000000000 --- a/src/account/migrations/0007_auto_20180123_1723.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- 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 deleted file mode 100644 index fd3af443879f22344cc51f1334eec41e445948ad..0000000000000000000000000000000000000000 --- a/src/account/migrations/0008_auto_20180124_1821.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- 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 deleted file mode 100644 index c9d1e0a2e2aa16abd91adb4970ac860e5e991ffc..0000000000000000000000000000000000000000 --- a/src/account/migrations/0009_profile_motivation.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- 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 deleted file mode 100644 index 2a9b50d5d2e04d41c68b1aba0135774b1bd97bc3..0000000000000000000000000000000000000000 --- a/src/account/migrations/0010_auto_20180124_1929.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- 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/migrations/0011_deadline.py b/src/account/migrations/0011_deadline.py deleted file mode 100644 index e88ed12d346eefbc0226895bd725d1bc49541928..0000000000000000000000000000000000000000 --- a/src/account/migrations/0011_deadline.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2018-01-25 18:42 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0010_auto_20180124_1929'), - ] - - operations = [ - migrations.CreateModel( - name='Deadline', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('deadline', models.DateField()), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/src/account/migrations/0012_auto_20180125_1957.py b/src/account/migrations/0012_auto_20180125_1957.py deleted file mode 100644 index 92845d1c75bcd23abaf68710d8c4bf93162fc7dd..0000000000000000000000000000000000000000 --- a/src/account/migrations/0012_auto_20180125_1957.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2018-01-25 18:57 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0011_deadline'), - ] - - operations = [ - migrations.AlterField( - model_name='deadline', - name='deadline', - field=models.DateField(null=True), - ), - ] diff --git a/src/account/migrations/0013_auto_20180203_2007.py b/src/account/migrations/0013_auto_20180203_2007.py deleted file mode 100644 index 8ac041e4d538e265f8f47fdaae0960ed54edd6b2..0000000000000000000000000000000000000000 --- a/src/account/migrations/0013_auto_20180203_2007.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0.1 on 2018-02-03 19:07 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0012_auto_20180125_1957'), - ] - - operations = [ - migrations.AlterField( - model_name='profile', - name='join_date', - field=models.DateTimeField(auto_now=True), - ), - ] diff --git a/src/account/migrations/0014_auto_20180203_2010.py b/src/account/migrations/0014_auto_20180203_2010.py deleted file mode 100644 index 0ad3c0554ec582ff41cc7536f02287c77f3fdb66..0000000000000000000000000000000000000000 --- a/src/account/migrations/0014_auto_20180203_2010.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0.1 on 2018-02-03 19:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0013_auto_20180203_2007'), - ] - - operations = [ - migrations.AlterField( - model_name='deadline', - name='deadline', - field=models.DateTimeField(null=True), - ), - ] diff --git a/src/account/migrations/0015_auto_20180203_2014.py b/src/account/migrations/0015_auto_20180203_2014.py deleted file mode 100644 index e823387e6830f772c4651f21c876c0ae0534f4cc..0000000000000000000000000000000000000000 --- a/src/account/migrations/0015_auto_20180203_2014.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.0.1 on 2018-02-03 19:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0014_auto_20180203_2010'), - ] - - operations = [ - migrations.AddField( - model_name='profile', - name='updated_at', - field=models.DateTimeField(auto_now=True), - ), - migrations.AlterField( - model_name='profile', - name='join_date', - field=models.DateTimeField(auto_now_add=True), - ), - ] diff --git a/src/account/migrations/0016_auto_20180203_2022.py b/src/account/migrations/0016_auto_20180203_2022.py deleted file mode 100644 index 86c2ae6dc33f0c16b7e8f1ea9489baf1e3d9cdd6..0000000000000000000000000000000000000000 --- a/src/account/migrations/0016_auto_20180203_2022.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.0.1 on 2018-02-03 19:22 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0015_auto_20180203_2014'), - ] - - operations = [ - migrations.AlterField( - model_name='profile', - name='motivation', - field=models.TextField(blank=True, default=''), - ), - migrations.AlterField( - model_name='profile', - name='nick', - field=models.CharField(blank=True, default='', max_length=15), - ), - ] diff --git a/src/account/migrations/0017_auto_20180205_2004.py b/src/account/migrations/0017_auto_20180205_2004.py deleted file mode 100644 index d2137f9af9d55ca32fd4b79ba44f09ce56a8abd5..0000000000000000000000000000000000000000 --- a/src/account/migrations/0017_auto_20180205_2004.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 2.0.1 on 2018-02-05 19:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0016_auto_20180203_2022'), - ] - - operations = [ - migrations.RenameField( - model_name='profile', - old_name='motivation', - new_name='motivation_about', - ), - migrations.AddField( - model_name='profile', - name='motivation_exercise', - field=models.TextField(blank=True, default=''), - ), - migrations.AddField( - model_name='profile', - name='motivation_profession', - field=models.TextField(blank=True, default=''), - ), - ] diff --git a/src/account/models.py b/src/account/models.py index 71b79f0c59102fe236def8d5459f6e3170e9f417..082a83c98de9be5d956af810734422ca9897c1c7 100644 --- a/src/account/models.py +++ b/src/account/models.py @@ -19,16 +19,27 @@ class GroupChoice(models.Model): class Profile(models.Model): + ROLES = ( + ('Staff', 'Staff'), + ('Applicant', 'Applicant'), + ('Student', 'Student'), + ('Denied', 'Denied'), + ) join_date = models.DateTimeField(auto_now_add=True, editable=False) updated_at = models.DateTimeField(auto_now=True, editable=False) - user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE) + user = models.OneToOneField( + User, + related_name='profile', + on_delete=models.CASCADE + ) # TODO: Change the default to json render side motivation_about = models.TextField(blank=True, default='') motivation_profession = models.TextField(blank=True, default='') motivation_exercise = models.TextField(blank=True, default='') nick = models.CharField(max_length=15, blank=True, default='') signed = models.BooleanField(default=False, null=False) - groups = models.ManyToManyField(GroupChoice, related_name='profiles') + groups = models.ManyToManyField(GroupChoice, related_name='profiles', blank=True) + role = models.CharField(max_length=10, choices=ROLES, default='Applicant') @property def full_name(self): diff --git a/src/account/serializers.py b/src/account/serializers.py index 322a882ae63cca316144a20a811a7607d4e1b609..e38ad58cb689559fb56cb78a93e94631401e0ecc 100644 --- a/src/account/serializers.py +++ b/src/account/serializers.py @@ -1,5 +1,7 @@ from rest_framework import serializers from account import models +from common.middleware import CurrentUserMiddleware +from common import email class ChoiceSerializer(serializers.ModelSerializer): @@ -9,31 +11,53 @@ class ChoiceSerializer(serializers.ModelSerializer): class ProfileSerializer(serializers.ModelSerializer): - groups = serializers.SlugRelatedField(many=True, slug_field="choice", queryset=models.GroupChoice.objects.all()) + groups = serializers.SlugRelatedField(many=True, slug_field='choice', queryset=models.GroupChoice.objects.all()) updated_at = serializers.DateTimeField(read_only=True) - signed = serializers.BooleanField() + full_name = serializers.SerializerMethodField() class Meta: model = models.Profile + read_only_fields = ('id', 'join_date', 'updated_at', 'full_name', ) fields = ( 'id', 'join_date', 'updated_at', - 'user', 'nick', 'signed', 'groups', 'motivation_about', 'motivation_profession', 'motivation_exercise', + 'full_name', + 'role', ) - def validate(self, data): + def validate_updated_at(self, value): deadline = models.Deadline.get_solo().deadline - if deadline is None: - return data - - if data['signed'] is True and data['updated_at'] > deadline: + if deadline is not None and value > deadline: raise serializers.ValidationError("You cannot join after the deadline") - - return data + return value + + def validate_role(self, value): + modifier_role = CurrentUserMiddleware.get_current_user_profile().role + if value != modifier_role and modifier_role != "Staff": + raise serializers.ValidationError("You don't have permission change role") + return value + + def validate_signed(self, value): + modifier = CurrentUserMiddleware.get_current_user_profile() + if value is False and modifier.role != "Staff": + raise serializers.ValidationError("You cannot join without signed") + return value + + def update(self, instance, validated_data): + new_role = validated_data.get('role', instance.role) + if instance.role != new_role: + if new_role == 'Student': + email.admitted(instance.user) + if new_role == 'Denied': + email.denied(instance.user) + return super().update(instance, validated_data) + + def get_full_name(self, obj): + return obj.full_name diff --git a/src/account/views.py b/src/account/views.py index fde64a8529b11c5561318014a5305b2ce5aef8df..c2ad4ec5b61ff4ae7545845c8b1b9149ec42bba5 100644 --- a/src/account/views.py +++ b/src/account/views.py @@ -2,6 +2,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 common.permissions import IsSafeOrPatch from . import models from . import serializers @@ -9,13 +10,15 @@ from . import serializers class ProfileViewSet(viewsets.ModelViewSet): serializer_class = serializers.ProfileSerializer - permission_classes = (permissions.IsAuthenticated, ) + permission_classes = (permissions.IsAuthenticated, IsSafeOrPatch) def get_queryset(self): user = self.request.user - if user.has_perm(permissions.IsAdminUser): + if user.profile.role == 'Staff': + role = self.request.query_params.get("role", None) + if role is not None: + return models.Profile.objects.filter(role=role) return models.Profile.objects.all() - return models.Profile.objects.filter(pk=user.profile.id) @list_route(methods=['get']) diff --git a/src/common/email.py b/src/common/email.py new file mode 100644 index 0000000000000000000000000000000000000000..1c4151ebb776e983001a6f4a7690b904e66148ad --- /dev/null +++ b/src/common/email.py @@ -0,0 +1,32 @@ +from django.core.mail import send_mail + + +def registration(email): + subject = "REGISTRATION TEST" + message = "Üdvözlünk a kszképzésen!" + send_mail(subject, message, 'noreply@devteam.sch.bme.hu', [email, ]) + + +def admitted(email): + subject = "ADMITTED TEST" + message = "Gratulálunk, te vagy a kiválasztott!!" + send_mail(subject, message, 'noreply@devteam.sch.bme.hu', [email, ]) + + +def denied(email): + subject = "DENIED TEST" + message = "Sajnos idén nem nyertél felvételt, próbáld meg legközelebb" + send_mail(subject, message, 'noreply@devteam.sch.bme.hu', [email, ]) + + +def new_homework(emails): + subject = "NEW HOMEWORK TEST" + message = "Szia!\nEgy új házi lett kiadva, ha tíz percen belül megoldod akkor fasza gyerek vagy," \ + " ha nem életed végéig bánnifogod..." + send_mail(subject, message, 'noreply@devteam.sch.bme.hu', emails) + + +def homework_corrected(email): + subject = "HOMEWORK CORRECTED TEST" + message = "Nagyszerű mentoraink kijavították házifeladatod, vajon most kaptál meglepit?!" + send_mail(subject, message, 'noreply@devteam.sch.bme.hu', [email, ]) diff --git a/src/common/emails/admitted.txt b/src/common/emails/admitted.txt new file mode 100644 index 0000000000000000000000000000000000000000..f719168dc8c3c64344246577ec5f2ec4d30a4645 --- /dev/null +++ b/src/common/emails/admitted.txt @@ -0,0 +1,6 @@ +Kedves %(name)s! + +Gratulálunk! Jelentkezésedet elfogadtuk! Innentől kezdve aktívan figyeld leveleidet, mert hamarosan megkapod az első képzésalkalmunkra szóló meghívódat! + +Üdvözlettel: +A mentorok \ No newline at end of file diff --git a/src/common/emails/denied.txt b/src/common/emails/denied.txt new file mode 100644 index 0000000000000000000000000000000000000000..b085e7a886dee3306df419745fd40ccae023e2c7 --- /dev/null +++ b/src/common/emails/denied.txt @@ -0,0 +1,6 @@ +Kedves %(name)s! + +Sajnos - hely hiányában - idei képzésalkamunkon nem tudsz részt venni. Ne csüggedj, következő évben is indítunk ilyen programot, és ha addig is szorgalmasan tevékenykedsz, akkor biztosan be fogsz kerülni! + +Üdvözlettel: +A mentorok \ No newline at end of file diff --git a/src/common/emails/homework_corrected.txt b/src/common/emails/homework_corrected.txt new file mode 100644 index 0000000000000000000000000000000000000000..0128e57221ea25fd7dc69c1bff505cc817a08112 --- /dev/null +++ b/src/common/emails/homework_corrected.txt @@ -0,0 +1,7 @@ +Kedves %(name)s! + +A(z) %(title)s című házi feladatod státusza %(status)s lett. +Az alábbi linken tudod megtekinteni a részleteket: %(link)s . + +Üdvözlettel: +A mentorok \ No newline at end of file diff --git a/src/common/emails/new_homework.txt b/src/common/emails/new_homework.txt new file mode 100644 index 0000000000000000000000000000000000000000..9a6618346dc6ca140dd69b0eaa3238676fcebe7f --- /dev/null +++ b/src/common/emails/new_homework.txt @@ -0,0 +1,7 @@ +Kedves %(name)s! + +Új házi feladat került kiadásra, melyet az alábbi linken tudsz megtekinteni: %(link)s +Határidő: %(deadline)s + +Sok sikert! +A mentorok \ No newline at end of file diff --git a/src/common/emails/registration.txt b/src/common/emails/registration.txt new file mode 100644 index 0000000000000000000000000000000000000000..f9068e283264a6509bf534bc4c7e7a2c677cd432 --- /dev/null +++ b/src/common/emails/registration.txt @@ -0,0 +1,10 @@ +Kedves %(name)s! + +Örömmel vettük jelenkezésedet az idei KSZKépzés programunkba. Azonban ahhoz, hogy ezt el is tudjuk bírálni, még szükségünk van pár dologra tőled: + - ki kell töltened a profilodat, + - találsz pár feladatot is az oldalon, ezeket meg kell oldanod, + - gyere el Újoncdélutánunkra, erről majd még küldünk emailt. +A feladatokhoz jó szórakozást kívánunk, és ne ijedj meg, ha valamit nem tudsz fejből, Google-t és egyéb segítséget természetesen használhatsz! + +Üdvözlettel: +A mentorok diff --git a/src/common/middleware.py b/src/common/middleware.py new file mode 100644 index 0000000000000000000000000000000000000000..82f040c15458facfd7e6603558465825b2dc4c4a --- /dev/null +++ b/src/common/middleware.py @@ -0,0 +1,19 @@ +from threading import local + +_profile = local() + + +class CurrentUserMiddleware(object): + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + _profile.value = request.user + return self.get_response(request) + + def get_current_user_profile(): + return _profile.value.profile + + def get_current_user(): + return _profile.value diff --git a/src/common/permissions.py b/src/common/permissions.py index 5aac2f078cb7cb6044024c7c29bb01559a8fed64..7d2384c7bf9186939272c68ac9d5f61dbfe58e72 100644 --- a/src/common/permissions.py +++ b/src/common/permissions.py @@ -3,27 +3,28 @@ 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 + return request.method in SAFE_METHODS or\ + (request.user.is_authenticated and request.user.profile.role == 'Staff') class IsStaffOrReadOnlyForAuthenticated(BasePermission): - """ - The request is authenticated as a staff, or is a read-only request for authenticated. - """ - def has_permission(self, request, view): - return request.user.is_staff or request.method in SAFE_METHODS and request.user.is_authenticated + return request.user.is_authenticated and\ + (request.method in SAFE_METHODS or request.user.profile.role == 'Staff') class IsStaffUser(BasePermission): - """ - The request is authenticated as a staff - """ + def has_permission(self, request, view): + return request.user.is_authenticated and request.user.profile.role == 'Staff' + + +class IsSafeOrPatch(BasePermission): + def has_permission(self, request, view): + return request.method in SAFE_METHODS or request.method == 'PATCH' + +class IsStaffOrStudent(BasePermission): def has_permission(self, request, view): - return request.user.is_staff + return request.user.is_authenticated and\ + (request.user.profile.role == 'Staff' or request.user.profile.role == 'Student') diff --git a/src/document/migrations/0001_initial.py b/src/document/migrations/0001_initial.py index 1982261b5587cc0c2c527977925709598452aeac..a6c028301e73f9984456b29c47277a90f2ca37ed 100644 --- a/src/document/migrations/0001_initial.py +++ b/src/document/migrations/0001_initial.py @@ -1,5 +1,7 @@ -# Generated by Django 2.0.1 on 2018-05-28 15:21 +# Generated by Django 2.0.1 on 2019-01-17 15:06 +import common.validators +import django.core.validators from django.db import migrations, models import django.db.models.deletion @@ -9,7 +11,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('account', '0017_auto_20180205_2004'), + ('homework', '0001_initial'), + ('account', '0001_initial'), ] operations = [ @@ -19,8 +22,9 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('uploaded_at', models.DateTimeField(auto_now_add=True)), ('name', models.CharField(max_length=150)), - ('description', models.TextField()), - ('file', models.FileField(upload_to='')), + ('description', models.TextField(blank=True, default='')), + ('file', models.FileField(upload_to='', validators=[django.core.validators.FileExtensionValidator(['png', 'jpeg', 'jpg', 'zip']), common.validators.FileSizeValidator(size_limit=52428800)])), + ('solution', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='files', to='homework.Solution')), ('uploaded_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='account.Profile')), ], ), diff --git a/src/document/migrations/0002_auto_20190121_1332.py b/src/document/migrations/0002_auto_20190121_1332.py new file mode 100644 index 0000000000000000000000000000000000000000..5cf755b92259e186c711bd5be422791f17294858 --- /dev/null +++ b/src/document/migrations/0002_auto_20190121_1332.py @@ -0,0 +1,20 @@ +# Generated by Django 2.0.1 on 2019-01-21 12:32 + +import common.validators +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('document', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='document', + name='file', + field=models.FileField(blank=True, default='', upload_to='', validators=[django.core.validators.FileExtensionValidator(['png', 'jpeg', 'jpg', 'zip']), common.validators.FileSizeValidator(size_limit=52428800)]), + ), + ] diff --git a/src/document/migrations/0003_auto_20190121_1335.py b/src/document/migrations/0003_auto_20190121_1335.py new file mode 100644 index 0000000000000000000000000000000000000000..fa4d1c22ae825d615e093126a69d4a0a76132ad0 --- /dev/null +++ b/src/document/migrations/0003_auto_20190121_1335.py @@ -0,0 +1,20 @@ +# Generated by Django 2.0.1 on 2019-01-21 12:35 + +import common.validators +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('document', '0002_auto_20190121_1332'), + ] + + operations = [ + migrations.AlterField( + model_name='document', + name='file', + field=models.FileField(blank=True, upload_to='', validators=[django.core.validators.FileExtensionValidator(['png', 'jpeg', 'jpg', 'zip']), common.validators.FileSizeValidator(size_limit=52428800)]), + ), + ] diff --git a/src/document/migrations/0004_auto_20190123_1812.py b/src/document/migrations/0004_auto_20190123_1812.py new file mode 100644 index 0000000000000000000000000000000000000000..9eb42c6664d6d3fe22abb6ec091b7ea965643db5 --- /dev/null +++ b/src/document/migrations/0004_auto_20190123_1812.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.1 on 2019-01-23 17:12 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('document', '0003_auto_20190121_1335'), + ] + + operations = [ + migrations.AlterField( + model_name='document', + name='solution', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='homework.Solution'), + ), + ] diff --git a/src/document/migrations/0005_auto_20190129_1438.py b/src/document/migrations/0005_auto_20190129_1438.py new file mode 100644 index 0000000000000000000000000000000000000000..5c207c4a9776a2776d3d1b5da9d0473d2a283175 --- /dev/null +++ b/src/document/migrations/0005_auto_20190129_1438.py @@ -0,0 +1,25 @@ +# Generated by Django 2.0.1 on 2019-01-29 13:38 + +import common.validators +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('document', '0004_auto_20190123_1812'), + ] + + operations = [ + migrations.AlterField( + model_name='document', + name='file', + field=models.FileField(blank=True, null=True, upload_to='', validators=[django.core.validators.FileExtensionValidator(['png', 'jpeg', 'jpg', 'zip']), common.validators.FileSizeValidator(size_limit=52428800)]), + ), + migrations.AlterField( + model_name='document', + name='name', + field=models.CharField(blank=True, default='', max_length=150), + ), + ] diff --git a/src/document/models.py b/src/document/models.py index e2c30e36742be70a2cb4b4f9b429dcde73eab4c5..a9d72a22f77c1d87841b0e580c3747a57b73961e 100644 --- a/src/document/models.py +++ b/src/document/models.py @@ -1,14 +1,30 @@ from django.db import models +from django.core import validators from account.models import Profile +from homework.models import Solution +from common.validators import FileSizeValidator class Document(models.Model): uploaded_by = models.ForeignKey(Profile, on_delete=models.DO_NOTHING) uploaded_at = models.DateTimeField(auto_now_add=True, editable=False) - name = models.CharField(max_length=150) - description = models.TextField() - file = models.FileField() + name = models.CharField(max_length=150, blank=True, default='') + description = models.TextField(blank=True, default='') + file = models.FileField( + validators=[ + validators.FileExtensionValidator([ + 'png', + 'jpeg', + 'jpg', + 'zip', + ]), + FileSizeValidator(size_limit=52428800), # 52428800 - 50MiB + ], + blank=True, + null=True + ) + solution = models.ForeignKey(Solution, related_name='files', on_delete=models.CASCADE) def __str__(self): return self.name diff --git a/src/document/serializers.py b/src/document/serializers.py index 47e0e1cd73d13b5e0bc22bd79aba3535a9e8002e..f7ad1c31123d1a5e159c07187174d087d5daaf6e 100644 --- a/src/document/serializers.py +++ b/src/document/serializers.py @@ -1,13 +1,28 @@ from rest_framework import serializers - from common.serializers import CurrentUserProfileDefault from . import models +from common.middleware import CurrentUserMiddleware + +_max_count = 1 class DocumentSerializer(serializers.ModelSerializer): - uploaded_at = serializers.DateTimeField(read_only=True) uploaded_by = serializers.HiddenField(default=CurrentUserProfileDefault()) + uploaded_by_name = serializers.SerializerMethodField() class Meta: model = models.Document - fields = ('uploaded_by', 'uploaded_at', 'name', 'description', 'file') + fields = ('uploaded_by', 'uploaded_at', 'name', 'description', 'file', 'uploaded_by_name', 'solution', ) + + def get_uploaded_by_name(self, obj): + return obj.uploaded_by.full_name + + def validate_solution(self, value): + profile = CurrentUserMiddleware.get_current_user_profile() + if value not in profile.solution.all(): + raise serializers.ValidationError('You dont have permission!') + count = models.Document.objects.filter(uploaded_by=profile, solution=value).count() + if count >= _max_count: + raise serializers.ValidationError('You cant upload more than ' + str(_max_count) + + ' document to one solution!') + return value diff --git a/src/document/views.py b/src/document/views.py index 70e78a074604bb240aca2930d9325ac31f60d8bc..00f53216570810c84504625b4c1f2abdf2896f2d 100644 --- a/src/document/views.py +++ b/src/document/views.py @@ -1,11 +1,38 @@ from rest_framework import viewsets - from common import permissions from . import models from . import serializers +from rest_framework.parsers import JSONParser, MultiPartParser class DocumentViewSet(viewsets.ModelViewSet): - queryset = models.Document.objects.all() serializer_class = serializers.DocumentSerializer - permission_classes = (permissions.IsStaffOrReadOnly, ) + permission_classes = (permissions.IsStaffOrStudent, ) + parser_classes = (JSONParser, MultiPartParser) + + def get_queryset(self): + user = self.request.user + if user.profile.role == 'Staff': + queryset = self.staff_queryset() + else: + queryset = self.student_queryset(user.profile) + return queryset + + def staff_queryset(self): + queryset = models.Document.objects.all() + profile_id = self.request.query_params.get('profileID', None) + solution_id = self.request.query_params.get('solutionID', None) + if profile_id is not None and solution_id is not None: + return queryset.filter(uploaded_by=profile_id, solution=solution_id) + if profile_id is not None: + return queryset.filter(uploaded_by=profile_id) + if solution_id is not None: + return queryset.filter(solution=solution_id) + return queryset + + def student_queryset(self, profile): + queryset = models.Document.objects.filter(uploaded_by=profile) + solution_id = self.request.query_params.get('solutionID', None) + if solution_id is not None: + return queryset.filter(solution=solution_id) + return queryset diff --git a/src/homework/migrations/0001_initial.py b/src/homework/migrations/0001_initial.py old mode 100755 new mode 100644 index b92796bcb9ea8806d6a15480b2d9ccfe95609d1e..13125342ce3865c45e5e8304a5353c3b5837e371 --- a/src/homework/migrations/0001_initial.py +++ b/src/homework/migrations/0001_initial.py @@ -1,7 +1,6 @@ -# Generated by Django 2.0.1 on 2018-04-05 08:33 +# Generated by Django 2.0.1 on 2019-01-17 15:06 -import common.validators -import django.core.validators +import common.middleware from django.db import migrations, models import django.db.models.deletion @@ -11,7 +10,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('account', '0017_auto_20180205_2004'), + ('account', '0001_initial'), ] operations = [ @@ -21,10 +20,10 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), - ('ready', models.BooleanField(default=False)), - ('accepted', models.BooleanField(default=False)), - ('files', models.FileField(upload_to='', validators=[django.core.validators.FileExtensionValidator(['png', 'jpeg', 'jpg', 'zip']), common.validators.FileSizeValidator(size_limit=52428800)])), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student_solution', to='account.Profile')), + ('note', models.TextField(blank=True, default='')), + ('accepted', models.BooleanField()), + ('corrected', models.BooleanField()), + ('created_by', models.ForeignKey(default=common.middleware.CurrentUserMiddleware.get_current_user_profile, on_delete=django.db.models.deletion.DO_NOTHING, related_name='solution', to='account.Profile')), ], ), migrations.CreateModel( @@ -36,12 +35,12 @@ class Migration(migrations.Migration): ('title', models.CharField(max_length=150)), ('text', models.TextField()), ('deadline', models.DateTimeField()), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='account.Profile')), + ('created_by', models.ForeignKey(default=common.middleware.CurrentUserMiddleware.get_current_user_profile, on_delete=django.db.models.deletion.DO_NOTHING, related_name='tasks', to='account.Profile')), ], ), migrations.AddField( model_name='solution', name='task', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='task_solution', to='homework.Task'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='solutions', to='homework.Task'), ), ] diff --git a/src/homework/migrations/0002_auto_20180411_0047.py b/src/homework/migrations/0002_auto_20190121_2124.py similarity index 60% rename from src/homework/migrations/0002_auto_20180411_0047.py rename to src/homework/migrations/0002_auto_20190121_2124.py index cafe25c08698315b551c3d0d0eaa5430eb0ef5d5..3abf5a83e87d8424a882e3c92f55af2f1478d3ee 100644 --- a/src/homework/migrations/0002_auto_20180411_0047.py +++ b/src/homework/migrations/0002_auto_20190121_2124.py @@ -1,4 +1,4 @@ -# Generated by Django 2.0.1 on 2018-04-10 22:47 +# Generated by Django 2.0.1 on 2019-01-21 20:24 from django.db import migrations, models @@ -10,13 +10,14 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RemoveField( + migrations.AlterField( model_name='solution', - name='ready', + name='accepted', + field=models.BooleanField(default=False), ), migrations.AlterField( model_name='solution', - name='accepted', - field=models.BooleanField(), + name='corrected', + field=models.BooleanField(default=False), ), ] diff --git a/src/homework/models.py b/src/homework/models.py index a62333df3d2905702b87c01d835bf099e47b49fd..4ecb6e958c9a9349ca39a26d75c57463f030c1fc 100755 --- a/src/homework/models.py +++ b/src/homework/models.py @@ -1,40 +1,38 @@ from django.db import models -from django.core import validators - -from common.validators import FileSizeValidator +from common.middleware import CurrentUserMiddleware from account.models import Profile class Task(models.Model): - created_by = models.ForeignKey(Profile, on_delete=models.DO_NOTHING) + created_by = models.ForeignKey( + Profile, + on_delete=models.DO_NOTHING, + related_name='tasks', + default=CurrentUserMiddleware.get_current_user_profile, + ) created_at = models.DateTimeField(auto_now_add=True, editable=False) updated_at = models.DateTimeField(auto_now=True, editable=False) title = models.CharField(max_length=150) text = models.TextField() deadline = models.DateTimeField() - # dokumentum kezeles def __str__(self): return self.title class Solution(models.Model): - task = models.ForeignKey(Task, related_name='task_solution', on_delete=models.CASCADE) - created_by = models.ForeignKey(Profile, related_name='student_solution', on_delete=models.CASCADE) + task = models.ForeignKey(Task, related_name='solutions', on_delete=models.CASCADE) + created_by = models.ForeignKey( + Profile, + related_name='solution', + on_delete=models.DO_NOTHING, + default=CurrentUserMiddleware.get_current_user_profile + ) created_at = models.DateTimeField(auto_now_add=True, editable=False) updated_at = models.DateTimeField(auto_now=True, editable=False) - accepted = models.BooleanField() - files = models.FileField( - validators=[ - validators.FileExtensionValidator([ - 'png', - 'jpeg', - 'jpg', - 'zip', - ]), - FileSizeValidator(size_limit=52428800), # 52428800 - 50MiB - ], - ) + note = models.TextField(blank=True, default='') + accepted = models.BooleanField(blank=True, default=False) + corrected = models.BooleanField(blank=True, default=False) def __str__(self): return "[{}] {}".format(self.created_at, self.created_by.full_name) diff --git a/src/homework/serializers.py b/src/homework/serializers.py index f7d7c81b2d31467a445b1d9a547808b85c9bc6f4..c6e4fde20ad2289c20456d7e5a7909bc47c87f2b 100755 --- a/src/homework/serializers.py +++ b/src/homework/serializers.py @@ -1,13 +1,12 @@ from rest_framework import serializers from django.utils import timezone - -from common.serializers import CurrentUserProfileDefault +from account.models import Profile from . import models +from common import email +from common.middleware import CurrentUserMiddleware class TaskSerializer(serializers.ModelSerializer): - created_by = serializers.HiddenField(default=CurrentUserProfileDefault()) - class Meta: model = models.Task read_only_fields = ('created_by', 'created_at', 'updated_at') @@ -18,22 +17,63 @@ class TaskSerializer(serializers.ModelSerializer): raise serializers.ValidationError('Please, enter appropriate deadline.') return data + def create(self, validated_data): + profiles = Profile.objects.filter(role="Student").exclude(user__email='') + for profile in profiles: + email.new_homework(profile.user, validated_data.get('deadline')) + return self.Meta.model.objects.create(**validated_data) -class SolutionSerializer(serializers.ModelSerializer): - created_at = serializers.DateTimeField(read_only=True) - updated_at = serializers.DateTimeField(read_only=True) - created_by = serializers.HiddenField(default=CurrentUserProfileDefault()) +class SolutionSerializer(serializers.ModelSerializer): class Meta: model = models.Solution - read_only_fields = ('created_by', 'created_at', 'updated_at', 'ready') - fields = ('task', 'created_at', 'updated_at', 'accepted', 'files', 'created_by') + read_only_fields = ('created_by', 'created_at', 'updated_at', 'ready', 'files') + fields = ( + 'id', + 'task', + 'created_at', + 'updated_at', + 'accepted', + 'files', + 'created_by', + 'corrected', + 'note', + ) - def validate(self, data): - if timezone.now() > data['task'].deadline: + def validate_task(self, value): + if timezone.now() > value.deadline: raise serializers.ValidationError('You late.') - return data + return value + + def validate_accepted(self, value): + profile = CurrentUserMiddleware.get_current_user_profile() + if profile.role != 'Staff' and value: + raise serializers.ValidationError("You don't have permission to modify accepted!") + return value + + def validate_corrected(self, value): + profile = CurrentUserMiddleware.get_current_user_profile() + if profile.role != 'Staff' and value: + raise serializers.ValidationError("You don't have permission to modify corrected!") + return value + + def validate_note(self, value): + profile = CurrentUserMiddleware.get_current_user_profile() + if profile.role != 'Staff' and value != '': + raise serializers.ValidationError("You don't have permission to create note!") + return value + + def update(self, instance, validated_data): + if instance.corrected == False and validated_data.get('corrected', instance.corrected) == True: + email.homework_corrected( + instance.created_by.user, + instance.task.title, + validated_data.get('accepted', instance.accepted) + ) + return super().update(instance, validated_data) def create(self, validated_data): - validated_data['accepted'] = False - return self.Meta.model.objects.create(**validated_data) + profile = CurrentUserMiddleware.get_current_user_profile() + models.Solution.objects.filter(created_by=profile, task=validated_data['task']).delete() + return super().create(validated_data) + diff --git a/src/homework/views.py b/src/homework/views.py index 41f1638a5484fe74cf9f471ea353da89cb9ee7c8..9a9d88c415e5b6182a6707bdcfb32db355f7d05a 100755 --- a/src/homework/views.py +++ b/src/homework/views.py @@ -1,27 +1,26 @@ from rest_framework import viewsets -from common import permissions -from rest_framework.permissions import IsAuthenticated from . import serializers from . import models +from common import permissions class TasksViewSet(viewsets.ModelViewSet): serializer_class = serializers.TaskSerializer queryset = models.Task.objects.all() - permission_classes = (permissions.IsStaffOrReadOnlyForAuthenticated, ) + permission_classes = (permissions.IsStaffOrReadOnlyForAuthenticated, permissions.IsStaffOrStudent, ) class SolutionsViewSet(viewsets.ModelViewSet): serializer_class = serializers.SolutionSerializer - permission_classes = (IsAuthenticated, ) + permission_classes = (permissions.IsStaffOrStudent, ) def get_queryset(self): user = self.request.user - queryset = models.Solution.objects.filter(created_by=user) - if user.has_perm(permissions.IsStaffUser): + queryset = models.Solution.objects.filter(created_by=user.profile) + if user.profile.role == 'Staff': queryset = models.Solution.objects.all() - user_id = self.request.query_params.get('userID', None) - if user_id is not None: - queryset = queryset.filter(created_by=user_id) + profile_id = self.request.query_params.get('profileID', None) + if profile_id is not None: + queryset = queryset.filter(created_by=profile_id) return queryset diff --git a/src/kszkepzes/settings/base.py b/src/kszkepzes/settings/base.py index dd73603a60b94e23ef0f7892f885ff713c32de67..b831a0ec690d3c2ff85248e79972ab90f1b260f2 100644 --- a/src/kszkepzes/settings/base.py +++ b/src/kszkepzes/settings/base.py @@ -58,6 +58,7 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'common.middleware.CurrentUserMiddleware', ] ROOT_URLCONF = 'kszkepzes.urls' diff --git a/src/kszkepzes/settings/local.py b/src/kszkepzes/settings/local.py index 9b5ed21c9e3f65438f966db7f1913bc34b7ffa97..53eb49a1bd60f3f66631867f420381d3070b85a1 100644 --- a/src/kszkepzes/settings/local.py +++ b/src/kszkepzes/settings/local.py @@ -1 +1,9 @@ from .base import * + +SESSION_COOKIE_SECURE=False +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = 'smtp.gmail.com' +EMAIL_USE_TLS = True +EMAIL_PORT = 587 +EMAIL_HOST_USER = os.getenv('EMAIL') +EMAIL_HOST_PASSWORD = os.getenv('EMAIL_PASSWORD') diff --git a/src/kszkepzes/settings/production.py b/src/kszkepzes/settings/production.py index ebf105027c4fcc9bd936482fd349dc1d7f526323..cbd865978fa81069cdf9c99a29f8360375bb6fd2 100644 --- a/src/kszkepzes/settings/production.py +++ b/src/kszkepzes/settings/production.py @@ -15,7 +15,6 @@ DATABASES = { } } - REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', @@ -26,3 +25,6 @@ REST_FRAMEWORK = { } STATIC_ROOT = os.path.join(BASE_DIR, 'static_collected') +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = 'mail.sch.bme.hu' +EMAIL_PORT = 25 diff --git a/src/news/migrations/0001_initial.py b/src/news/migrations/0001_initial.py index 3e3bf0cd6b30767f0b17a436e2c1eede7b9061a7..a8f40ce6d982860cc36fa0d335132bf70b770d9e 100644 --- a/src/news/migrations/0001_initial.py +++ b/src/news/migrations/0001_initial.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-28 19:55 -from __future__ import unicode_literals +# Generated by Django 2.0.1 on 2019-01-17 15:06 +import common.middleware from django.db import migrations, models import django.db.models.deletion @@ -11,7 +10,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('account', '0002_auto_20171114_2144'), + ('account', '0001_initial'), ] operations = [ @@ -21,8 +20,10 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=200)), ('text', models.TextField()), - ('date', models.DateTimeField()), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='author', to='account.Profile')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('author', models.ForeignKey(default=common.middleware.CurrentUserMiddleware.get_current_user_profile, on_delete=django.db.models.deletion.DO_NOTHING, related_name='author', to='account.Profile')), + ('updated_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='updater', to='account.Profile')), ], ), ] diff --git a/src/news/migrations/0002_auto_20171220_1852.py b/src/news/migrations/0002_auto_20171220_1852.py deleted file mode 100644 index 82274a5e164ffb466661b8358d3839ee44f9f78a..0000000000000000000000000000000000000000 --- a/src/news/migrations/0002_auto_20171220_1852.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-12-20 17:52 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('news', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='article', - name='date', - field=models.DateTimeField(auto_now_add=True), - ), - ] diff --git a/src/news/migrations/0003_auto_20180126_0135.py b/src/news/migrations/0003_auto_20180126_0135.py deleted file mode 100644 index 744aef9118bc2963df22d62ea5af9c8230adf26c..0000000000000000000000000000000000000000 --- a/src/news/migrations/0003_auto_20180126_0135.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 2.0.1 on 2018-01-26 00:35 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('news', '0002_auto_20171220_1852'), - ] - - operations = [ - migrations.RenameField( - model_name='article', - old_name='date', - new_name='created_at', - ), - migrations.AddField( - model_name='article', - name='updated_at', - field=models.DateTimeField(auto_now=True), - ), - migrations.AlterField( - model_name='article', - name='author', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='author', to='account.Profile'), - ), - ] diff --git a/src/news/models.py b/src/news/models.py index 0714bf16b3c1e24f9d9da0b476e5f8bdb04f428d..c216221410bc8e9f04f3b76c90265ed94191b9d6 100644 --- a/src/news/models.py +++ b/src/news/models.py @@ -1,13 +1,24 @@ from django.db import models from account.models import Profile +from common.middleware import CurrentUserMiddleware class Article(models.Model): - author = models.ForeignKey(Profile, related_name="author", on_delete=models.DO_NOTHING) + author = models.ForeignKey( + Profile, + related_name="author", + on_delete=models.DO_NOTHING, + default=CurrentUserMiddleware.get_current_user_profile + ) title = models.CharField(null=False, max_length=200) text = models.TextField() created_at = models.DateTimeField(auto_now_add=True, editable=False) updated_at = models.DateTimeField(auto_now=True, editable=False) + updated_by = models.ForeignKey( + Profile, + related_name="updater", + on_delete=models.DO_NOTHING + ) def __str__(self): return self.title diff --git a/src/news/serializers.py b/src/news/serializers.py index a4123d8f897dbc3dbf9689998d6c61c065bb1fae..514c63bd416184b25ca57672ed24a276c72a382d 100644 --- a/src/news/serializers.py +++ b/src/news/serializers.py @@ -1,13 +1,20 @@ from news.models import Article from rest_framework import serializers +from common.serializers import CurrentUserProfileDefault -class ArticleListSerializer(serializers.ModelSerializer): - author_name = serializers.SerializerMethodField() +class ArticleSerializer(serializers.ModelSerializer): + updated_by = serializers.HiddenField(default=CurrentUserProfileDefault()) + last_update_by = serializers.SerializerMethodField() + author = serializers.SerializerMethodField() class Meta: model = Article - fields = serializers.ALL_FIELDS + read_only_fields = ('author', 'created_at', 'updated_at', 'updated_by') + fields = '__all__' - def get_author_name(self, obj): - return obj.author.user.get_full_name() + def get_last_update_by(self, obj): + return obj.updated_by.full_name + + def get_author(self, obj): + return obj.author.full_name diff --git a/src/news/urls.py b/src/news/urls.py index d71ed82e9e76d7ee61aa6e91c0f8f868a28b1280..9536ff9f010db9f900d00e84c7d18555dafde6c0 100644 --- a/src/news/urls.py +++ b/src/news/urls.py @@ -3,6 +3,6 @@ from news import views router = routers.DefaultRouter() -router.register(r'news', views.NewsViewSet) +router.register(r'news', views.NewsViewSet, base_name='news') urlpatterns = router.urls diff --git a/src/news/views.py b/src/news/views.py index 7267fd827e731895a3ab25002b6f1fb5b9c1a581..764d0a93c4aeb7f6ff4a493c6db4aed6f24a6c5c 100644 --- a/src/news/views.py +++ b/src/news/views.py @@ -1,10 +1,10 @@ from common.permissions import IsStaffOrReadOnly from rest_framework import viewsets from news.models import Article -from news.serializers import ArticleListSerializer +from news.serializers import ArticleSerializer class NewsViewSet(viewsets.ModelViewSet): - serializer_class = ArticleListSerializer - permission_classes = [IsStaffOrReadOnly] + serializer_class = ArticleSerializer + permission_classes = (IsStaffOrReadOnly,) queryset = Article.objects.all().order_by('-created_at') diff --git a/src/stats/admin.py b/src/stats/admin.py index 050ebd1b03b37e58ab3bec15a2624270a5e37082..fb841605335f9cd12025ed91abe5361dc7f806f5 100644 --- a/src/stats/admin.py +++ b/src/stats/admin.py @@ -15,7 +15,7 @@ class EventAdmin(ExportMixin, admin.ModelAdmin): @admin.register(models.Note) class NoteAdmin(ExportMixin, admin.ModelAdmin): - list_display = ('user', 'note', 'event', 'created_by', 'created_at', 'updated_at') - list_filter = ('user', 'created_by', 'event') + list_display = ('profile', 'note', 'event', 'created_by', 'created_at', 'updated_at') + list_filter = ('profile', 'created_by', 'event') search_fields = ('event__name', 'note') resource_class = resources.NoteResource diff --git a/src/stats/migrations/0001_initial.py b/src/stats/migrations/0001_initial.py index 91e32fff21f7f6489284d3c0ab02d8b58949f049..b6e989e8f6f784a782bf6ef238584160ca141329 100644 --- a/src/stats/migrations/0001_initial.py +++ b/src/stats/migrations/0001_initial.py @@ -1,8 +1,8 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-11 14:14 -from __future__ import unicode_literals +# Generated by Django 2.0.1 on 2019-01-17 15:06 +import common.middleware from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -15,12 +15,27 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='KszkEvent', + name='Event', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date', models.DateField()), - ('num_of_pers', models.IntegerField()), - ('visitors', models.ManyToManyField(related_name='visitor', to='account.Profile')), + ('name', models.CharField(max_length=255)), + ('date', models.DateTimeField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('created_by', models.ForeignKey(default=common.middleware.CurrentUserMiddleware.get_current_user_profile, on_delete=django.db.models.deletion.DO_NOTHING, related_name='created_event', to='account.Profile')), + ('visitors', models.ManyToManyField(blank=True, related_name='events', to='account.Profile')), + ], + ), + migrations.CreateModel( + name='Note', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('note', models.TextField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('created_by', models.ForeignKey(default=common.middleware.CurrentUserMiddleware.get_current_user_profile, on_delete=django.db.models.deletion.DO_NOTHING, related_name='created_notes', to='account.Profile')), + ('event', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='stats.Event')), + ('profile', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='account.Profile')), ], ), ] diff --git a/src/stats/migrations/0002_auto_20171111_1646.py b/src/stats/migrations/0002_auto_20171111_1646.py deleted file mode 100644 index 7a51b6e003a0e51bf2130254034f7a8bfda83b8b..0000000000000000000000000000000000000000 --- a/src/stats/migrations/0002_auto_20171111_1646.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-11 15:46 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stats', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='kszkevent', - name='num_of_pers', - field=models.IntegerField(editable=False), - ), - ] diff --git a/src/stats/migrations/0002_event_description.py b/src/stats/migrations/0002_event_description.py new file mode 100644 index 0000000000000000000000000000000000000000..d7d056636e1bc6c5ad18457d09a92bc56ba30927 --- /dev/null +++ b/src/stats/migrations/0002_event_description.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.1 on 2019-01-19 14:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('stats', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='description', + field=models.TextField(blank=True, default=''), + ), + ] diff --git a/src/stats/migrations/0003_auto_20171114_2026.py b/src/stats/migrations/0003_auto_20171114_2026.py deleted file mode 100644 index 4f61427074f3da7f8f404795ce58528a1be00bb6..0000000000000000000000000000000000000000 --- a/src/stats/migrations/0003_auto_20171114_2026.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-14 19:26 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stats', '0002_auto_20171111_1646'), - ] - - operations = [ - migrations.AlterField( - model_name='kszkevent', - name='date', - field=models.DateTimeField(), - ), - ] diff --git a/src/stats/migrations/0003_auto_20190122_1449.py b/src/stats/migrations/0003_auto_20190122_1449.py new file mode 100644 index 0000000000000000000000000000000000000000..a3689e8ee847704324f3821df0508d6b89ffbb0d --- /dev/null +++ b/src/stats/migrations/0003_auto_20190122_1449.py @@ -0,0 +1,24 @@ +# Generated by Django 2.0.1 on 2019-01-22 13:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0002_auto_20190122_1341'), + ('stats', '0002_event_description'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='absent', + field=models.ManyToManyField(blank=True, related_name='events_absent', to='account.Profile'), + ), + migrations.AlterField( + model_name='event', + name='visitors', + field=models.ManyToManyField(blank=True, related_name='events_visitor', to='account.Profile'), + ), + ] diff --git a/src/stats/migrations/0004_remove_kszkevent_num_of_pers.py b/src/stats/migrations/0004_remove_kszkevent_num_of_pers.py deleted file mode 100644 index 06bf71b435fa15cddf94d3352678cba5aa44784a..0000000000000000000000000000000000000000 --- a/src/stats/migrations/0004_remove_kszkevent_num_of_pers.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.5 on 2017-11-14 19:38 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stats', '0003_auto_20171114_2026'), - ] - - operations = [ - migrations.RemoveField( - model_name='kszkevent', - name='num_of_pers', - ), - ] diff --git a/src/stats/migrations/0005_auto_20180214_2206.py b/src/stats/migrations/0005_auto_20180214_2206.py deleted file mode 100644 index f4a0033cc39e3b8b311676b83ca7fdb22fa3bccf..0000000000000000000000000000000000000000 --- a/src/stats/migrations/0005_auto_20180214_2206.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 2.0.1 on 2018-02-14 21:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0017_auto_20180205_2004'), - ('stats', '0004_remove_kszkevent_num_of_pers'), - ] - - operations = [ - migrations.CreateModel( - name='Event', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('date', models.DateTimeField()), - ('visitors', models.ManyToManyField(related_name='visitor', to='account.Profile')), - ], - ), - migrations.RemoveField( - model_name='kszkevent', - name='visitors', - ), - migrations.DeleteModel( - name='KszkEvent', - ), - ] diff --git a/src/stats/migrations/0006_auto_20180214_2239.py b/src/stats/migrations/0006_auto_20180214_2239.py deleted file mode 100644 index 11bcd479e58a3a965982f251caa3b021c98273f2..0000000000000000000000000000000000000000 --- a/src/stats/migrations/0006_auto_20180214_2239.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.0.1 on 2018-02-14 21:39 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stats', '0005_auto_20180214_2206'), - ] - - operations = [ - migrations.AlterField( - model_name='event', - name='visitors', - field=models.ManyToManyField(related_name='visitor', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/src/stats/migrations/0007_auto_20180215_0018.py b/src/stats/migrations/0007_auto_20180215_0018.py deleted file mode 100644 index 9cd65ce00b95c767f3bf9db33128446d83b79ad2..0000000000000000000000000000000000000000 --- a/src/stats/migrations/0007_auto_20180215_0018.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.0.1 on 2018-02-14 23:18 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stats', '0006_auto_20180214_2239'), - ] - - operations = [ - migrations.AlterField( - model_name='event', - name='visitors', - field=models.ManyToManyField(related_name='visitor', to='account.Profile'), - ), - ] diff --git a/src/stats/migrations/0008_note.py b/src/stats/migrations/0008_note.py deleted file mode 100644 index 8b08926a7916ef37a514309d69c0f9b3ac8779c0..0000000000000000000000000000000000000000 --- a/src/stats/migrations/0008_note.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 2.0.1 on 2018-02-21 00:18 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0017_auto_20180205_2004'), - ('stats', '0007_auto_20180215_0018'), - ] - - operations = [ - migrations.CreateModel( - name='Note', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('note', models.TextField()), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_notes', to='account.Profile')), - ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='stats.Event')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='account.Profile')), - ], - ), - ] diff --git a/src/stats/models.py b/src/stats/models.py index aa38fd947c9ec32fd41c407f52387c1c683779b7..be958204fba61174220ddc988100c21405f44241 100644 --- a/src/stats/models.py +++ b/src/stats/models.py @@ -1,28 +1,45 @@ from django.db import models from account.models import Profile -from django.utils import timezone -from django.core.exceptions import ValidationError +from common.middleware import CurrentUserMiddleware class Event(models.Model): name = models.CharField(max_length=255) date = models.DateTimeField(null=False) - visitors = models.ManyToManyField(Profile, related_name='visitor') - - def clean(self): - if self.date > timezone.now(): - raise ValidationError('Invalid date') + description = models.TextField(blank=True, default='') + visitors = models.ManyToManyField( + Profile, + related_name='events_visitor', + blank=True + ) + absent = models.ManyToManyField( + Profile, + related_name='events_absent', + blank=True + ) + created_by = models.ForeignKey( + Profile, + related_name='created_event', + on_delete=models.DO_NOTHING, + default=CurrentUserMiddleware.get_current_user_profile + ) + created_at = models.DateTimeField(auto_now_add=True, editable=False) + updated_at = models.DateTimeField(auto_now=True, editable=False) def __str__(self): return self.name class Note(models.Model): - event = models.ForeignKey(Event, related_name='notes', on_delete=models.CASCADE) - user = models.ForeignKey(Profile, related_name='notes', on_delete=models.CASCADE) + event = models.ForeignKey(Event, related_name='notes', on_delete=models.CASCADE, blank=True, null=True) + profile = models.ForeignKey(Profile, related_name='notes', on_delete=models.CASCADE, blank=True, null=True) note = models.TextField() - - created_by = models.ForeignKey(Profile, related_name='created_notes', on_delete=models.CASCADE) + created_by = models.ForeignKey( + Profile, + related_name='created_notes', + on_delete=models.DO_NOTHING, + default=CurrentUserMiddleware.get_current_user_profile + ) created_at = models.DateTimeField(auto_now_add=True, editable=False) updated_at = models.DateTimeField(auto_now=True, editable=False) diff --git a/src/stats/serializers.py b/src/stats/serializers.py index 0620c993155de89cea2f2c9e463d00737248b803..e09d7b1b86fe537ddee2106ec72753d626696380 100644 --- a/src/stats/serializers.py +++ b/src/stats/serializers.py @@ -1,8 +1,51 @@ from rest_framework import serializers +from common.serializers import CurrentUserProfileDefault from . import models -class EventSerializer(serializers.ModelSerializer): +class StaffEventSerializer(serializers.ModelSerializer): + created_by_name = serializers.SerializerMethodField() + visitor_number = serializers.SerializerMethodField() + + class Meta: + model = models.Event + fields = '__all__' + read_only_fields = ('created_at', 'update_at', 'created_by') + + def get_created_by_name(self, obj): + return obj.created_by.full_name + + def get_visitor_number(self, obj): + return obj.visitors.all().count() + + def validate(self, data): + if 'absent' in data and 'visitors' in data: + for i in data['absent']: + if i in data['visitors']: + raise serializers.ValidationError('You cant add a student to absent and visitor in the same time.') + return data + + +class StudentEventSerializer(serializers.ModelSerializer): class Meta: model = models.Event - fields = ('name', 'date', 'visitors') + fields = ('id', 'name', 'date', 'description', ) + read_only_fields = ('id', 'name', 'date', 'description', ) + + +class NoteSerializer(serializers.ModelSerializer): + created_by = serializers.HiddenField(default=CurrentUserProfileDefault()) + created_by_name = serializers.SerializerMethodField() + + class Meta: + model = models.Note + fields = '__all__' + read_only_fields = ('created_at', 'update_at', 'created_by') + + def get_created_by_name(self, obj): + return obj.created_by.full_name + + def validate(self, data): + if data['profile'] is None and data['event'] is None: + raise serializers.ValidationError('You have to add profile or event') + return data diff --git a/src/stats/urls.py b/src/stats/urls.py index 8f270ce8246724e9a29aaf99dd3d1e7d6690aec7..0e669f8037c98287185212ecbe0c2b43c03bd267 100644 --- a/src/stats/urls.py +++ b/src/stats/urls.py @@ -1,8 +1,9 @@ from rest_framework import routers - from . import views router = routers.DefaultRouter() -router.register(r'events', views.EventViewSet) +router.register(r'staff_events', views.StaffEventViewSet, base_name='staff_events') +router.register(r'student_events', views.StudentEventViewSet, base_name='student_events') +router.register(r'notes', views.NoteViewSet, base_name='notes') urlpatterns = router.urls diff --git a/src/stats/views.py b/src/stats/views.py index f2c91f1e70a8b38d620a0a500709f38f97359d9c..69ab78cb832c266e1e04a726ed6158f55455a6b3 100644 --- a/src/stats/views.py +++ b/src/stats/views.py @@ -1,9 +1,34 @@ from rest_framework import viewsets - from . import models from . import serializers +from common.permissions import IsStaffUser, IsStaffOrStudent + + +class StaffEventViewSet(viewsets.ModelViewSet): + serializer_class = serializers.StaffEventSerializer + queryset = models.Event.objects.all().order_by('date') + permission_classes = (IsStaffUser, ) + + +class StudentEventViewSet(viewsets.ModelViewSet): + serializer_class = serializers.StudentEventSerializer + queryset = models.Event.objects.all().order_by('date') + permission_classes = (IsStaffOrStudent, ) + + +class NoteViewSet(viewsets.ModelViewSet): + serializer_class = serializers.NoteSerializer + permission_classes = (IsStaffUser, ) + def get_queryset(self): + queryset = models.Note.objects.all() + profile_id = self.request.query_params.get('profileID', None) + event_id = self.request.query_params.get('eventID', None) + if profile_id is not None and event_id is not None: + return queryset.filter(user=profile_id, event=event_id) + if profile_id is not None: + return queryset.filter(user=profile_id) + if event_id is not None: + return queryset.filter(event=event_id) + return queryset -class EventViewSet(viewsets.ModelViewSet): - serializer_class = serializers.EventSerializer - queryset = models.Event.objects.all()