Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 169-add-date-to-examtype
  • 233-make-exam-a-many-to-many-field-on-studentinfo-model
  • 236-improve-importer-experience
  • 243-replace-toggle-buttons-with-switches
  • 250-update-vuetify
  • 258-add-markdown-viewer
  • 265-fix-selection-changing-on-window-switching
  • 272-reviewers-should-be-able-to-assign-exercise-groups-to-tutors
  • 276-create-new-yarn-lockfile
  • 279-tutor-overview-no-scrolling
  • 282-copy-button-does-not-work-when-reviewing-corrections
  • 286-fix-misalignment-of-hide-show-sidebar-buttons
  • 287-build-test-image-constantly-failing
  • 288-add-dropdown-to-participantspage-to-set-students-groups
  • 289-fix-change-log-card
  • 291-revise-to-old-export-scheme
  • 292-update-gitlab-ci-config-for-new-runner
  • 292-update-gitlab-ci-config-for-new-runner-2
  • add-exercise-util-script
  • document-frontend-components
  • grady-exam
  • jakob.dieterle-master-patch-13835
  • master
  • parallel-test
  • test-233-branch-remove-examtype-foreign-key-on-group
  • update-export-dialogs
  • 0.0.1
  • 0.1
  • 0.2
  • 0.3
  • 0.4
  • 0.4.1
  • 0.4.2
  • 0.5.0
  • 0.5.1
  • 1.0.0
  • 1.1.0
  • 2.0.0
  • 2.0.1
  • 2.1.0
  • 2.1.1
  • 2.2.0
  • 3.0.0
  • 3.0.1
  • 4.0.0
  • 4.1.0
  • 4.2.0
  • 4.3.0
  • 4.4.0
  • 4.4.1
  • 5.0.0
  • 5.0.1
  • 5.1.0
  • 5.1.1
  • 5.1.2
  • 5.1.3
  • 5.1.4
  • 5.1.5
  • 5.1.6
  • 5.1.7
  • 5.2.0
  • 5.3.0
  • 5.3.1
  • 5.3.2
  • 5.4.0
  • 5.4.1
  • 5.4.2
  • 6.0.0
  • 6.1.0
  • legacy
70 results

Target

Select target project
  • j.michal/grady
1 result
Select Git revision
  • 169-add-date-to-examtype
  • 233-make-exam-a-many-to-many-field-on-studentinfo-model
  • 236-improve-importer-experience
  • 243-replace-toggle-buttons-with-switches
  • 250-update-vuetify
  • 258-add-markdown-viewer
  • 265-fix-selection-changing-on-window-switching
  • 272-reviewers-should-be-able-to-assign-exercise-groups-to-tutors
  • 276-create-new-yarn-lockfile
  • 279-tutor-overview-no-scrolling
  • 282-copy-button-does-not-work-when-reviewing-corrections
  • 286-fix-misalignment-of-hide-show-sidebar-buttons
  • 287-build-test-image-constantly-failing
  • 288-add-dropdown-to-participantspage-to-set-students-groups
  • 289-fix-change-log-card
  • 291-revise-to-old-export-scheme
  • 292-update-gitlab-ci-config-for-new-runner
  • 292-update-gitlab-ci-config-for-new-runner-2
  • add-exercise-util-script
  • document-frontend-components
  • grady-exam
  • jakob.dieterle-master-patch-13835
  • master
  • parallel-test
  • test-233-branch-remove-examtype-foreign-key-on-group
  • update-export-dialogs
  • 0.0.1
  • 0.1
  • 0.2
  • 0.3
  • 0.4
  • 0.4.1
  • 0.4.2
  • 0.5.0
  • 0.5.1
  • 1.0.0
  • 1.1.0
  • 2.0.0
  • 2.0.1
  • 2.1.0
  • 2.1.1
  • 2.2.0
  • 3.0.0
  • 3.0.1
  • 4.0.0
  • 4.1.0
  • 4.2.0
  • 4.3.0
  • 4.4.0
  • 4.4.1
  • 5.0.0
  • 5.0.1
  • 5.1.0
  • 5.1.1
  • 5.1.2
  • 5.1.3
  • 5.1.4
  • 5.1.5
  • 5.1.6
  • 5.1.7
  • 5.2.0
  • 5.3.0
  • 5.3.1
  • 5.3.2
  • 5.4.0
  • 5.4.1
  • 5.4.2
  • 6.0.0
  • 6.1.0
  • legacy
