diff --git a/.gitignore b/.gitignore
index ffb3de3832416b870bf800ac19d59da9db49bf56..1d40e0537e5f4894bc7867676e6bfd9227d0c563 100644
--- a/.gitignore
+++ b/.gitignore
@@ -110,4 +110,10 @@ environment.ps1
 environment.bat
 
 #PyCharm
-.idea/
\ No newline at end of file
+.idea/
+
+#VSCode
+.vscode/
+
+#media
+/media/*
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 19b1c23cf3699519e16058625d22789b55afeb6f..027b673d3aa9b66ab968a2c1feefa874519443b7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,7 +25,7 @@ before_script:
   - python -V                                   # Print out python version for debugging
   # Uncomment next line if your Django app needs a JS runtime:
   # - apt-get update -q && apt-get install nodejs -yqq
-  - pip install -r requirements/development.txt
+  - pip install -r requirements/production.txt
 # To get Django tests to work you may need to create a settings file using
 # the following DATABASES:
 #
diff --git a/.travis.yml b/.travis.yml
index af5ebcc2a6397f7ada705a69cd12a91e2f4267a6..75c6e5b0c8f2ea94269adadf1c8c8da18cf74673 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,18 +1,18 @@
 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
+  - "3.7"
+  - "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:
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..0dd2d90d20d02e6e18f3773fa43b4feeea582145
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,27 @@
+# pull official base image
+FROM python:3.8.1
+
+# set work directory
+WORKDIR /usr/src/app
+
+# set environment variables
+ENV PYTHONDONTWRITEBYTECODE 1
+ENV PYTHONUNBUFFERED 1
+
+# install dependencies
+RUN apt-get -y update
+RUN apt-get install -y python python-pip python-dev python-django-extensions postgresql-client netcat
+RUN pip install --upgrade pip
+COPY ./requirements/production.txt /usr/src/app/requirements.txt
+RUN pip install -r requirements.txt
+
+RUN apt-get -y update && apt-get -y autoremove
+
+# copy entrypoint.sh
+COPY ./src/entrypoint.sh /usr/src/app/entrypoint.sh
+
+# copy project
+COPY ./src /usr/src/app/
+
+# run entrypoint.sh
+ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
\ No newline at end of file
diff --git a/Dockerfile.prod b/Dockerfile.prod
new file mode 100644
index 0000000000000000000000000000000000000000..030717cdd793a0a7367cade822888bf852029aff
--- /dev/null
+++ b/Dockerfile.prod
@@ -0,0 +1,27 @@
+FROM python:3.8.1
+
+ENV PYTHONDONTWRITEBYTECODE 1
+ENV PYTHONUNBUFFERED 1
+ENV TZ Europe/Budapest
+
+ENV HOME /home/app
+ENV APP_HOME /home/app/kszkepzes-backend
+RUN groupadd app && useradd -g app app && \
+mkdir -p /home/app && mkdir $APP_HOME && \
+mkdir $APP_HOME/staticfiles && mkdir $APP_HOME/mediafiles
+WORKDIR $APP_HOME
+
+COPY ./requirements/production.txt requirements.txt
+
+RUN apt-get -y update && apt-get install -y python python-pip python-dev python-django-extensions postgresql-client netcat
+RUN pip install -r requirements.txt
+
+COPY ./src $APP_HOME
+
+RUN chown -R app:app $APP_HOME
+
+USER app
+
+EXPOSE 8000
+
+CMD ["gunicorn", "kszkepzes.wsgi:application", "--bind", "0.0.0.0:8000"]
diff --git a/README.md b/README.md
index 3084bb5c17ad754726f57ea7e286e5ae7a98e235..b3d8e230e67c037c729c2f6d1d5d824b8247d4aa 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,15 @@
 1. python3 -m venv venv
 2. source venv/bin/activate
 3. cp environment.sh.example environment.sh
-4. Ki kell tölteni a környezeti változókat.
+4. Ki kell tölteni a környezeti változókat.  
+Az authsch-s adatokat az https://auth.sch.bme.hu/ fejlesztői konzol menüpontja alatt lehet legenerálni új kliens hozzáadásával.
+Atirányítási cím: `http://127.0.0.1:3000/api/v1/complete/authsch`
+```shell script
+   export SECRET_KEY=<Ide bármi kerülhet>
+   export AUTHSCH_KEY=<authsch-s Kliens azonosító>
+   export AUTHSCH_SECRET=<authsch-s Kliens kulcs>
+   export MEDIA_ROOT=static/
+  ```
 5. source environment.sh
 6. pip install -r requirements/development.txt
 7. python3 src/manage.py runserver
diff --git a/requirements/base.in b/requirements/base.in
index 84ce1c98213e06963da8ab3b722b9c5c39f83d2a..e319619d85d0f23b7fee16d86e0c3da1a0f0d4c1 100644
--- a/requirements/base.in
+++ b/requirements/base.in
@@ -1,5 +1,5 @@
-Django==2.0.1
-djangorestframework==3.7.7
+Django==2.2.4
+djangorestframework==3.10.2
 django-social-authsch==0.1
 django-solo==1.1.3
-django-import-export==1.0.0
+django-import-export==1.2.0
diff --git a/requirements/development.in b/requirements/development.in
deleted file mode 100644
index d80fc6250c3504682e90f62fdf9abfed415f824c..0000000000000000000000000000000000000000
--- a/requirements/development.in
+++ /dev/null
@@ -1,5 +0,0 @@
--r base.in
-flake8==3.4.1
-pip-tools==1.9.0
-django-extensions==1.9.9
-python-language-server==0.13.0
diff --git a/requirements/development.txt b/requirements/development.txt
deleted file mode 100644
index 91118f565509bfebb4e633fd2f19702300e03c0c..0000000000000000000000000000000000000000
--- a/requirements/development.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# This file is autogenerated by pip-compile
-# To update, run:
-#
-#    pip-compile --output-file development.txt development.in
-#
-certifi==2017.7.27.1      # via requests
-chardet==3.0.4            # via requests
-click==6.7                # via pip-tools
-configparser==3.5.0       # via python-language-server
-defusedxml==0.5.0         # via python3-openid, social-auth-core
-diff-match-patch==20121119  # via django-import-export
-django-extensions==1.9.9
-django-import-export==1.0.0
-django-social-authsch==0.1
-django-solo==1.1.3
-django>=2.0.10
-djangorestframework==3.7.7
-et-xmlfile==1.0.1         # via openpyxl
-first==2.0.1              # via pip-tools
-flake8==3.4.1
-future==0.16.0            # via python-language-server
-idna==2.6                 # via requests
-jdcal==1.3                # via openpyxl
-jedi==0.11.1              # via python-language-server
-json-rpc==1.10.8          # via python-language-server
-mccabe==0.6.1             # via flake8, python-language-server
-oauthlib==2.0.6           # via requests-oauthlib, social-auth-core
-odfpy==1.3.6              # via tablib
-openpyxl==2.5.0           # via tablib
-parso==0.1.1              # via jedi
-pip-tools==1.9.0
-pluggy==0.6.0             # via python-language-server
-pycodestyle==2.3.1        # via flake8, python-language-server
-pydocstyle==2.1.1         # via python-language-server
-pyflakes==1.5.0           # via flake8, python-language-server
-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>=4.2b1             # via tablib
-requests-oauthlib==0.8.0  # via social-auth-core
-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
-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.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/requirements/production.in b/requirements/production.in
index 00a73eea0fce88f6ffa4c793be6b1966a47d95b8..2ef446c78ccb7cf01c48ff5cee36a04d5f6f6abc 100644
--- a/requirements/production.in
+++ b/requirements/production.in
@@ -1,2 +1,11 @@
 -r base.in
+psycopg2-binary
 gunicorn==19.7.1
+flake8==3.7.8
+pip-tools==4.1.0
+django-extensions==2.2.1
+python-language-server==0.28.2
+drf-yasg==1.16.1
+packaging==19.1
+Pillow==7.0.0
+djangorestframework-api-key==1.4.1
\ No newline at end of file
diff --git a/requirements/production.txt b/requirements/production.txt
index d4403006c713efb94b3f60beeb43b6b2144d0cb3..7cdc80bd945fd1c0c126cfee9d379ae6c4c884f9 100644
--- a/requirements/production.txt
+++ b/requirements/production.txt
@@ -2,35 +2,69 @@
 # This file is autogenerated by pip-compile
 # To update, run:
 #
-#    pip-compile --output-file production.txt production.in
+#    pip-compile --output-file=requirements/production.txt requirements/production.in
 #
+attrs==19.3.0             # via packaging
 certifi==2018.1.18        # via requests
 chardet==3.0.4            # via requests
+click==7.0                # via pip-tools
+coreapi==2.3.3            # via drf-yasg
+coreschema==0.0.4         # via coreapi, drf-yasg
 defusedxml==0.5.0         # via python3-openid, social-auth-core
 diff-match-patch==20121119  # via django-import-export
-django-import-export==1.0.0
+django-extensions==2.2.1
+django-import-export==1.2.0
 django-social-authsch==0.1
 django-solo==1.1.3
-django>=2.0.10
-djangorestframework==3.7.7
+django==2.2.4
+djangorestframework-api-key==1.4.1
+djangorestframework==3.10.2
+drf-yasg==1.16.1
+entrypoints==0.3          # via flake8
 et-xmlfile==1.0.1         # via openpyxl
+flake8==3.7.8
+future==0.18.2            # via python-language-server
 gunicorn==19.7.1
 idna==2.6                 # via requests
+importlib-metadata==1.5.0  # via pluggy
+inflection==0.3.1         # via drf-yasg
+itypes==1.1.0             # via coreapi
 jdcal==1.3                # via openpyxl
+jedi==0.14.1              # via python-language-server
+jinja2==2.11.1            # via coreschema
+markupsafe==1.1.1         # via jinja2
+mccabe==0.6.1             # via flake8
 oauthlib==2.0.6           # via requests-oauthlib, social-auth-core
 odfpy==1.3.6              # via tablib
 openpyxl==2.5.0           # via tablib
+packaging==19.1
+parso==0.6.1              # via jedi
+pillow==7.0.0
+pip-tools==4.1.0
+pluggy==0.13.1            # via python-language-server
+psycopg2-binary==2.8.4
+pycodestyle==2.5.0        # via flake8
+pyflakes==2.1.1           # via flake8
 pyjwt==1.5.3              # via social-auth-core
+pyparsing==2.4.6          # via packaging
+python-jsonrpc-server==0.3.4  # via python-language-server
+python-language-server==0.28.2
 python3-openid==3.1.0     # via social-auth-core
 pytz==2017.3              # via django
-pyyaml>=4.2b1              # via tablib
+pyyaml==5.3               # via tablib
 requests-oauthlib==0.8.0  # via social-auth-core
-requests>=2.20.0         # via requests-oauthlib, social-auth-core
-six==1.11.0               # via social-auth-app-django, social-auth-core
+requests==2.22.0          # via coreapi, requests-oauthlib, social-auth-core
+ruamel.yaml.clib==0.2.0   # via ruamel.yaml
+ruamel.yaml==0.16.7       # via drf-yasg
+six==1.11.0               # via django-extensions, drf-yasg, packaging, pip-tools, social-auth-app-django, social-auth-core
 social-auth-app-django==2.1.0  # via django-social-authsch
 social-auth-core==1.6.0   # via django-social-authsch, social-auth-app-django
+sqlparse==0.3.0           # via django
 tablib==0.12.1            # via django-import-export
+ujson==1.35               # via python-jsonrpc-server
 unicodecsv==0.14.1        # via tablib
-urllib3>=1.23             # via requests
+uritemplate==3.0.1        # via coreapi, drf-yasg
+urllib3==1.25.8           # via requests
 xlrd==1.1.0               # via tablib
 xlwt==1.3.0               # via tablib
+zipp==2.1.0               # via importlib-metadata
diff --git a/src/account/migrations/0001_initial.py b/src/account/migrations/0001_initial.py
index 8550f16222d06151f89e7dbf62fb44cc7233e753..10c1415fab7a219189d6dd4338d89ca2ba0c7d5d 100644
--- a/src/account/migrations/0001_initial.py
+++ b/src/account/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.0.1 on 2019-01-17 15:06
+# Generated by Django 2.2.4 on 2020-02-04 14:16
 
 from django.conf import settings
 from django.db import migrations, models
@@ -19,6 +19,8 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('deadline', models.DateTimeField(null=True)),
+                ('messageBefore', models.TextField(blank=True, default='')),
+                ('messageAfter', models.TextField(blank=True, default='')),
             ],
             options={
                 'abstract': False,
@@ -42,7 +44,7 @@ class Migration(migrations.Migration):
                 ('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)),
+                ('role', models.CharField(choices=[('Staff', 'Staff'), ('Applicant', 'Applicant'), ('Student', 'Student'), ('Denied', 'Denied')], 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_20190122_1341.py b/src/account/migrations/0002_auto_20190122_1341.py
deleted file mode 100644
index e9ee7b662af95a01f09815050e8650ab04603b30..0000000000000000000000000000000000000000
--- a/src/account/migrations/0002_auto_20190122_1341.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# 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/models.py b/src/account/models.py
index 082a83c98de9be5d956af810734422ca9897c1c7..40a38470f6478f44b733c1f7c1b25f606db00141 100644
--- a/src/account/models.py
+++ b/src/account/models.py
@@ -1,6 +1,7 @@
 from django.db import models
 from django.contrib.auth.models import User
 from solo.models import SingletonModel
+from django.db.models import Sum
 
 
 class GroupChoice(models.Model):
@@ -12,7 +13,11 @@ class GroupChoice(models.Model):
         ('HAT', 'Hallgatói Tudásbázis'),
         ('N', 'None'),
     )
-    choice = models.CharField(max_length=10, choices=TEAMS, default='N', unique=True)
+    choice = models.CharField(
+        max_length=10,
+        choices=TEAMS,
+        default='N',
+        unique=True)
 
     def __str__(self):
         return self.choice
@@ -32,15 +37,27 @@ class Profile(models.Model):
         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', blank=True)
+    groups = models.ManyToManyField(
+        GroupChoice, related_name='profiles', blank=True)
     role = models.CharField(max_length=10, choices=ROLES, default='Applicant')
 
+    @property
+    def events_visited(self):
+        return self.events_visitor.all().count()
+
+    @property
+    def homework_bits(self):
+        return self.solution.filter(accepted=True) \
+            .values('task__bits') \
+            .aggregate(total_bits=Sum('task__bits')) \
+            .get('total_bits')
+
     @property
     def full_name(self):
         return self.user.get_full_name()
@@ -51,3 +68,5 @@ class Profile(models.Model):
 
 class Deadline(SingletonModel):
     deadline = models.DateTimeField(null=True)
+    messageBefore = models.TextField(blank=True, default='')
+    messageAfter = models.TextField(blank=True, default='')
diff --git a/src/account/resources.py b/src/account/resources.py
index 4682f638b07cadfff0e0d216c394ce466d980866..1217cad331af7ec6fc291ce0417dc2a020284167 100644
--- a/src/account/resources.py
+++ b/src/account/resources.py
@@ -5,7 +5,8 @@ from . import models
 class SignUpResource(resources.ModelResource):
     groups = fields.Field(
         attribute='groups',
-        widget=widgets.ManyToManyWidget(model=models.GroupChoice, separator=' ,', field='choice'),
+        widget=widgets.ManyToManyWidget(
+            model=models.GroupChoice, separator=' ,', field='choice'),
     )
 
     class Meta:
diff --git a/src/account/serializers.py b/src/account/serializers.py
index e38ad58cb689559fb56cb78a93e94631401e0ecc..895d845a7554129824391e5cc320edc5072dc85c 100644
--- a/src/account/serializers.py
+++ b/src/account/serializers.py
@@ -1,6 +1,5 @@
 from rest_framework import serializers
 from account import models
-from common.middleware import CurrentUserMiddleware
 from common import email
 
 
@@ -10,10 +9,14 @@ class ChoiceSerializer(serializers.ModelSerializer):
         fields = ('choice', 'profile')
 
 
-class ProfileSerializer(serializers.ModelSerializer):
-    groups = serializers.SlugRelatedField(many=True, slug_field='choice', queryset=models.GroupChoice.objects.all())
+class ProfileSerializer_User(serializers.ModelSerializer):
+    groups = serializers.SlugRelatedField(
+        many=True,
+        slug_field='choice',
+        queryset=models.GroupChoice.objects.all())
     updated_at = serializers.DateTimeField(read_only=True)
     full_name = serializers.SerializerMethodField()
+    bits = serializers.SerializerMethodField()
 
     class Meta:
         model = models.Profile
@@ -30,23 +33,25 @@ class ProfileSerializer(serializers.ModelSerializer):
             'motivation_exercise',
             'full_name',
             'role',
+            'bits'
         )
 
     def validate_updated_at(self, value):
         deadline = models.Deadline.get_solo().deadline
         if deadline is not None and value > deadline:
-            raise serializers.ValidationError("You cannot join after the deadline")
+            raise serializers.ValidationError(
+                "You cannot join after the deadline")
         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")
+        modifier_role = self.context['request'].user.profile.role
+        if value != modifier_role:
+            raise serializers.ValidationError(
+                "You don't have permission to change role")
         return value
 
     def validate_signed(self, value):
-        modifier = CurrentUserMiddleware.get_current_user_profile()
-        if value is False and modifier.role != "Staff":
+        if value is False:
             raise serializers.ValidationError("You cannot join without signed")
         return value
 
@@ -61,3 +66,87 @@ class ProfileSerializer(serializers.ModelSerializer):
 
     def get_full_name(self, obj):
         return obj.full_name
+
+    def get_bits(self, obj):
+        if obj.homework_bits is None:
+            return obj.events_visited
+        return obj.homework_bits + obj.events_visited
+
+
+class ProfileSerializer_Staff(serializers.ModelSerializer):
+    groups = serializers.SlugRelatedField(
+        many=True,
+        slug_field='choice',
+        queryset=models.GroupChoice.objects.all())
+    updated_at = serializers.DateTimeField(read_only=True)
+    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',
+            'nick',
+            'signed',
+            'groups',
+            'motivation_about',
+            'motivation_profession',
+            'motivation_exercise',
+            'full_name',
+            'role',
+            'events_visited',
+            'homework_bits'
+        )
+
+    def validate_updated_at(self, value):
+        deadline = models.Deadline.get_solo().deadline
+        if deadline is not None and value > deadline:
+            raise serializers.ValidationError(
+                "You cannot join after the deadline")
+        return value
+
+    def validate_role(self, value):
+        return value
+
+    def validate_signed(self, value):
+        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
+
+
+class MonitoringSerializer(serializers.ModelSerializer):
+    bits = serializers.SerializerMethodField()
+    full_name = serializers.SerializerMethodField()
+
+    class Meta:
+        model = models.Profile
+        read_only_fields = (
+            'id',
+            'bits',
+            'full_name',
+        )
+        fields = (
+            'id',
+            'bits',
+            'full_name',
+        )
+
+    def get_bits(self, obj):
+        if obj.homework_bits is None:
+            return obj.events_visited
+        return obj.homework_bits + obj.events_visited
+
+    def get_full_name(self, obj):
+        return obj.full_name
diff --git a/src/account/urls.py b/src/account/urls.py
index ebf85f3d5bdf9885beb1ad7f28d1bce0e6624575..07e85fddebf48ed4fe6e582f6e2d9f493567af92 100644
--- a/src/account/urls.py
+++ b/src/account/urls.py
@@ -3,5 +3,15 @@ from . import views
 
 router = routers.DefaultRouter()
 
-router.register(r'profiles', views.ProfileViewSet, base_name='profile')
+router.register(
+    r'profiles',
+    views.ProfileViewSet,
+    base_name='profile'
+)
+router.register(
+    r'monitoring/profiles',
+    views.MonitorinViewSet,
+    base_name='monitoring'
+)
+
 urlpatterns = router.urls
diff --git a/src/account/views.py b/src/account/views.py
index c2ad4ec5b61ff4ae7545845c8b1b9149ec42bba5..456f83c27f5d2434794ce769355b3e3e5544b12e 100644
--- a/src/account/views.py
+++ b/src/account/views.py
@@ -1,17 +1,40 @@
 from rest_framework import viewsets
 from rest_framework import permissions
 from rest_framework.response import Response
-from rest_framework.decorators import list_route
+from rest_framework.decorators import action
 from common.permissions import IsSafeOrPatch
+from rest_framework_api_key.permissions import HasAPIKey
 
 from . import models
 from . import serializers
 
 
+class MonitorinViewSet(viewsets.ModelViewSet):
+    serializer_class = serializers.MonitoringSerializer
+    permission_classes = (HasAPIKey,)
+
+    def get_queryset(self):
+        return models.Profile.objects.filter(role='Student')
+
+    @action(detail=False)
+    def totalbits(self, request):
+        profiles = models.Profile.objects.filter(role='Student')
+        bits = map(lambda item: serializers.MonitoringSerializer(item).data['bits'], profiles)
+        return Response({
+            'sum': sum(bits)
+        })
+
+
 class ProfileViewSet(viewsets.ModelViewSet):
-    serializer_class = serializers.ProfileSerializer
+    serializer_class = serializers.ProfileSerializer_User
     permission_classes = (permissions.IsAuthenticated, IsSafeOrPatch)
 
+    def get_serializer_class(self):
+        user = self.request.user
+        if user.profile.role == 'Staff':
+            return serializers.ProfileSerializer_Staff
+        return serializers.ProfileSerializer_User
+
     def get_queryset(self):
         user = self.request.user
         if user.profile.role == 'Staff':
@@ -21,7 +44,16 @@ class ProfileViewSet(viewsets.ModelViewSet):
             return models.Profile.objects.all()
         return models.Profile.objects.filter(pk=user.profile.id)
 
-    @list_route(methods=['get'])
+    @action(detail=False)
     def me(self, request):
         serializer = self.serializer_class(request.user.profile)
         return Response(serializer.data)
+
+    @action(detail=False)
+    def deadline(self, request):
+        deadline = models.Deadline.get_solo()
+        return Response({
+            'deadline': deadline.deadline,
+            'messageBefore': deadline.messageBefore,
+            'messageAfter': deadline.messageAfter
+        })
diff --git a/src/common/email.py b/src/common/email.py
index 45b8705f994cea140f6746831d916630364d0c0e..ffb32b274836aef944ce8fdeead4ced50ecc8c80 100644
--- a/src/common/email.py
+++ b/src/common/email.py
@@ -1,8 +1,8 @@
 from django.core.mail import send_mail
 import codecs
 
-sender_email = 'noreply@ujonc.sch.bme.hu'
-link = 'https://ujonc.sch.bme.hu/homework'
+sender_email = 'noreply@ujonc.kszk.bme.hu'
+link = 'https://ujonc.kszk.bme.hu/homework'
 
 
 def read_email(name):
@@ -16,6 +16,7 @@ def registration(user):
     message = read_email('registration.txt')
     message = str.format(message % {'name': user.get_full_name()})
     send_mail(subject, message, sender_email, [user.email, ])
+    pass
 
 
 def admitted(user):
@@ -23,6 +24,7 @@ def admitted(user):
     message = read_email('admitted.txt')
     message = str.format(message % {'name': user.get_full_name()})
     send_mail(subject, message, sender_email, [user.email, ])
+    pass
 
 
 def denied(user):
@@ -30,14 +32,17 @@ def denied(user):
     message = read_email('denied.txt')
     message = str.format(message % {'name': user.get_full_name()})
     send_mail(subject, message, sender_email, [user.email, ])
+    pass
 
 
 def new_homework(user, deadline):
     deadline = deadline.strftime('%Y-%m-%d %H:%M')
     subject = "Új házifeladat"
     message = read_email('new_homework.txt')
-    message = str.format(message % {'name': user.get_full_name(), 'link': link, 'deadline': deadline})
+    message = str.format(
+        message % {'name': user.get_full_name(), 'link': link, 'deadline': deadline})
     send_mail(subject, message, sender_email, [user.email, ])
+    pass
 
 
 def homework_corrected(user, title, accepted):
@@ -47,5 +52,7 @@ def homework_corrected(user, title, accepted):
     else:
         status = 'Hibás'
     message = read_email('homework_corrected.txt')
-    message = str.format(message % {'name': user.get_full_name(), 'link': link, 'status': status, 'title': title})
+    message = str.format(message % {'name': user.get_full_name(
+    ), 'link': link, 'status': status, 'title': title})
     send_mail(subject, message, sender_email, [user.email, ])
+    pass
diff --git a/src/common/middleware.py b/src/common/middleware.py
deleted file mode 100644
index 82f040c15458facfd7e6603558465825b2dc4c4a..0000000000000000000000000000000000000000
--- a/src/common/middleware.py
+++ /dev/null
@@ -1,19 +0,0 @@
-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 7d2384c7bf9186939272c68ac9d5f61dbfe58e72..71019190dc24d58c05180291c99b3d4f83ed1fcd 100644
--- a/src/common/permissions.py
+++ b/src/common/permissions.py
@@ -5,13 +5,13 @@ from rest_framework.permissions import SAFE_METHODS
 class IsStaffOrReadOnly(BasePermission):
     def has_permission(self, request, view):
         return request.method in SAFE_METHODS or\
-               (request.user.is_authenticated and request.user.profile.role == 'Staff')
+            (request.user.is_authenticated and request.user.profile.role == 'Staff')
 
 
 class IsStaffOrReadOnlyForAuthenticated(BasePermission):
     def has_permission(self, request, view):
         return request.user.is_authenticated and\
-               (request.method in SAFE_METHODS or request.user.profile.role == 'Staff')
+            (request.method in SAFE_METHODS or request.user.profile.role == 'Staff')
 
 
 class IsStaffUser(BasePermission):
@@ -27,4 +27,5 @@ class IsSafeOrPatch(BasePermission):
 class IsStaffOrStudent(BasePermission):
     def has_permission(self, request, view):
         return request.user.is_authenticated and\
-               (request.user.profile.role == 'Staff' or request.user.profile.role == 'Student')
+            (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 a6c028301e73f9984456b29c47277a90f2ca37ed..f40f0e7e4a8d42373f06d0b01ce63b3a6f730cf8 100644
--- a/src/document/migrations/0001_initial.py
+++ b/src/document/migrations/0001_initial.py
@@ -1,9 +1,10 @@
-# Generated by Django 2.0.1 on 2019-01-17 15:06
+# Generated by Django 2.2.4 on 2020-02-04 14:16
 
 import common.validators
 import django.core.validators
 from django.db import migrations, models
 import django.db.models.deletion
+import document.models
 
 
 class Migration(migrations.Migration):
@@ -21,11 +22,11 @@ class Migration(migrations.Migration):
             fields=[
                 ('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)),
+                ('name', models.CharField(blank=True, default='', max_length=150)),
                 ('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')),
+                ('file', models.FileField(blank=True, null=True, upload_to=document.models.document_file_name, validators=[django.core.validators.FileExtensionValidator(['png', 'jpeg', 'jpg', 'zip']), common.validators.FileSizeValidator(size_limit=52428800)])),
+                ('solution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='homework.Solution')),
+                ('uploaded_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='documents', to='account.Profile')),
             ],
         ),
     ]
diff --git a/src/document/migrations/0002_auto_20190121_1332.py b/src/document/migrations/0002_auto_20190121_1332.py
deleted file mode 100644
index 5cf755b92259e186c711bd5be422791f17294858..0000000000000000000000000000000000000000
--- a/src/document/migrations/0002_auto_20190121_1332.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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
deleted file mode 100644
index fa4d1c22ae825d615e093126a69d4a0a76132ad0..0000000000000000000000000000000000000000
--- a/src/document/migrations/0003_auto_20190121_1335.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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
deleted file mode 100644
index 9eb42c6664d6d3fe22abb6ec091b7ea965643db5..0000000000000000000000000000000000000000
--- a/src/document/migrations/0004_auto_20190123_1812.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# 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
deleted file mode 100644
index 5c207c4a9776a2776d3d1b5da9d0473d2a283175..0000000000000000000000000000000000000000
--- a/src/document/migrations/0005_auto_20190129_1438.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# 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 8e26ea20ec77461fe475a53e308c9d0194e131c2..f4103dc2c2a9c43cc467fa48c118ec73a5ee3ba9 100644
--- a/src/document/models.py
+++ b/src/document/models.py
@@ -10,7 +10,7 @@ from common.validators import FileSizeValidator
 
 def document_file_name(instance, filename):
     return '/'.join([
-        'document',
+        'public/document',
         instance.solution.task.title,
         instance.uploaded_by.full_name,
         filename
@@ -18,7 +18,11 @@ def document_file_name(instance, filename):
 
 
 class Document(models.Model):
-    uploaded_by = models.ForeignKey(Profile, on_delete=models.DO_NOTHING)
+    uploaded_by = models.ForeignKey(
+        Profile,
+        on_delete=models.DO_NOTHING,
+        related_name='documents',
+    )
     uploaded_at = models.DateTimeField(auto_now_add=True, editable=False)
     name = models.CharField(max_length=150, blank=True, default='')
     description = models.TextField(blank=True, default='')
@@ -36,7 +40,8 @@ class Document(models.Model):
         null=True,
         upload_to=document_file_name
     )
-    solution = models.ForeignKey(Solution, related_name='files', on_delete=models.CASCADE)
+    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 f7ad1c31123d1a5e159c07187174d087d5daaf6e..616321803f490246ede36bb5a43285d54ecc6577 100644
--- a/src/document/serializers.py
+++ b/src/document/serializers.py
@@ -1,7 +1,6 @@
 from rest_framework import serializers
 from common.serializers import CurrentUserProfileDefault
 from . import models
-from common.middleware import CurrentUserMiddleware
 
 _max_count = 1
 
@@ -9,20 +8,51 @@ _max_count = 1
 class DocumentSerializer(serializers.ModelSerializer):
     uploaded_by = serializers.HiddenField(default=CurrentUserProfileDefault())
     uploaded_by_name = serializers.SerializerMethodField()
+    file_url = serializers.SerializerMethodField()
 
     class Meta:
         model = models.Document
-        fields = ('uploaded_by', 'uploaded_at', 'name', 'description', 'file', 'uploaded_by_name', 'solution', )
+        fields = [
+            'uploaded_by',
+            'uploaded_at',
+            'name',
+            'description',
+            'file_url',
+            'file',
+            'uploaded_by_name',
+            'solution',
+        ]
+
+        read_only_fields = [
+            'uploaded_by',
+            'uploaded_at',
+            'file_url',
+            'uploaded_by_name',
+        ]
+
+        extra_kwargs = {
+            'file': {'write_only': True},
+        }
+
+    def to_representation(self, instance):
+        data = super().to_representation(instance)
+        if not data['file_url']:
+            data['file_url'] = ""
+        return data
 
     def get_uploaded_by_name(self, obj):
         return obj.uploaded_by.full_name
 
+    def get_file_url(self, obj):
+        return f"/api/v1/documents/{obj.id}/download/"
+
     def validate_solution(self, value):
-        profile = CurrentUserMiddleware.get_current_user_profile()
+        profile = self.context['request'].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()
+        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!')
+            raise serializers.ValidationError(
+                f'You cant upload more than {_max_count} document to one solution!')
         return value
diff --git a/src/document/views.py b/src/document/views.py
index 00f53216570810c84504625b4c1f2abdf2896f2d..86e53f1f7643a134731c18c3c52b891d2de4d581 100644
--- a/src/document/views.py
+++ b/src/document/views.py
@@ -3,6 +3,10 @@ from common import permissions
 from . import models
 from . import serializers
 from rest_framework.parsers import JSONParser, MultiPartParser
+from django.http import HttpResponse, Http404
+from rest_framework.decorators import action
+import os
+import mimetypes
 
 
 class DocumentViewSet(viewsets.ModelViewSet):
@@ -23,7 +27,10 @@ class DocumentViewSet(viewsets.ModelViewSet):
         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)
+            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:
@@ -36,3 +43,23 @@ class DocumentViewSet(viewsets.ModelViewSet):
         if solution_id is not None:
             return queryset.filter(solution=solution_id)
         return queryset
+
+    def perform_create(self, serializer):
+        kwargs = {
+            'uploaded_by': self.request.user.profile
+        }
+
+        serializer.save(**kwargs)
+
+    @action(detail=True, methods=["get"])
+    def download(self, request, pk):
+        document = self.get_object()
+        with document.file.open() as fh:
+            response = HttpResponse(
+                fh.read(),
+                content_type=mimetypes.guess_type(document.file.name)
+            )
+            response['Content-Disposition'] = \
+                'inline; filename=' + os.path.basename(document.file.name)
+            return response
+        raise Http404
diff --git a/src/entrypoint.prod.sh b/src/entrypoint.prod.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5236bfaeabe748c12cfce8f387009f568021975a
--- /dev/null
+++ b/src/entrypoint.prod.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [ "$DJANGO_SETTINGS_MODULE" = "kszkepzes.settings.production" ]
+then
+    echo "Waiting for postgres..."
+
+    while ! nc -z $DB_HOST $DB_PORT; do
+      sleep 0.1
+    done
+
+    echo "PostgreSQL started"
+fi
+
+exec "$@"
\ No newline at end of file
diff --git a/src/entrypoint.sh b/src/entrypoint.sh
new file mode 100755
index 0000000000000000000000000000000000000000..e837b4f5bad4448d9d7558f0dc0ebb9de1b2b9bb
--- /dev/null
+++ b/src/entrypoint.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if [ "$DJANGO_SETTINGS_MODULE" = "kszkepzes.settings.production" ]
+then
+    echo "Waiting for postgres..."
+
+    while ! nc -z $DB_HOST $DB_PORT; do
+      sleep 0.1
+    done
+
+    echo "PostgreSQL started"
+fi
+
+python manage.py flush --no-input
+python manage.py migrate
+python manage.py collectstatic --no-input --clear
+
+exec "$@"
\ No newline at end of file
diff --git a/src/groups/__init__.py b/src/groups/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/groups/admin.py b/src/groups/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..8eefe1fca67e802c1a591c1705104a3bc34f23fc
--- /dev/null
+++ b/src/groups/admin.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+
+from . import models
+
+admin.site.register(models.Group)
diff --git a/src/groups/apps.py b/src/groups/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6984a81f64b2c3bf2ebae54e2d9501333bafa38
--- /dev/null
+++ b/src/groups/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class GroupsConfig(AppConfig):
+    name = 'groups'
diff --git a/src/groups/migrations/0001_initial.py b/src/groups/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..643e9bace651b115a74ee0ce5c4dcbc9157b6b82
--- /dev/null
+++ b/src/groups/migrations/0001_initial.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.2.4 on 2020-02-02 14:41
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Group',
+            fields=[
+                ('id', models.AutoField(auto_created=True,
+                                        primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(blank=True, default='', max_length=50)),
+                ('description', models.TextField(blank=True, default='')),
+            ],
+        ),
+    ]
diff --git a/src/groups/migrations/__init__.py b/src/groups/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/groups/models.py b/src/groups/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..2953fdc811b6e9a837cd4e4eb6b1cf8f4f72f9bd
--- /dev/null
+++ b/src/groups/models.py
@@ -0,0 +1,11 @@
+from django.db import models
+
+# KSZK groups, like Devteam, Sysadmin, ...
+
+
+class Group(models.Model):
+    name = models.CharField(max_length=50, blank=True, default='')
+    description = models.TextField(blank=True, default='')
+
+    def __str__(self):
+        return self.name
diff --git a/src/groups/serializers.py b/src/groups/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..2f14fa78374ea5983b9ec38f05d4f50be82b760e
--- /dev/null
+++ b/src/groups/serializers.py
@@ -0,0 +1,16 @@
+from rest_framework import serializers
+from . import models
+
+
+class GroupSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = models.Group
+        read_only_fields = (
+            'id',
+            'name',
+            'description',)
+        fields = (
+            'id',
+            'name',
+            'description',
+        )
diff --git a/src/groups/tests.py b/src/groups/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..a79ca8be565f44aacce95bad20c1ee34d175ed20
--- /dev/null
+++ b/src/groups/tests.py
@@ -0,0 +1,3 @@
+# from django.test import TestCase
+
+# Create your tests here.
diff --git a/src/groups/urls.py b/src/groups/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..a265b6f79ebda0883deed15fc061f23a3369c88a
--- /dev/null
+++ b/src/groups/urls.py
@@ -0,0 +1,7 @@
+from rest_framework import routers
+from . import views
+
+router = routers.DefaultRouter()
+
+router.register(r'groups', views.GroupsViewSet, base_name='groups')
+urlpatterns = router.urls
diff --git a/src/groups/views.py b/src/groups/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..2149906966c10423777d5da772f3402579e89904
--- /dev/null
+++ b/src/groups/views.py
@@ -0,0 +1,9 @@
+from rest_framework import viewsets
+
+from . import serializers
+from . import models
+
+
+class GroupsViewSet(viewsets.ReadOnlyModelViewSet):
+    serializer_class = serializers.GroupSerializer
+    queryset = models.Group.objects.all()
diff --git a/src/homework/migrations/0001_initial.py b/src/homework/migrations/0001_initial.py
index 13125342ce3865c45e5e8304a5353c3b5837e371..562d226240a20d19ece2eb84f0586b9fc50976cd 100644
--- a/src/homework/migrations/0001_initial.py
+++ b/src/homework/migrations/0001_initial.py
@@ -1,6 +1,5 @@
-# Generated by Django 2.0.1 on 2019-01-17 15:06
+# Generated by Django 2.2.4 on 2020-02-04 14:16
 
-import common.middleware
 from django.db import migrations, models
 import django.db.models.deletion
 
@@ -15,32 +14,29 @@ class Migration(migrations.Migration):
 
     operations = [
         migrations.CreateModel(
-            name='Solution',
+            name='Task',
             fields=[
                 ('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)),
-                ('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')),
+                ('title', models.CharField(max_length=150)),
+                ('text', models.TextField()),
+                ('deadline', models.DateTimeField()),
+                ('bits', models.IntegerField(default=1)),
+                ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='tasks', to='account.Profile')),
             ],
         ),
         migrations.CreateModel(
-            name='Task',
+            name='Solution',
             fields=[
                 ('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)),
-                ('title', models.CharField(max_length=150)),
-                ('text', models.TextField()),
-                ('deadline', models.DateTimeField()),
-                ('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')),
+                ('note', models.TextField(blank=True, default='')),
+                ('accepted', models.BooleanField(blank=True, default=False)),
+                ('corrected', models.BooleanField(blank=True, default=False)),
+                ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='solution', to='account.Profile')),
+                ('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='solutions', to='homework.Task')),
             ],
         ),
-        migrations.AddField(
-            model_name='solution',
-            name='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_20190121_2124.py b/src/homework/migrations/0002_auto_20190121_2124.py
deleted file mode 100644
index 3abf5a83e87d8424a882e3c92f55af2f1478d3ee..0000000000000000000000000000000000000000
--- a/src/homework/migrations/0002_auto_20190121_2124.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.0.1 on 2019-01-21 20:24
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('homework', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='solution',
-            name='accepted',
-            field=models.BooleanField(default=False),
-        ),
-        migrations.AlterField(
-            model_name='solution',
-            name='corrected',
-            field=models.BooleanField(default=False),
-        ),
-    ]
diff --git a/src/homework/models.py b/src/homework/models.py
index 4ecb6e958c9a9349ca39a26d75c57463f030c1fc..be416b1182356e443c499faa0650b7e04bb8a7cd 100755
--- a/src/homework/models.py
+++ b/src/homework/models.py
@@ -1,5 +1,4 @@
 from django.db import models
-from common.middleware import CurrentUserMiddleware
 from account.models import Profile
 
 
@@ -8,25 +7,25 @@ class Task(models.Model):
         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()
+    bits = models.IntegerField(default=1)
 
     def __str__(self):
         return self.title
 
 
 class Solution(models.Model):
-    task = models.ForeignKey(Task, related_name='solutions', 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)
@@ -34,5 +33,9 @@ class Solution(models.Model):
     accepted = models.BooleanField(blank=True, default=False)
     corrected = models.BooleanField(blank=True, default=False)
 
+    @property
+    def my_bits(self):
+        return 10
+
     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 3ee662e264b2710475a78c9b0538a2ad8b2770cb..e59ed06790824ae30bdaf891699df03960cdca68 100755
--- a/src/homework/serializers.py
+++ b/src/homework/serializers.py
@@ -3,7 +3,6 @@ from django.utils import timezone
 from account.models import Profile
 from . import models
 from common import email
-from common.middleware import CurrentUserMiddleware
 
 
 class TaskSerializer(serializers.ModelSerializer):
@@ -14,20 +13,28 @@ class TaskSerializer(serializers.ModelSerializer):
 
     def validate(self, data):
         if timezone.now() >= data['deadline']:
-            raise serializers.ValidationError('Please, enter appropriate deadline.')
+            raise serializers.ValidationError(
+                'Please, enter appropriate deadline.')
         return data
 
     def create(self, validated_data):
-        profiles = Profile.objects.filter(role="Student").exclude(user__email='')
+        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):
+class SolutionSerializer_Student(serializers.ModelSerializer):
     class Meta:
         model = models.Solution
-        read_only_fields = ('created_by', 'created_at', 'updated_at', 'ready', 'files')
+        read_only_fields = (
+            'created_by',
+            'created_at',
+            'updated_at',
+            'ready',
+            'files')
         fields = (
             'id',
             'task',
@@ -45,26 +52,59 @@ class SolutionSerializer(serializers.ModelSerializer):
             raise serializers.ValidationError('You late.')
         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!")
+    def validate_note(self, value):
+        if value != '':
+            raise serializers.ValidationError(
+                "You don't have permission to create note!")
         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 update(self, instance, validated_data):
+        if instance.corrected is not True and validated_data.get(
+                'corrected', instance.corrected) is True:
+            email.homework_corrected(
+                instance.created_by.user,
+                instance.task.title,
+                validated_data.get('accepted', instance.accepted)
+            )
+        return super().update(instance, validated_data)
 
-    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!")
+    def create(self, validated_data):
+        profile = self.context['request'].user.profile
+        models.Solution.objects.filter(
+            created_by=profile,
+            task=validated_data['task']).delete()
+        return super().create(validated_data)
+
+
+class SolutionSerializer_Staff(serializers.ModelSerializer):
+    class Meta:
+        model = models.Solution
+        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_task(self, value):
+        if timezone.now() > value.deadline:
+            raise serializers.ValidationError('You late.')
         return value
 
     def update(self, instance, validated_data):
-        if instance.corrected is not True and validated_data.get('corrected', instance.corrected) is True:
+        if instance.corrected is not True and validated_data.get(
+                'corrected', instance.corrected) is True:
             email.homework_corrected(
                 instance.created_by.user,
                 instance.task.title,
@@ -73,6 +113,8 @@ class SolutionSerializer(serializers.ModelSerializer):
         return super().update(instance, validated_data)
 
     def create(self, validated_data):
-        profile = CurrentUserMiddleware.get_current_user_profile()
-        models.Solution.objects.filter(created_by=profile, task=validated_data['task']).delete()
+        profile = self.context['request'].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 9a9d88c415e5b6182a6707bdcfb32db355f7d05a..554533838cf23f7fedca98b9d6d8490688d9257e 100755
--- a/src/homework/views.py
+++ b/src/homework/views.py
@@ -8,13 +8,29 @@ from common import permissions
 class TasksViewSet(viewsets.ModelViewSet):
     serializer_class = serializers.TaskSerializer
     queryset = models.Task.objects.all()
-    permission_classes = (permissions.IsStaffOrReadOnlyForAuthenticated, permissions.IsStaffOrStudent, )
+    permission_classes = (
+        permissions.IsStaffOrReadOnlyForAuthenticated,
+        permissions.IsStaffOrStudent,
+    )
+
+    def perform_create(self, serializer):
+        kwargs = {
+            'created_by': self.request.user.profile
+        }
+
+        serializer.save(**kwargs)
 
 
 class SolutionsViewSet(viewsets.ModelViewSet):
-    serializer_class = serializers.SolutionSerializer
+    serializer_class = serializers.SolutionSerializer_Student
     permission_classes = (permissions.IsStaffOrStudent, )
 
+    def get_serializer_class(self):
+        user = self.request.user
+        if user.profile.role == 'Staff':
+            return serializers.SolutionSerializer_Staff
+        return serializers.SolutionSerializer_Student
+
     def get_queryset(self):
         user = self.request.user
         queryset = models.Solution.objects.filter(created_by=user.profile)
@@ -24,3 +40,10 @@ class SolutionsViewSet(viewsets.ModelViewSet):
             if profile_id is not None:
                 queryset = queryset.filter(created_by=profile_id)
         return queryset
+
+    def perform_create(self, serializer):
+        kwargs = {
+            'created_by': self.request.user.profile
+        }
+
+        serializer.save(**kwargs)
diff --git a/src/images/__init__.py b/src/images/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/images/admin.py b/src/images/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..01981f71f01bb049f26d2f3e12783ae24650009e
--- /dev/null
+++ b/src/images/admin.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+from .models import Image
+
+admin.site.register(Image)
+# Register your models here.
diff --git a/src/images/apps.py b/src/images/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..a873d1a4e0f3523f2d9e297c7c895e710098e878
--- /dev/null
+++ b/src/images/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class ImagesConfig(AppConfig):
+    name = 'images'
diff --git a/src/images/migrations/0001_initial.py b/src/images/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..2319c1469c25d31d08611fb04d3e4a1c94aba949
--- /dev/null
+++ b/src/images/migrations/0001_initial.py
@@ -0,0 +1,21 @@
+# Generated by Django 2.2.4 on 2020-02-07 22:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Images',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('image', models.ImageField(blank=True, null=True, upload_to='images/')),
+            ],
+        ),
+    ]
diff --git a/src/images/migrations/0002_auto_20200208_0254.py b/src/images/migrations/0002_auto_20200208_0254.py
new file mode 100644
index 0000000000000000000000000000000000000000..045c2a144dfaf0952963f7841f53d9e740bc40e5
--- /dev/null
+++ b/src/images/migrations/0002_auto_20200208_0254.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.2.4 on 2020-02-08 01:54
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('images', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='images',
+            name='image',
+            field=models.ImageField(blank=True, null=True, upload_to='public/images/'),
+        ),
+    ]
diff --git a/src/images/migrations/0003_auto_20200208_0402.py b/src/images/migrations/0003_auto_20200208_0402.py
new file mode 100644
index 0000000000000000000000000000000000000000..4868a49ca7b2222bd8bce800b0e4344032b3b844
--- /dev/null
+++ b/src/images/migrations/0003_auto_20200208_0402.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.2.4 on 2020-02-08 03:02
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('images', '0002_auto_20200208_0254'),
+    ]
+
+    operations = [
+        migrations.RenameModel(
+            old_name='Images',
+            new_name='Image',
+        ),
+    ]
diff --git a/src/images/migrations/__init__.py b/src/images/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/images/models.py b/src/images/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..cab2442d4eefa7e8d5200145ab567d911d12bffb
--- /dev/null
+++ b/src/images/models.py
@@ -0,0 +1,20 @@
+import os
+
+from django.db import models
+from django.dispatch import receiver
+
+
+class Image(models.Model):
+    image = models.ImageField(
+        upload_to='public/images/', null=True, blank=True
+        )
+
+    def __str__(self):
+        return str(self.id)
+
+# Deletes file from filesystem when File object is deleted.
+@receiver(models.signals.post_delete, sender=Image)
+def auto_delete_image_on_delete(sender, instance, **kwargs):
+    if instance.image:
+        if os.path.isfile(instance.image.path):
+            os.remove(instance.image.path)
diff --git a/src/images/serializers.py b/src/images/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..b318581cc09e83aa77582afb4217893b1b235866
--- /dev/null
+++ b/src/images/serializers.py
@@ -0,0 +1,17 @@
+from images.models import Image
+from rest_framework import serializers
+
+
+class ImageSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Image
+        fields = ('image',)
+
+    def to_representation(self, instance):
+        response = super(
+            ImageSerializer,
+            self
+        ).to_representation(instance)
+        if instance.image:
+            response['image'] = instance.image.url
+        return response
diff --git a/src/images/tests.py b/src/images/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/images/urls.py b/src/images/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..b16c854441022549d60f67c06b69d15ac63cf1bd
--- /dev/null
+++ b/src/images/urls.py
@@ -0,0 +1,8 @@
+from rest_framework import routers
+from images import views
+
+
+router = routers.DefaultRouter()
+router.register(r'images', views.ImagesViewSet, base_name='images')
+
+urlpatterns = router.urls
diff --git a/src/images/views.py b/src/images/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..097e757f9fe3902c4e02838735e6e3b466591960
--- /dev/null
+++ b/src/images/views.py
@@ -0,0 +1,10 @@
+from common.permissions import IsStaffOrReadOnly
+from rest_framework import viewsets
+from images.models import Image
+from images.serializers import ImageSerializer
+
+
+class ImagesViewSet(viewsets.ModelViewSet):
+    serializer_class = ImageSerializer
+    permission_classes = (IsStaffOrReadOnly, )
+    queryset = Image.objects.all()
diff --git a/src/kszkepzes/settings/base.py b/src/kszkepzes/settings/base.py
index b831a0ec690d3c2ff85248e79972ab90f1b260f2..fa4afc6fa9a812faa4d6ddd0c096c85cf72c7cdb 100644
--- a/src/kszkepzes/settings/base.py
+++ b/src/kszkepzes/settings/base.py
@@ -13,7 +13,7 @@ https://docs.djangoproject.com/en/1.11/ref/settings/
 import os
 
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
 
 # Quick-start development settings - unsuitable for production
@@ -40,6 +40,7 @@ INSTALLED_APPS = [
     'django_extensions',
     'import_export',
     'rest_framework',
+    "rest_framework_api_key",
     'social_django',
     'solo',
     'authsch',
@@ -48,6 +49,10 @@ INSTALLED_APPS = [
     'stats',
     'news',
     'document',
+    'mentors',
+    'images',
+    'groups',
+    'drf_yasg',
 ]
 
 MIDDLEWARE = [
@@ -58,7 +63,6 @@ MIDDLEWARE = [
     'django.contrib.auth.middleware.AuthenticationMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
-    'common.middleware.CurrentUserMiddleware',
 ]
 
 ROOT_URLCONF = 'kszkepzes.urls'
@@ -154,7 +158,8 @@ USE_TZ = True
 # Static files (CSS, JavaScript, Images)
 # https://docs.djangoproject.com/en/1.11/howto/static-files/
 
-STATIC_URL = '/django-static/'
+STATIC_URL = '/staticfiles/'
+STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
 
-MEDIA_ROOT = os.getenv('MEDIA_ROOT', '/tmp')
-MEDIA_URL = '/media/'
+MEDIA_URL = "/mediafiles/"
+MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles")
diff --git a/src/kszkepzes/settings/local.py b/src/kszkepzes/settings/local.py
index 53eb49a1bd60f3f66631867f420381d3070b85a1..1c4e0b5cb5ea38e79c195fc5e345f921fb5a8849 100644
--- a/src/kszkepzes/settings/local.py
+++ b/src/kszkepzes/settings/local.py
@@ -1,6 +1,8 @@
 from .base import *
 
-SESSION_COOKIE_SECURE=False
+DEBUG = int(os.environ.get("DEBUG", default=1))
+
+SESSION_COOKIE_SECURE = False
 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
 EMAIL_HOST = 'smtp.gmail.com'
 EMAIL_USE_TLS = True
diff --git a/src/kszkepzes/settings/production.py b/src/kszkepzes/settings/production.py
index cbd865978fa81069cdf9c99a29f8360375bb6fd2..f6d364955eb58dbcdd773123c282c597ee0ccc5a 100644
--- a/src/kszkepzes/settings/production.py
+++ b/src/kszkepzes/settings/production.py
@@ -1,12 +1,12 @@
 from .base import *
 
-DEBUG = False
+DEBUG = int(os.environ.get("DEBUG", default=0))
 
 ALLOWED_HOSTS = ['*']
 
 DATABASES = {
     'default': {
-        'ENGINE': 'django.db.backends.postgresql',
+        'ENGINE': os.getenv('DB_ENGINE', 'django.db.backends.postgresql'),
         'NAME': os.getenv('DB_NAME', 'kszkepzes'),
         'USER': os.getenv('DB_USER'),
         'PASSWORD': os.getenv('DB_PASSWORD'),
@@ -24,7 +24,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/kszkepzes/urls.py b/src/kszkepzes/urls.py
index f5e414edc9604b99a6d4118130f32b1e4b25b724..c07e90ed5b4b50ce44b43077039adb5e8f38e0fd 100644
--- a/src/kszkepzes/urls.py
+++ b/src/kszkepzes/urls.py
@@ -4,7 +4,30 @@ from django.conf import settings
 from django.contrib import admin
 from django.contrib.auth import views as auth_views
 
+from rest_framework import permissions
+from drf_yasg.views import get_schema_view
+from drf_yasg import openapi
+
+schema_view = get_schema_view(
+    openapi.Info(
+        title="Snippets API",
+        default_version='v1',
+        description="Test description",
+        terms_of_service="https://www.google.com/policies/terms/",
+        contact=openapi.Contact(email="contact@snippets.local"),
+        license=openapi.License(name="BSD License"),
+    ),
+    public=True,
+    permission_classes=(permissions.AllowAny,),
+)
+
 urlpatterns = [
+    url(r'^api/v1/swagger(?P<format>\.json|\.yaml)$',
+        schema_view.without_ui(cache_timeout=0), name='schema-json'),
+    url(r'^api/v1/swagger/$', schema_view.with_ui('swagger',
+                                                  cache_timeout=0), name='schema-swagger-ui'),
+    url(r'^api/v1/redoc/$', schema_view.with_ui('redoc',
+                                                cache_timeout=0), name='schema-redoc'),
     url(r'^admin/', admin.site.urls),
     url(r'^api/v1/', include('social_django.urls', namespace='social')),
     url(r'^api/v1/homework/', include('homework.urls')),
@@ -12,8 +35,12 @@ urlpatterns = [
     url(r'^api/v1/', include('account.urls')),
     url(r'^api/v1/', include('news.urls')),
     url(r'^api/v1/', include('document.urls')),
+    url(r'^api/v1/', include('groups.urls')),
+    url(r'^api/v1/', include('mentors.urls')),
+    url(r'^api/v1/', include('images.urls')),
     url(r'^api/v1/logout/$', auth_views.LogoutView.as_view(), name='logout'),
 ]
 
 if settings.DEBUG:
-    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+    urlpatterns += static(settings.MEDIA_URL,
+                          document_root=settings.MEDIA_ROOT)
diff --git a/src/kszkepzes/wsgi.py b/src/kszkepzes/wsgi.py
index 12023b6a78e7c9bbb5cc7e48d9d4f63b9eba7b3e..f29c7593000fed09e6a5a94b4c631462fb71bc73 100644
--- a/src/kszkepzes/wsgi.py
+++ b/src/kszkepzes/wsgi.py
@@ -11,6 +11,7 @@ import os
 
 from django.core.wsgi import get_wsgi_application
 
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "kszkepzes.settings.production")
+os.environ.setdefault("DJANGO_SETTINGS_MODULE",
+                      "kszkepzes.settings.production")
 
 application = get_wsgi_application()
diff --git a/src/mentors/__init__.py b/src/mentors/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/mentors/admin.py b/src/mentors/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..bacbf7e7188cfdcd9b02e7ebae4224e61e575e06
--- /dev/null
+++ b/src/mentors/admin.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+from .models import Mentor
+
+admin.site.register(Mentor)
+# Register your models here.
diff --git a/src/mentors/apps.py b/src/mentors/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..5649da659a30793fa15b89d01037e08c0fdb3275
--- /dev/null
+++ b/src/mentors/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class MentorsConfig(AppConfig):
+    name = 'mentors'
diff --git a/src/mentors/migrations/0001_initial.py b/src/mentors/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..0939292fa324326b75466f3c55561f92fbe628ad
--- /dev/null
+++ b/src/mentors/migrations/0001_initial.py
@@ -0,0 +1,27 @@
+# Generated by Django 2.2.4 on 2020-02-04 14:16
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('account', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Mentor',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=200)),
+                ('text', models.TextField()),
+                ('image', models.ImageField(blank=True, null=True, upload_to='mentors/images/')),
+                ('email', models.EmailField(max_length=254)),
+                ('mentor', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='mentor', to='account.Profile')),
+            ],
+        ),
+    ]
diff --git a/src/mentors/migrations/0002_auto_20200209_1955.py b/src/mentors/migrations/0002_auto_20200209_1955.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ffcb72b291e7efe9a1c82f5da1c9d3b827e9d36
--- /dev/null
+++ b/src/mentors/migrations/0002_auto_20200209_1955.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.2.4 on 2020-02-09 18:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('mentors', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='mentor',
+            name='image',
+            field=models.ImageField(blank=True, null=True, upload_to='public/mentors/images/'),
+        ),
+    ]
diff --git a/src/mentors/migrations/0003_remove_mentor_mentor.py b/src/mentors/migrations/0003_remove_mentor_mentor.py
new file mode 100644
index 0000000000000000000000000000000000000000..dcd08df7e84fac46c9f69642b3132d7e89955cd8
--- /dev/null
+++ b/src/mentors/migrations/0003_remove_mentor_mentor.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.2.4 on 2020-02-09 22:52
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('mentors', '0002_auto_20200209_1955'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='mentor',
+            name='mentor',
+        ),
+    ]
diff --git a/src/mentors/migrations/__init__.py b/src/mentors/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/mentors/models.py b/src/mentors/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..b641319bcfe6aaf07c8754ef13850c4c9e53491c
--- /dev/null
+++ b/src/mentors/models.py
@@ -0,0 +1,22 @@
+import os
+
+from django.db import models
+from django.dispatch import receiver
+
+
+class Mentor(models.Model):
+    name = models.CharField(null=False, max_length=200)
+    text = models.TextField()
+    image = models.ImageField(
+        upload_to='public/mentors/images/', null=True, blank=True)
+    email = models.EmailField()
+
+    def __str__(self):
+        return self.name
+
+# Deletes file from filesystem when File object is deleted.
+@receiver(models.signals.post_delete, sender=Mentor)
+def auto_delete_image_on_delete(sender, instance, **kwargs):
+    if instance.image:
+        if os.path.isfile(instance.image.path):
+            os.remove(instance.image.path)
diff --git a/src/mentors/serializers.py b/src/mentors/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6e84b8565716eae6c90a67019742bfc22ef99f4
--- /dev/null
+++ b/src/mentors/serializers.py
@@ -0,0 +1,18 @@
+from mentors.models import Mentor
+from rest_framework import serializers
+
+
+class MentorSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = Mentor
+        fields = '__all__'
+
+    def to_representation(self, instance):
+        response = super(
+            MentorSerializer,
+            self
+        ).to_representation(instance)
+        if instance.image:
+            response['image'] = instance.image.url
+        return response
diff --git a/src/mentors/tests.py b/src/mentors/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/mentors/urls.py b/src/mentors/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..027a533ba50729d7ac321e7c6a3068575a59e76c
--- /dev/null
+++ b/src/mentors/urls.py
@@ -0,0 +1,8 @@
+from rest_framework import routers
+from mentors import views
+
+
+router = routers.DefaultRouter()
+router.register(r'mentors', views.MentorsViewSet, base_name='mentors')
+
+urlpatterns = router.urls
diff --git a/src/mentors/views.py b/src/mentors/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..9fd090dc8e5aa8df0a7ae1c08204e023ab335bfe
--- /dev/null
+++ b/src/mentors/views.py
@@ -0,0 +1,14 @@
+from common.permissions import IsStaffOrStudent, \
+    IsStaffOrReadOnlyForAuthenticated
+from rest_framework import viewsets
+from mentors.models import Mentor
+from mentors.serializers import MentorSerializer
+
+
+class MentorsViewSet(viewsets.ModelViewSet):
+    serializer_class = MentorSerializer
+    permission_classes = (
+        IsStaffOrReadOnlyForAuthenticated,
+        IsStaffOrStudent,
+    )
+    queryset = Mentor.objects.all().order_by('name')
diff --git a/src/news/migrations/0001_initial.py b/src/news/migrations/0001_initial.py
index a8f40ce6d982860cc36fa0d335132bf70b770d9e..616cde052b50e3c1a7ee94e3d1c817a8d8a54b85 100644
--- a/src/news/migrations/0001_initial.py
+++ b/src/news/migrations/0001_initial.py
@@ -1,6 +1,5 @@
-# Generated by Django 2.0.1 on 2019-01-17 15:06
+# Generated by Django 2.2.4 on 2020-02-04 14:16
 
-import common.middleware
 from django.db import migrations, models
 import django.db.models.deletion
 
@@ -22,7 +21,7 @@ class Migration(migrations.Migration):
                 ('text', models.TextField()),
                 ('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')),
+                ('author', models.ForeignKey(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/models.py b/src/news/models.py
index c216221410bc8e9f04f3b76c90265ed94191b9d6..17837b5d6a2287cc7429c2a7fdb26a4b13e3589e 100644
--- a/src/news/models.py
+++ b/src/news/models.py
@@ -1,6 +1,5 @@
 from django.db import models
 from account.models import Profile
-from common.middleware import CurrentUserMiddleware
 
 
 class Article(models.Model):
@@ -8,7 +7,6 @@ class Article(models.Model):
         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()
@@ -17,7 +15,7 @@ class Article(models.Model):
     updated_by = models.ForeignKey(
         Profile,
         related_name="updater",
-        on_delete=models.DO_NOTHING
+        on_delete=models.DO_NOTHING,
     )
 
     def __str__(self):
diff --git a/src/news/serializers.py b/src/news/serializers.py
index 514c63bd416184b25ca57672ed24a276c72a382d..7d967b59bde22839708b919f0796cfc39d716d9c 100644
--- a/src/news/serializers.py
+++ b/src/news/serializers.py
@@ -1,16 +1,14 @@
 from news.models import Article
 from rest_framework import serializers
-from common.serializers import CurrentUserProfileDefault
 
 
 class ArticleSerializer(serializers.ModelSerializer):
-    updated_by = serializers.HiddenField(default=CurrentUserProfileDefault())
     last_update_by = serializers.SerializerMethodField()
     author = serializers.SerializerMethodField()
 
     class Meta:
         model = Article
-        read_only_fields = ('author', 'created_at', 'updated_at', 'updated_by')
+        read_only_fields = ('author', 'created_at', 'updated_at')
         fields = '__all__'
 
     def get_last_update_by(self, obj):
diff --git a/src/news/views.py b/src/news/views.py
index 764d0a93c4aeb7f6ff4a493c6db4aed6f24a6c5c..28e9e71fd1f32a36460f4580ccb672e57b684d43 100644
--- a/src/news/views.py
+++ b/src/news/views.py
@@ -8,3 +8,11 @@ class NewsViewSet(viewsets.ModelViewSet):
     serializer_class = ArticleSerializer
     permission_classes = (IsStaffOrReadOnly,)
     queryset = Article.objects.all().order_by('-created_at')
+
+    def perform_create(self, serializer):
+        kwargs = {
+            'author': self.request.user.profile,
+            'updated_by': self.request.user.profile,
+        }
+
+        serializer.save(**kwargs)
diff --git a/src/stats/admin.py b/src/stats/admin.py
index fb841605335f9cd12025ed91abe5361dc7f806f5..6c3f3b784e18268a87cbc7f48dd87392cb54c746 100644
--- a/src/stats/admin.py
+++ b/src/stats/admin.py
@@ -15,7 +15,8 @@ class EventAdmin(ExportMixin, admin.ModelAdmin):
 
 @admin.register(models.Note)
 class NoteAdmin(ExportMixin, admin.ModelAdmin):
-    list_display = ('profile', 'note', 'event', 'created_by', 'created_at', 'updated_at')
+    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 b6e989e8f6f784a782bf6ef238584160ca141329..8b549fd6b7a023d2c93664a43881beb02b4871fb 100644
--- a/src/stats/migrations/0001_initial.py
+++ b/src/stats/migrations/0001_initial.py
@@ -1,6 +1,5 @@
-# Generated by Django 2.0.1 on 2019-01-17 15:06
+# Generated by Django 2.2.4 on 2020-02-04 14:16
 
-import common.middleware
 from django.db import migrations, models
 import django.db.models.deletion
 
@@ -20,10 +19,12 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('name', models.CharField(max_length=255)),
                 ('date', models.DateTimeField()),
+                ('description', models.TextField(blank=True, default='')),
                 ('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')),
+                ('absent', models.ManyToManyField(blank=True, related_name='events_absent', to='account.Profile')),
+                ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='created_event', to='account.Profile')),
+                ('visitors', models.ManyToManyField(blank=True, related_name='events_visitor', to='account.Profile')),
             ],
         ),
         migrations.CreateModel(
@@ -33,7 +34,7 @@ class Migration(migrations.Migration):
                 ('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')),
+                ('created_by', models.ForeignKey(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_event_description.py b/src/stats/migrations/0002_event_description.py
deleted file mode 100644
index d7d056636e1bc6c5ad18457d09a92bc56ba30927..0000000000000000000000000000000000000000
--- a/src/stats/migrations/0002_event_description.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# 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_20190122_1449.py b/src/stats/migrations/0003_auto_20190122_1449.py
deleted file mode 100644
index a3689e8ee847704324f3821df0508d6b89ffbb0d..0000000000000000000000000000000000000000
--- a/src/stats/migrations/0003_auto_20190122_1449.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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/models.py b/src/stats/models.py
index be958204fba61174220ddc988100c21405f44241..366c1d4b7aef6aa518521cdae18343b1a970983c 100644
--- a/src/stats/models.py
+++ b/src/stats/models.py
@@ -1,6 +1,5 @@
 from django.db import models
 from account.models import Profile
-from common.middleware import CurrentUserMiddleware
 
 
 class Event(models.Model):
@@ -21,7 +20,6 @@ class Event(models.Model):
         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)
@@ -31,14 +29,15 @@ class Event(models.Model):
 
 
 class Note(models.Model):
-    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)
+    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.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/resources.py b/src/stats/resources.py
index 712c971ae27eb0f4bebfd30be07eb952d8c5fbb7..b2e284c57094b9ec582900772bf7f007bbdd555e 100644
--- a/src/stats/resources.py
+++ b/src/stats/resources.py
@@ -7,7 +7,8 @@ from . import models
 class EventResource(resources.ModelResource):
     visitors = fields.Field(
         attribute='visitors',
-        widget=widgets.ManyToManyWidget(model=Profile, separator=' ,', field='full_name'),
+        widget=widgets.ManyToManyWidget(
+            model=Profile, separator=' ,', field='full_name'),
     )
 
     class Meta:
diff --git a/src/stats/serializers.py b/src/stats/serializers.py
index e09d7b1b86fe537ddee2106ec72753d626696380..975a7d9af573980c82fa3f9aebd7aaa864968baa 100644
--- a/src/stats/serializers.py
+++ b/src/stats/serializers.py
@@ -22,7 +22,8 @@ class StaffEventSerializer(serializers.ModelSerializer):
         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.')
+                    raise serializers.ValidationError(
+                        'You cant add a student to absent and visitor in the same time.')
         return data
 
 
@@ -37,6 +38,15 @@ class NoteSerializer(serializers.ModelSerializer):
     created_by = serializers.HiddenField(default=CurrentUserProfileDefault())
     created_by_name = serializers.SerializerMethodField()
 
+    def to_representation(self, instance):
+        data = super().to_representation(instance)
+        if not data['event']:
+            data['event'] = ""
+        if not data['profile']:
+            data['profile'] = ""
+
+        return data
+
     class Meta:
         model = models.Note
         fields = '__all__'
@@ -47,5 +57,6 @@ class NoteSerializer(serializers.ModelSerializer):
 
     def validate(self, data):
         if data['profile'] is None and data['event'] is None:
-            raise serializers.ValidationError('You have to add profile or event')
+            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 0e669f8037c98287185212ecbe0c2b43c03bd267..51ed16131f024b07b2401965526d8855ff2cdd35 100644
--- a/src/stats/urls.py
+++ b/src/stats/urls.py
@@ -2,8 +2,10 @@ from rest_framework import routers
 from . import views
 
 router = routers.DefaultRouter()
-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'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 c917e47dd8b96927fe4f1ec018975c84ed6656b5..f1e5c802775b4728575577f9311e1e533a29488d 100644
--- a/src/stats/views.py
+++ b/src/stats/views.py
@@ -9,12 +9,26 @@ class StaffEventViewSet(viewsets.ModelViewSet):
     queryset = models.Event.objects.all().order_by('date')
     permission_classes = (IsStaffUser, )
 
+    def perform_create(self, serializer):
+        kwargs = {
+            'created_by': self.request.user.profile
+        }
+
+        serializer.save(**kwargs)
+
 
 class StudentEventViewSet(viewsets.ModelViewSet):
     serializer_class = serializers.StudentEventSerializer
     queryset = models.Event.objects.all().order_by('date')
     permission_classes = (IsStaffOrStudent, )
 
+    def perform_create(self, serializer):
+        kwargs = {
+            'created_by': self.request.user.profile
+        }
+
+        serializer.save(**kwargs)
+
 
 class NoteViewSet(viewsets.ModelViewSet):
     serializer_class = serializers.NoteSerializer
@@ -31,3 +45,10 @@ class NoteViewSet(viewsets.ModelViewSet):
         if event_id is not None:
             return queryset.filter(event=event_id)
         return queryset
+
+    def perform_create(self, serializer):
+        kwargs = {
+            'created_by': self.request.user.profile
+        }
+
+        serializer.save(**kwargs)