Gitlab Community Edition Instance

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

Target

Select target project
  • j.michal/grady
1 result
Select Git revision
Show changes
Commits on Source (7)
Showing
with 677 additions and 489 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)
......
......@@ -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.
# 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'),
),
]
......@@ -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'])
......
......@@ -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
......
<template>
<v-dialog
v-model="show"
width="30%"
max-width="400"
>
<v-card>
<v-card-title class="title">
Change your password
</v-card-title>
<v-card-text>
<v-form class="mx-4">
<v-form
ref="form"
v-model="formIsValid"
lazy-validation
@submit.prevent="submitChange"
>
<v-card-title class="title">
Change your password
</v-card-title>
<v-card-text>
<v-text-field
v-model="currentPassword"
label="Current password"
type="password"
autofocus
required
:error-messages="oldPasswordRejected ? 'Wrong password.' : undefined"
:rules="[ rules.required, rules.oldPasswordCorrect ]"
@input="oldPasswordRejected = false"
/>
<v-text-field
v-model="newPassword"
label="New password"
type="password"
required
:error-messages="newPasswordErrors"
:rules="[ rules.required ]"
@input="newPasswordErrors = null"
/>
<v-text-field
v-model="newPasswordRepeated"
label="Repeat new password"
type="password"
:error-messages="errorMessageRepeat"
required
:rules="[ rules.required, rules.matchesPassword ]"
/>
</v-form>
</v-card-text>
<v-card-actions>
<v-btn
:disabled="!allowChange"
@click="submitChange"
>
Change password
</v-btn>
<v-btn
color="red"
@click="$emit('hide')"
>
Cancel
</v-btn>
</v-card-actions>
<v-alert
type="error"
:value="errorAlert"
>
{{ errorAlert }}
</v-alert>
</v-card-text>
<v-card-actions class="justify-end">
<v-btn
flat
color="primary"
@click="$emit('hide')"
>
Cancel
</v-btn>
<v-btn
:disabled="!formIsValid"
flat
color="primary"
type="submit"
>
Change password
</v-btn>
</v-card-actions>
</v-form>
</v-card>
</v-dialog>
</template>
......@@ -53,6 +73,7 @@
import { mapState } from 'vuex'
import { changePassword } from '@/api'
import { Authentication } from '@/store/modules/authentication'
import { required } from '@/util/form-rules'
export default {
name: 'PasswordChangeDialog',
......@@ -61,34 +82,36 @@ export default {
show: true,
currentPassword: '',
newPassword: '',
newPasswordRepeated: ''
newPasswordRepeated: '',
formIsValid: false,
rules: {
required,
matchesPassword: v => v === this.newPassword || 'Passwords do not match.',
},
errorAlert: null,
oldPasswordRejected: false,
newPasswordErrors: null
}
},
computed: {
userPk () { return Authentication.state.user.pk },
equalNewPasswords () {
return this.newPassword === this.newPasswordRepeated
},
allowChange () {
return this.equalNewPasswords && !!this.currentPassword
},
errorMessageRepeat () {
if (!this.equalNewPasswords) {
return 'Repeated new password is different than new one'
}
return ''
}
userPk () { return Authentication.state.user.pk }
},
watch: {
show (val) {
if (!val) {
this.$emit('hide')
}
},
newPassword () {
if (this.newPasswordRepeated !== '')
this.$refs.form.validate()
}
},
methods: {
submitChange () {
if (!this.$refs.form.validate())
return
const data = {
oldPassword: this.currentPassword,
newPassword: this.newPassword
......@@ -100,17 +123,19 @@ export default {
type: 'success'
})
this.$emit('hide')
}).catch(() => {
let reasons = ''
if (error.response) {
reasons = error.response.data.password.map(reason => `- ${reason}`).join('<br/>')
}
this.$notify({
title: 'Error!',
text: `Unable to change password<br/>${reasons}`,
type: 'error',
duration: -1
})
}).catch(error => {
if (error.response && error.response.status === 401) {
this.oldPasswordRejected = true
this.$refs.form.validate()
return
}
if (error.response && error.response.status === 406) {
this.newPasswordErrors = error.response.data.new_password
return
}
this.errorAlert = error.toString()
})
}
}
......
......@@ -3,12 +3,16 @@
<v-card-title class="title">
Datenschutzerklärung
</v-card-title>
<v-card-text>
<v-divider />
<v-card-text class="content">
<GDPRNotice id="gdpr-notice" />
</v-card-text>
<v-card-actions>
<v-divider />
<v-card-actions class="justify-end">
<v-btn
id="accept-gdpr-notice"
color="primary"
flat
@click="acceptedGDPR = true"
>
Einwilligen
......@@ -16,41 +20,60 @@
</v-card-actions>
</v-card>
<v-card v-else>
<v-card-title class="title">
Register
</v-card-title>
<v-card-text>
<v-text-field
id="input-register-username"
v-model="credentials.username"
label="Username"
required
autofocus
/>
<v-text-field
id="input-register-password"
v-model="credentials.password"
label="Password"
required
type="password"
/>
</v-card-text>
<v-card-actions class="justify-center">
<v-btn
id="register-submit"
flat
:loading="loading"
@click="register"
>
submit
</v-btn>
</v-card-actions>
<v-form
ref="registrationForm"
v-model="registrationFormIsValid"
lazy-validation
@submit.prevent="register"
>
<v-card-title class="title">
Register
</v-card-title>
<v-card-text>
<v-text-field
id="input-register-username"
v-model="credentials.username"
label="Username"
required
:error-messages="usernameErrors"
:rules="[ required ]"
autofocus
@input="usernameErrors = null"
/>
<v-text-field
id="input-register-password"
v-model="credentials.password"
label="Password"
required
:rules="[ required ]"
type="password"
/>
<v-alert
type="error"
:value="errorAlert"
>
{{ errorAlert }}
</v-alert>
</v-card-text>
<v-card-actions class="justify-center">
<v-btn
id="register-submit"
flat
:loading="loading"
:disabled="!registrationFormIsValid"
type="submit"
>
submit
</v-btn>
</v-card-actions>
</v-form>
</v-card>
</template>
<script>
import { registerTutor } from '@/api'
import GDPRNotice from '@/components/GDPRNotice'
import { required } from '@/util/form-rules'
export default {
name: 'RegisterDialog',
......@@ -62,26 +85,26 @@ export default {
password: ''
},
loading: false,
acceptedGDPR: false
acceptedGDPR: false,
registrationFormIsValid: false,
required: required,
errorAlert: null,
usernameErrors: null,
}
},
methods: {
register () {
if (!this.$refs.registrationForm.validate())
return
this.loading = true
registerTutor(this.credentials).then(() => {
this.$emit('registered', this.credentials)
}).catch(error => {
let reasons = ''
if (error.response) {
reasons = error.response.data.password.map(reason => `- ${reason}`).join('<br/>')
}
console.log(reasons)
this.$notify({
title: 'Unable to register',
text: `Couldn't register a tutor account.<br/>${reasons}`,
type: 'error',
duration: -1
})
if (error.response && error.response.data && error.response.data.username)
this.usernameErrors = error.response.data.username
else
this.errorAlert = `Couldn't register a tutor account: ${error}`
}).finally(() => { this.loading = false })
}
}
......@@ -89,5 +112,7 @@ export default {
</script>
<style scoped>
.content {
height: 50vh;
}
</style>
......@@ -24,7 +24,7 @@ export default class FeedbackLabel extends Vue {
@Prop({ type: String, required: true }) readonly description!: string
@Prop({ type: String, required: true }) readonly colour!: string
@Prop({ type: Boolean, default: false }) readonly removable!: boolean
onClose() {
this.$emit('remove-clicked', this.pk)
}
......
......@@ -119,7 +119,7 @@ export default class FeedbackLabelForm extends Vue {
if (duplicate) {
this.$notify({
title: 'Label creation error',
text: 'A label with the same name already exists. ' +
text: 'A label with the same name already exists. ' +
'You can, however, update the label',
type: 'error',
duration: -1
......
......@@ -133,12 +133,12 @@ export default class LabelSelector extends Vue {
*/
removedFeedbackLabels() {
if (!SubmissionNotes.state.changedLabels) return new Array()
const labelsOrig = SubmissionNotes.state.origFeedback.labels
const labelsUpdated = SubmissionNotes.state.updatedFeedback.labels
if (labelsOrig === undefined) return new Array()
return labelsOrig.filter((label) => {
return !labelsUpdated.includes(label)
})
......@@ -169,7 +169,7 @@ export default class LabelSelector extends Vue {
})
if (!label) return
return {
return {
pk: val,
name: label.name,
description: label.description,
......@@ -189,7 +189,7 @@ export default class LabelSelector extends Vue {
if (!SubmissionNotes.state.changedLabels) {
SubmissionNotes.SET_FEEDBACK_LABELS([...SubmissionNotes.state.origFeedback.labels])
}
SubmissionNotes.REMOVE_FEEDBACK_LABEL(pk)
} else {
this.$emit('label-removed', pk)
......@@ -203,8 +203,8 @@ export default class LabelSelector extends Vue {
*/
addLabel(pk: number) {
if (this.assignedToFeedback) {
if (!this.unchangedFeedbackLabels().includes(pk) &&
!this.addedFeedbackLabels().includes(pk))
if (!this.unchangedFeedbackLabels().includes(pk) &&
!this.addedFeedbackLabels().includes(pk))
{
if (!SubmissionNotes.state.changedLabels) {
SubmissionNotes.SET_FEEDBACK_LABELS([...SubmissionNotes.state.origFeedback.labels])
......
......@@ -44,8 +44,8 @@
</v-icon>
</td>
<td>
<v-btn
icon
<v-btn
icon
:color="props.item.mark"
@click="changeMark(props.item.ofSubmission, props.item.mark)"
/>
......@@ -145,14 +145,14 @@ export default class FeedbackTable extends Vue {
// TODO: it is possible that a user is not assigned to the feedback, but still made a comment on it
// this happens when a reviewer adds a comment to a feedback that he has never corrected himself
// we could either ignore this case or add every user that has made a comment to a feedback
// we could either ignore this case or add every user that has made a comment to a feedback
// to the list of associated users in the filterFeedbackByTutorStage method
/**
* Used to determine wether or not a single Feedback should appear in the history based on
* the tutor and stage selection.
*
*
* Returns true if the given feedback is not filtered and should thus be displayed in the
* history. Returns false if the given feedback should be excluded from the history.
*/
......@@ -190,7 +190,7 @@ export default class FeedbackTable extends Vue {
/**
* Used to determine wether or not a single Feedback should appear in the history based on
* the included and excluded labels filter.
*
*
* Returns true if the given feedback is not filtered and should thus be displayed in the
* history. Returns false if the given feedback should be excluded from the history.
*/
......
......@@ -9,9 +9,9 @@
</v-card-title>
<v-card-text>
<div>
In this section you can configure the running instance.
In this section you can configure the running instance.
Any changes you make are immediately applied, but it is required that every user
logs out and in again to make sure that all users receive the new configuration.
logs out and in again to make sure that all users receive the new configuration.
</div>
<v-layout wrap>
<v-flex
......
......@@ -151,7 +151,7 @@ export default {
const lang = SubmissionNotes.submissionType.programmingLanguage
return lang === SubmissionType.ProgrammingLanguageEnum.Markdown
},
submissionObj () {
submissionObj () {
return this.assignment ? this.assignment.submission : this.submissionWithoutAssignment
},
feedbackObj () {
......