70 results
Show changes
Commits on Source (10)
Showing
with 627 additions and 461 deletions
......@@ -27,7 +27,7 @@ cache:
# ========================== Build Testing section =========================== #
build_test_env:
image: python:3.6
image: python:3.8
stage: build
script:
- pip install pipenv
......@@ -93,7 +93,7 @@ build_test_image:
# ============================== Testing section ============================= #
# ----------------------------- Backend subsection --------------------------- #
.test_template_virtualenv: &test_definition_virtualenv
image: python:3.6
image: python:3.8
before_script:
- pip install pipenv
- VENV=$(pipenv --venv)
......@@ -112,7 +112,7 @@ test_pytest:
<<: *test_definition_virtualenv
stage: test
services:
- postgres:9.6
- postgres:13
script:
- pytest --cov --ds=grady.settings.test core/tests
artifacts:
......@@ -152,7 +152,7 @@ test_frontend:
<<: *test_definition_frontend
stage: test
services:
- postgres:9.6
- postgres:13
script:
- cp frontend/dist/index.html core/templates
- python util/format_index.py
......
......@@ -9,23 +9,23 @@ RUN yarn
COPY frontend/ .
RUN yarn build
FROM python:3.6-alpine
FROM alpine:edge
WORKDIR /code
# This set is needed otherwise the postgres driver wont work
RUN apk update \
&& apk add --virtual build-deps gcc python3-dev musl-dev curl \
&& apk add --no-cache postgresql-dev
&& apk add build-base gcc curl libzmq musl-dev zeromq-dev python3 python3-dev py3-pip \
&& apk add --no-cache postgresql-dev git
RUN apk add --no-cache git
# Create symlink for python
RUN ln -sf python3 /usr/bin/python
RUN pip install pipenv
COPY Pipfile .
COPY Pipfile.lock .
RUN pipenv install --system --deploy && rm -rf /root/.cache
# CACHED
COPY . .
COPY --from=node /app/dist /code/frontend/dist
......@@ -34,6 +34,8 @@ COPY --from=node /app/dist/index.html /code/core/templates/index.html
ENV PYTHONUNBUFFERED 1
RUN python util/format_index.py
RUN python manage.py collectstatic --noinput
RUN apk del build-deps
# Reduces image size
RUN apk del build-base musl-dev python3-dev zeromq-dev
CMD ["./deploy.sh"]
APP_LIST ?= core grady util
DB_NAME = postgres
.PHONY: run install migrations-check isort isort-check test
.ONESHELL:
.PHONY: run install migrations-check isort isort-check test teste2e
run:
python manage.py runserver 0.0.0.0:8000
......@@ -21,16 +23,21 @@ migrate:
test:
pytest --ds=grady.settings core/tests
teste2e:
cd frontend && yarn build && cp dist/index.html ../core/templates && cd .. && python util/format_index.py && python manage.py collectstatic --no-input && HEADLESS_TESTS=$(headless) pytest --ds=grady.settings $(path); git checkout core/templates/index.html
teste2e-nc:
cp frontend/dist/index.html ./core/templates && python util/format_index.py && python manage.py collectstatic --no-input && HEADLESS_TESTS=$(headless) pytest --ds=grady.settings $(path); git checkout core/templates/index.html
frontend/dist: $(shell find frontend/src -type f)
yarn --cwd frontend build
teste2e: frontend/dist
set -e
cp frontend/dist/index.html core/templates
trap "git checkout core/templates/index.html" EXIT
python util/format_index.py
python manage.py collectstatic --no-input
HEADLESS_TESTS=$(headless) pytest --ds=grady.settings $(path)
coverage:
set -e
DJANGO_SETTINGS_MODULE=grady.settings pytest --cov
coverage html
db:
docker run -d --name $(DB_NAME) -p 5432:5432 postgres:9.5
docker run -d --name $(DB_NAME) -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:13
......@@ -4,37 +4,37 @@ url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
flake8 = "~=3.6.0"
flake8 = "~=3.8.3"
pre-commit = "~=1.13.0"
pytest = "~=4.4"
pytest-cov = "~=2.6.0"
pytest-xdist = "~=1.29"
pytest-django = "~=3.5.0"
pytest = "~=6.1.0"
pytest-cov = "~=2.10.1"
pytest-xdist = "~=2.1.0"
pytest-django = "~=3.10.0"
selenium = "~=3.141.0"
factory-boy = "~=2.11.0"
Faker = "~=1.0.0"
factory-boy = "~=3.0.1"
Faker = "~=4.1.3"
[packages]
django-cors-headers = "~=2.4.0"
django-extensions = "~=2.1"
pyzmq = "~=19.0.2"
django-cors-headers = "~=3.5.0"
django-extensions = "~=3.0.9"
djangorestframework-jwt = "~=1.11.0"
djangorestframework = ">=3.9,<3.10"
django-silk = "~=4.0.1"
djangorestframework = "~=3.11.0"
django-silk = "~=4.1.0"
djangorestframework-camel-case = {git = "https://gitlab.gwdg.de/grady-corp/djangorestframework-camel-case.git"}
drf-yasg = "~=1.12.0"
gunicorn = "~=19.9.0"
psycopg2-binary = "~=2.7.0"
python-json-logger = "~=0.1.0"
whitenoise = "~=4.1.0"
drf-yasg = "~=1.17.1"
gunicorn = "~=20.0.4"
psycopg2-binary = "~=2.8.6"
whitenoise = "~=5.2.0"
xlrd = "~=1.2.0"
xkcdpass = "==1.17.0"
django-constance = {extras = ["database"],version = "~=2.3.1"}
semver = "~=2.8.1"
Django = "<3.0,>=2.2"
sentry-sdk = "==0.11.2"
nbformat = "~=4.4.0"
nbconvert = "~=5.6.0"
JSON-log-formatter = "~=0.3.0"
xkcdpass = "~=1.17.3"
django-constance = {extras = ["database"],version = "~=2.7.0"}
semver = "~=2.10.2"
Django = "~=3.1.1"
nbformat = "~=5.0.7"
nbconvert = "~=6.0.6"
[requires]
python_version = "3.6"
python_version = "3.8"
This diff is collapsed.
......@@ -62,7 +62,7 @@ installed automatically during the installation process.
To set up a new development instance perform the following steps:
1. Create a virtual environment with a Python3.6 interpreter and install
1. Create a virtual environment with a Python3.6 interpreter and install
all relevant dependencies:
```shell script
......@@ -82,7 +82,7 @@ pipenv shell
4. Set up a Postgres 9.5 database. If you have docker installed the
easiest way is to just run it in a docker container, like this:
```shell script
docker run -d --rm --name postgres -p 5432:5432 postgres:9.5
docker run -d --rm --name postgres -p 5432:5432 postgres:13
```
......@@ -137,31 +137,31 @@ make teste2e path=functional_tests headless=True
for headless mode (Note: You might need to install additional dependencies).
make teste2e
Notice that this will always issue a complete rebuild of the frontend. If you want to run tests without building the
frontend anew, use
make teste2e-nc
## Production
In order to run the app in production, a server with
[Docker](https://www.docker.com/) is needed. To make routing to the
In order to run the app in production, a server with
[Docker](https://www.docker.com/) is needed. To make routing to the
respective instances easier, we recommend running [traefik](https://traefik.io/)
as a reverse proxy on the server. For easier configuration of the containers
we recommend using `docker-compose`. The following guide will assume both these
dependencies are available.
### Setting up a new instance
Simply copy the following `docker-compose.yml` onto your production server:
Simply copy the following `docker-compose.yml` onto your production server:
```yaml
version: "3"
services:
postgres:
image: postgres:9.6
image: postgres:13
labels:
traefik.enable: "false"
networks:
......@@ -198,14 +198,14 @@ networks:
external: false
```
and set the `INSTANCE`, `URLPATH`, `GRADY_HOST` variables either directly in the
and set the `INSTANCE`, `URLPATH`, `GRADY_HOST` variables either directly in the
compose file or within an `.env` file in the same directory as the `docker-compose.yml`
(it will be automatically loaded by `docker-compose`).
(it will be automatically loaded by `docker-compose`).
Login to gwdg gitlab docker registry by entering:
```commandline
docker login docker.gitlab.gwdg.de
```
Running
Running
```commandline
docker-compose pull
docker-compose up -d
......@@ -214,17 +214,17 @@ will download the latest postgres and grady images and run them in the backgroun
### Importing exam data
#### Exam data structure
In order to import the exam data it must be in a specific format.
In order to import the exam data it must be in a specific format.
You need the following:
1. A .json file file containing the output of the converted ILIAS export which is
generated by [hektor](https://gitlab.gwdg.de/j.michal/hektor)
2. A plain text file containing one username per line. A new **reviewer** account
2. A plain text file containing one username per line. A new **reviewer** account
will be created with the corresponding username and a randomly
generated password. The passwords are written to a `.importer_passwords` file.
This step should not be skipped because a reviewer account is necessary in order
generated password. The passwords are written to a `.importer_passwords` file.
This step should not be skipped because a reviewer account is necessary in order
to activate the tutor accounts.
#### Importing exam data
In order to create reviewer accounts, open an interactive shell session in the running container:
......
# Generated by Django 2.2.16 on 2020-09-29 12:02
import core.models.user_account
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_feedback_modified'),
]
operations = [
migrations.AlterField(
model_name='useraccount',
name='exercise_groups',
field=models.ManyToManyField(blank=True, default=core.models.user_account.group_default, related_name='users', to='core.Group'),
),
]
# Generated by Django 3.1.2 on 2020-10-27 12:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0005_auto_20200929_1202'),
]
operations = [
migrations.AlterField(
model_name='useraccount',
name='first_name',
field=models.CharField(blank=True, max_length=150, verbose_name='first name'),
),
]
from rest_framework.test import APIClient, APITestCase
import pytest
import os
from rest_framework.test import APIClient, APITestCase
from constance.test import override_config
from core.models import UserAccount
......@@ -22,3 +25,47 @@ class AuthTests(APITestCase):
token = self.client.post('/api/get-token/', self.credentials).data
response = self.client.post('/api/refresh-token/', token)
self.assertContains(response, 'token')
@override_config(REGISTRATION_PASSWORD='pw')
def test_registration_correct_password(self):
credentials = {
'username': 'john-doe',
'password': 'safeandsound',
'registration_password': 'pw',
}
response = self.client.post('/api/corrector/register/', credentials)
self.assertEqual(201, response.status_code)
@override_config(REGISTRATION_PASSWORD='wrong_pw')
def test_registration_wrong_password(self):
credentials = {
'username': 'john-doe',
'password': 'safeandsound',
'registration_password': 'pw',
}
response = self.client.post('/api/corrector/register/', credentials)
self.assertEqual(403, response.status_code)
@pytest.mark.skipif(os.environ.get('DJANGO_DEV', False),
reason="No password strengths checks in dev")
@override_config(REGISTRATION_PASSWORD='pw')
def test_password_is_strong_enough(self):
response = self.client.post('/api/corrector/register/', {
'username': 'hans',
'password': 'weak',
'registration_password': 'pw',
})
self.assertEqual(400, response.status_code)
self.assertIn('password', response.data)
@override_config(REGISTRATION_PASSWORD='pw')
def test_cannot_register_active(self):
response = self.client.post('/api/corrector/register/', {
'username': 'hans',
'password': 'safeandsound',
'registration_password': 'pw',
'is_active': True
})
self.assertEqual(403, response.status_code)
......@@ -22,10 +22,10 @@ class ConfigurationViewTestCase(APITestCase):
def setUp(self):
self.client.force_authenticate(user=self.reviewer)
self.rev_list_response = self.client.get(f'/api/config/')
self.rev_list_response = self.client.get('/api/config/')
self.client.force_authenticate(user=self.student)
self.stud_list_response = self.client.get(f'/api/config/')
self.stud_list_response = self.client.get('/api/config/')
stud_patch_data = {
"singleCorrection": True,
......@@ -37,10 +37,10 @@ class ConfigurationViewTestCase(APITestCase):
}
self.client.force_authenticate(user=self.reviewer)
self.rev_patch_response = self.client.patch(f'/api/config/change_config/', rev_patch_data)
self.rev_patch_response = self.client.patch('/api/config/change_config/', rev_patch_data)
self.client.force_authenticate(user=self.student)
self.stud_patch_response = self.client.patch(f'/api/config/change_config/', stud_patch_data)
self.stud_patch_response = self.client.patch('/api/config/change_config/', stud_patch_data)
def test_student_can_access(self):
self.assertEqual(status.HTTP_200_OK, self.stud_list_response.status_code)
......
......@@ -104,7 +104,7 @@ class ExportInstanceTest(APITestCase):
# submissionTypes fields
self.assertIn('submissionTypes', instance)
self.assertEquals(2, len(instance['submissionTypes']))
self.assertEqual(2, len(instance['submissionTypes']))
self.assertIn('pk', instance['submissionTypes'][0])
self.assertEqual('01. Sort', instance['submissionTypes'][0]['name'])
self.assertEqual(35, instance['submissionTypes'][0]['fullScore'])
......
......@@ -5,12 +5,11 @@
* GET /tutorlist list of all tutors with their scores
"""
from django.contrib.auth import get_user_model
import pytest
from constance.test import override_config
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.test import (APIClient, APIRequestFactory, APITestCase,
force_authenticate)
import os
from core.models import Feedback, TutorSubmissionAssignment
from core.views import CorrectorApiViewSet
......@@ -221,38 +220,12 @@ class TutorRegisterTests(APITestCase):
self.reviewer = self.user_factory.make_reviewer()
self.client = APIClient()
@pytest.mark.skipif(os.environ.get('DJANGO_DEV', False),
reason="No password strengths checks in dev")
def test_password_is_strong_enough(self):
response = self.client.post('/api/corrector/register/', {
'username': 'hans',
'password': 'weak'
})
self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code)
self.assertIn('password', response.data)
def test_anonymous_can_request_access(self):
response = self.client.post('/api/corrector/register/', {
'username': 'hans',
'password': 'safeandsound'
})
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
def test_cannot_register_active(self):
response = self.client.post('/api/corrector/register/', {
'username': 'hans',
'password': 'safeandsound',
'is_active': True
})
self.assertEqual(status.HTTP_403_FORBIDDEN, response.status_code)
@override_config(REGISTRATION_PASSWORD='pw')
def test_reviewer_can_activate_tutor(self):
response = self.client.post('/api/corrector/register/', {
'username': 'hans',
'password': 'safeandsound'
'password': 'safeandsound',
'registration_password': 'pw',
})
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
......
......@@ -11,21 +11,21 @@ from core import views
router = DefaultRouter()
router.register('student', views.StudentReviewerApiViewSet,
basename='student')
router.register('examtype', views.ExamApiViewSet)
router.register('feedback', views.FeedbackApiView)
router.register('feedback-comment', views.FeedbackCommentApiView)
router.register('examtype', views.ExamApiViewSet, basename='examtype')
router.register('feedback', views.FeedbackApiView, basename='feedback')
router.register('feedback-comment', views.FeedbackCommentApiView, basename='feedback-comment')
router.register('submission', views.SubmissionViewSet,
basename='submission')
router.register('submissiontype', views.SubmissionTypeApiView)
router.register('submissiontype', views.SubmissionTypeApiView, basename='submissiontype')
router.register('corrector', views.CorrectorApiViewSet, basename='corrector')
router.register('assignment', views.AssignmentApiViewSet)
router.register('assignment', views.AssignmentApiViewSet, basename='assignment')
router.register('statistics', views.StatisticsEndpoint, basename='statistics')
router.register('user', views.UserAccountViewSet, basename='user')
router.register('label', views.LabelApiViewSet, basename='label')
router.register('label-statistics', views.LabelStatistics, basename='label-statistics')
router.register('solution-comment', views.SolutionCommentApiViewSet, basename='solution-comment')
router.register('group', views.GroupApiViewSet, basename='group')
router.register('config', views.InstanceConfigurationViewSet, base_name='config')
router.register('config', views.InstanceConfigurationViewSet, basename='config')
schema_view = get_schema_view(
openapi.Info(
......
......@@ -21,15 +21,15 @@ class AssignmentApiViewSet(
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet):
queryset = TutorSubmissionAssignment.objects.all()
serializer_class = AssignmentSerializer
permission_classes = (IsTutorOrReviewer, )
def get_queryset(self):
base_queryset = TutorSubmissionAssignment.objects.all()
if self.action in ['list', 'active', 'destroy']:
return self.queryset.all()
return base_queryset.all()
else:
return self.queryset.filter(owner=self.request.user)
return base_queryset.filter(owner=self.request.user)
def _fetch_assignment(self, serializer):
try:
......
......@@ -131,9 +131,14 @@ class CorrectorApiViewSet(
def register(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
if serializer.validated_data.get('is_active', False):
raise PermissionDenied(detail='Cannot be created active')
registration_password = request.data.get('registration_password', None)
if registration_password is None or registration_password != config.REGISTRATION_PASSWORD:
raise PermissionDenied(detail='Invalid registration password')
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
......@@ -225,13 +230,6 @@ class StatisticsEndpoint(viewsets.ViewSet):
class SubmissionViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = (IsTutorOrReviewer,)
queryset = models.Submission.objects \
.select_related('type') \
.select_related('feedback') \
.prefetch_related('tests') \
.prefetch_related('feedback__feedback_lines') \
.prefetch_related('feedback__feedback_lines__of_tutor') \
.all()
def get_serializer_class(self):
if self.request.user.is_reviewer() or config.EXERCISE_MODE:
......@@ -242,15 +240,23 @@ class SubmissionViewSet(viewsets.ReadOnlyModelViewSet):
return SubmissionNoTypeSerializer
def get_queryset(self):
base_queryset = models.Submission.objects \
.select_related('type') \
.select_related('feedback') \
.prefetch_related('tests') \
.prefetch_related('feedback__feedback_lines') \
.prefetch_related('feedback__feedback_lines__of_tutor') \
.all()
if self.request.user.is_reviewer() \
or (self.request.user.is_tutor() and config.EXERCISE_MODE):
return self.queryset
return base_queryset
elif self.request.user.is_student():
return self.queryset.filter(
return base_queryset.filter(
student__user=self.request.user
)
else:
return self.queryset.filter(
return base_queryset.filter(
assignments__owner=self.request.user
)
......
......@@ -21,12 +21,6 @@ class FeedbackApiView(
viewsets.GenericViewSet):
""" Gets a list of an individual exam by Id if provided """
permission_classes = (permissions.IsTutorOrReviewer,)
queryset = models.Feedback.objects \
.select_related('of_submission') \
.select_related('of_submission__type') \
.select_related('of_submission__student') \
.select_related('of_submission__student__user') \
.all()
lookup_field = 'of_submission__pk'
lookup_url_kwarg = 'submission_pk'
......@@ -46,19 +40,26 @@ class FeedbackApiView(
return serializers.FeedbackSerializer
def get_queryset(self):
base_queryset = models.Feedback.objects \
.select_related('of_submission') \
.select_related('of_submission__type') \
.select_related('of_submission__student') \
.select_related('of_submission__student__user') \
.all()
if self.request.user.is_reviewer():
return self.queryset \
return base_queryset \
.prefetch_related('feedback_lines') \
.prefetch_related('feedback_lines__of_tutor') \
.all()
user_groups = self.request.user.exercise_groups.all()
if self.request.user.is_tutor() and config.EXERCISE_MODE:
return self.queryset.filter(
return base_queryset.filter(
of_submission__student__user__exercise_groups__in=user_groups
)
return self.queryset.filter(
return base_queryset.filter(
of_submission__assignments__owner=self.request.user
)
......@@ -100,14 +101,15 @@ class FeedbackCommentApiView(
viewsets.GenericViewSet):
""" Gets a list of an individual exam by Id if provided """
permission_classes = (permissions.IsTutorOrReviewer,)
queryset = models.FeedbackComment.objects.all()
serializer_class = serializers.FeedbackCommentSerializer
def get_queryset(self):
base_queryset = models.FeedbackComment.objects.all()
user = self.request.user
if user.role == models.UserAccount.REVIEWER:
return self.queryset
return self.queryset.filter(of_tutor=user)
return base_queryset
return base_queryset.filter(of_tutor=user)
def destroy(self, request, *args, **kwargs):
with Lock():
......
......@@ -3,7 +3,7 @@ version: '3'
services:
postgres:
image: postgres:9.6
image: postgres:13
restart: always
networks:
- default
......
......@@ -16,6 +16,7 @@ module.exports = {
'default-case': 'error',
'guard-for-in': 'error',
'yoda': 'error',
'no-trailing-spaces': 'error',
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'as' }]
},
parserOptions: {
......
......@@ -12,8 +12,6 @@
"test:unit": "vue-cli-service test:unit --require mock-local-storage"
},
"dependencies": {
"@sentry/browser": "^5.6.3",
"@sentry/integrations": "^5.6.1",
"axios": "^0.18.0",
"file-saver": "^2.0.2",
"highlight.js": "^9.12.0",
......
<template>
<div>
<p><strong>Allgemeiner Hinweis und Pflichtinformationen</strong></p>
<p><strong>Benennung der verantwortlichen Stelle</strong></p>
<h4>Allgemeiner Hinweis und Pflichtinformationen</h4>
<p />
<h4>Benennung der verantwortlichen Stelle</h4>
<p>Die verantwortliche Stelle für die Datenverarbeitung auf dieser Website ist:</p>
<p>
<span id="s3-t-firma">Institut für Informatik - Georg-August-Universität Göttingen</span><br>
<span id="s3-t-ansprechpartner">Dr. Henrik Brosenne</span><br><span id="s3-t-strasse">Goldschmidtstraße 7</span><br>
<span id="s3-t-plz">37077</span> <span id="s3-t-ort">Göttingen</span>
</p><p />
</p>
<p>
Die verantwortliche Stelle entscheidet allein oder gemeinsam mit anderen über die Zwecke und Mittel der
Verarbeitung von personenbezogenen Daten (z.B. Namen, Kontaktdaten o. Ä.).
</p>
<p><strong>Recht auf Beschwerde bei der zuständigen Aufsichtsbehörde</strong></p>
<h4>Recht auf Beschwerde bei der zuständigen Aufsichtsbehörde</h4>
<p>
Als Betroffener steht Ihnen im Falle eines datenschutzrechtlichen Verstoßes ein Beschwerderecht bei der zuständigen
Aufsichtsbehörde zu. Zuständige Aufsichtsbehörde bezüglich datenschutzrechtlicher Fragen ist:<br>
Aufsichtsbehörde zu. Zuständige Aufsichtsbehörde bezüglich datenschutzrechtlicher Fragen ist:
</p>
<p>
Die Landesbeauftragte für den Datenschutz Niedersachsen<br>
Prinzenstraße 5<br>
30159 Hannover<br>
</p><p><strong>Recht auf Datenübertragbarkeit</strong></p>
</p>
<h4>Recht auf Datenübertragbarkeit</h4>
<p>
Ihnen steht das Recht zu, Daten, die wir auf Grundlage der Erfüllung eines Vertrags
automatisiert verarbeiten, an sich oder an Dritte aushändigen zu lassen. Die Bereitstellung erfolgt in einem
maschinenlesbaren Format. Sofern Sie die direkte Übertragung der Daten an einen anderen Verantwortlichen verlangen, erfolgt dies nur, soweit es technisch machbar ist.
</p>
<p><strong>Recht auf Auskunft, Berichtigung, Sperrung, Löschung</strong></p>
<h4>Recht auf Auskunft, Berichtigung, Sperrung, Löschung</h4>
<p>
Sie haben jederzeit im Rahmen der geltenden gesetzlichen Bestimmungen das Recht auf unentgeltliche Auskunft über
Ihre gespeicherten personenbezogenen Daten, Herkunft der Daten, deren Empfänger und den Zweck der
......@@ -35,7 +41,7 @@
auch zu weiteren Fragen zum Thema personenbezogene Daten können Sie sich jederzeit über die im Impressum aufgeführten Kontaktmöglichkeiten an uns wenden.
</p>
<p><strong>SSL- bzw. TLS-Verschlüsselung</strong></p>
<h4>SSL- bzw. TLS-Verschlüsselung</h4>
<p>
Aus Sicherheitsgründen und zum Schutz der Übertragung vertraulicher Inhalte, die Sie an uns als Seitenbetreiber
senden, nutzt unsere Website eine SSL-bzw. TLS-Verschlüsselung. Damit sind Daten, die Sie über diese Website
......@@ -43,35 +49,39 @@
Adresszeile Ihres Browsers und am Schloss-Symbol in der Browserzeile.
</p>
<p><strong>Datenschutzbeauftragter</strong></p>
<h4>Datenschutzbeauftragter</h4>
<p>Wir haben einen Datenschutzbeauftragten bestellt.</p>
Datenschutzbeauftragter der Universität<br>
Prof. Dr. Andreas Wiebe<br>
Lehrstuhl für Bürgerliches Recht, Wettbewerbs- und Immaterialgüterrecht, Medien- und Informationsrecht<br>
Platz der Göttinger Sieben 6<br>
37073 Göttingen<br>
Tel.: 0551 39 - 7381<br>
Fax: 0551 39 - 4437<br>
E-Mail: lehrstuhl.wiebe@jura.uni-goettingen.de<br>
<p>
Datenschutzbeauftragter der Universität<br>
Prof. Dr. Andreas Wiebe<br>
Lehrstuhl für Bürgerliches Recht, Wettbewerbs- und Immaterialgüterrecht, Medien- und Informationsrecht<br>
Platz der Göttinger Sieben 6<br>
37073 Göttingen<br>
Tel.: 0551 39 - 7381<br>
Fax: 0551 39 - 4437<br>
E-Mail: lehrstuhl.wiebe@jura.uni-goettingen.de
</p>
<p><strong>Server-Log-Dateien</strong></p>
<h4>Server-Log-Dateien</h4>
<p>Der Provider der Website erhebt automatisch Informationen, die Ihr Browser automatisch an uns übermittelt. Dies sind:</p>
<ul>
<li>Besuchte Seite auf unserer Domain</li>
<li>Datum und Uhrzeit der Serveranfrage</li>
<li>Browsertyp und Browserversion</li>
<li>Verwendetes Betriebssystem</li>
<li>Referrer URL</li>
<li>Hostname des zugreifenden Rechners</li>
<li>IP-Adresse</li>
</ul>
<p>
<ul>
<li>Besuchte Seite auf unserer Domain</li>
<li>Datum und Uhrzeit der Serveranfrage</li>
<li>Browsertyp und Browserversion</li>
<li>Verwendetes Betriebssystem</li>
<li>Referrer URL</li>
<li>Hostname des zugreifenden Rechners</li>
<li>IP-Adresse</li>
</ul>
</p>
<p>
Es findet keine Zusammenführung dieser Daten mit anderen Datenquellen statt. Grundlage der Datenverarbeitung
bildet Art. 6 Abs. 1 lit. b DSGVO, der die Verarbeitung von Daten zur Erfüllung eines Vertrags oder
vorvertraglicher Maßnahmen gestattet.
</p>
<p><strong>Registrierung auf dieser Website</strong></p>
<h4>Registrierung auf dieser Website</h4>
<p>
Zur Nutzung bestimmter Funktionen müssen Sie sich auf unserer Website registrieren. Die übermittelten Daten
dienen ausschließlich zum Zwecke der Nutzung des jeweiligen Angebotes oder Dienstes. Bei der Registrierung
......@@ -84,7 +94,7 @@
Gesetzliche Aufbewahrungsfristen bleiben unberührt.
</p>
<p><strong>Speicherdauer von Beiträgen und Kommentaren</strong></p>
<h4>Speicherdauer von Beiträgen und Kommentaren</h4>
<p>
Beiträge und Kommentare sowie damit in Verbindung stehende Daten, wie beispielsweise der Benutzername,
werden gespeichert. Der Inhalt verbleibt auf unserer Website, bis er vollständig gelöscht wurde oder aus
......@@ -95,13 +105,13 @@
Arbeitsvertrages.
</p>
<p><strong>Session storage</strong></p>
<h4>Session storage</h4>
<p>
Unsere Website verwendet den Session storage des Browsers. In diesem werden für die Dauer einer Sitzun (Session)
Daten auf Ihrem Endgerät gespeichert.
</p>
<p><strong>Google Web Fonts</strong></p>
<h4>Google Web Fonts</h4>
<p>Unsere Website verwendet Web Fonts von Google. Anbieter ist die Google Inc., 1600 Amphitheatre Parkway, Mountain View, CA 94043, USA.</p>
<p>
Durch den Einsatz dieser Web Fonts wird es möglich Ihnen die von uns gewünschte Darstellung unserer Website
......