diff --git a/frontend/src/utils/exam-correct/exam-correct.sass b/frontend/src/utils/exam-correct/exam-correct.sass index 6b96b4fe9..8e73bd7f7 100644 --- a/frontend/src/utils/exam-correct/exam-correct.sass +++ b/frontend/src/utils/exam-correct/exam-correct.sass @@ -12,10 +12,28 @@ table[uw-exam-correct] min-width: 200px th.uw-exam-correct--part-cell, td.uw-exam-correct--part-cell width: 85px + text-align: center input width: 70px padding: 4px 8px + td#uw-exam-correct__result + width: min-content + select + width: max-content + min-width: max-content + option + width: max-content + min-width: max-content + td#uw-exam-correct__result__grade + width: min-content + select + width: max-content + min-width: max-content + option + width: max-content + min-width: max-content + [uw-exam-correct] input:invalid:not(.no-value) border: 2px solid var(--color-error) diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index cce2b0a23..a05068330 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -1258,6 +1258,7 @@ BreadcrumbTutorInvite: Einladung zum Tutor BreadcrumbExamCorrectorInvite: Einladung zum Prüfungskorrektor BreadcrumbExamParticipantInvite: Einladung zum Prüfungsteilnehmer BreadcrumbExamRegister: Anmelden +BreadcrumbExamCorrect: Eintragen von Prüfungsergebnissen BreadcrumbApplicationFiles: Bewerbungsdateien BreadcrumbCourseNewsArchive: Archiv BreadcrumbCourseNewsFile: Datei diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index 4f7c2b67c..88f3ed764 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -1257,6 +1257,7 @@ BreadcrumbTutorInvite: Invitation to be a tutor BreadcrumbExamCorrectorInvite: Invitation to be an exam corrector BreadcrumbExamParticipantInvite: Invitation to be an exam participant BreadcrumbExamRegister: Register +BreadcrumbExamCorrect: Exam corrections BreadcrumbApplicationFiles: Application files BreadcrumbCourseNewsArchive: Archive BreadcrumbCourseNewsFile: File diff --git a/package.json b/package.json index 52fc3cc13..9b6947436 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "@babel/runtime": "^7.7.6", "@juggle/resize-observer": "^2.5.0", "core-js": "^3.4.8", + "js-cookie": "^2.2.1", "lodash.throttle": "^4.1.1", "js-cookie": "^2.2.1", "moment": "^2.24.0", diff --git a/records.json b/records.json index c46ad95db..d84014b37 100644 --- a/records.json +++ b/records.json @@ -1,5 +1,5 @@ { - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/app.sass": [ + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/app.scss": [ { "modules": { "byIdentifier": {}, @@ -12,7 +12,512 @@ } } ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/asidenav/asidenav.sass": [ + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/alerts/alerts.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/async-form/async-form.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/asidenav/asidenav.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/async-table/async-table-filter.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/async-table/async-table.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/show-hide/show-hide.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/form/form.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/inputs/inputs.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/inputs/radio.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/mass-input/mass-input.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/hide-columns/hide-columns.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/navbar/navbar.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/course-teaser/course-teaser.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/tooltips/tooltips.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/modal/modal.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/inputs/file-input.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/sass-loader/dist/cjs.js??ref--6-3!frontend/src/utils/inputs/checkbox.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--5-1!node_modules/postcss-loader/src/index.js??ref--5-2!frontend/src/utils/form/datepicker.css": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "modules": { + "byIdentifier": { + "multi frontend/src/polyfill.js frontend/src/main.js": 0, + "ignored node_modules/libsodium/dist/modules fs": 1, + "ignored node_modules/readable-stream/lib util": 2, + "ignored node_modules/readable-stream/lib/internal/streams util": 3, + "ignored node_modules/bn.js/lib buffer": 4, + "ignored node_modules/brorand crypto": 5, + "ignored node_modules/sodium-javascript crypto": 1 + }, + "usedIds": { + "0": 0, + "1": 1, + "2": 2, + "3": 3, + "4": 4, + "5": 5 + } + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + }, + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/app.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/alerts/alerts.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/asidenav/asidenav.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/show-hide/show-hide.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/async-form/async-form.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/async-table/async-table-filter.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/async-table/async-table.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/form/form.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/inputs/inputs.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/inputs/radio.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/mass-input/mass-input.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/hide-columns/hide-columns.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/navbar/navbar.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/tooltips/tooltips.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/course-teaser/course-teaser.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/modal/modal.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/inputs/file-input.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/inputs/checkbox.scss": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--5-1!node_modules/postcss-loader/src/index.js??ref--5-2!node_modules/resolve-url-loader/index.js??ref--5-3!frontend/src/utils/form/datepicker.css": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/app.sass": [ { "modules": { "byIdentifier": {}, @@ -38,7 +543,7 @@ } } ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/show-hide/show-hide.sass": [ + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/alerts/alerts.sass": [ { "modules": { "byIdentifier": {}, @@ -51,7 +556,7 @@ } } ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/alerts/alerts.sass": [ + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/asidenav/asidenav.sass": [ { "modules": { "byIdentifier": {}, @@ -77,19 +582,6 @@ } } ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/exam-correct/exam-correct.sass": [ - { - "modules": { - "byIdentifier": {}, - "usedIds": {} - }, - "chunks": { - "byName": {}, - "bySource": {}, - "usedIds": [] - } - } - ], "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/hide-columns/hide-columns.sass": [ { "modules": { @@ -103,19 +595,6 @@ } } ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/pageactions/pageactions.sass": [ - { - "modules": { - "byIdentifier": {}, - "usedIds": {} - }, - "chunks": { - "byName": {}, - "bySource": {}, - "usedIds": [] - } - } - ], "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/navbar/navbar.sass": [ { "modules": { @@ -142,19 +621,6 @@ } } ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/tooltips/tooltips.sass": [ - { - "modules": { - "byIdentifier": {}, - "usedIds": {} - }, - "chunks": { - "byName": {}, - "bySource": {}, - "usedIds": [] - } - } - ], "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/modal/modal.sass": [ { "modules": { @@ -168,6 +634,19 @@ } } ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/tooltips/tooltips.sass": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/mass-input/mass-input.sass": [ { "modules": { @@ -181,32 +660,6 @@ } } ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/async-table/async-table.sass": [ - { - "modules": { - "byIdentifier": {}, - "usedIds": {} - }, - "chunks": { - "byName": {}, - "bySource": {}, - "usedIds": [] - } - } - ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/async-table/async-table-filter.sass": [ - { - "modules": { - "byIdentifier": {}, - "usedIds": {} - }, - "chunks": { - "byName": {}, - "bySource": {}, - "usedIds": [] - } - } - ], "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/inputs/radio.sass": [ { "modules": { @@ -233,6 +686,45 @@ } } ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/show-hide/show-hide.sass": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/async-table/async-table.sass": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/async-table/async-table-filter.sass": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/inputs/file-input.sass": [ { "modules": { @@ -259,7 +751,7 @@ } } ], - "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--5-1!node_modules/postcss-loader/src/index.js??ref--5-2!node_modules/resolve-url-loader/index.js??ref--5-3!frontend/src/utils/form/datepicker.css": [ + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/exam-correct/exam-correct.sass": [ { "modules": { "byIdentifier": {}, @@ -272,19 +764,43 @@ } } ], - "modules": { - "byIdentifier": { - "multi frontend/src/polyfill.js frontend/src/main.js": 0, - "ignored node_modules/sodium-javascript crypto": 1 - }, - "usedIds": { - "0": 0, - "1": 1 + "app-manifest-webpack-plugin for \"/home/gkleen/projects/uni2work/static/wp-4.41/iconstats-[hash].json\"": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } } - }, - "chunks": { - "byName": {}, - "bySource": {}, - "usedIds": [] - } + ], + "app-manifest-webpack-plugin for \"../../config/favicon.json\"": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ], + "mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!node_modules/resolve-url-loader/index.js??ref--6-3!node_modules/sass-loader/dist/cjs.js??ref--6-4!frontend/src/utils/pageactions/pageactions.sass": [ + { + "modules": { + "byIdentifier": {}, + "usedIds": {} + }, + "chunks": { + "byName": {}, + "bySource": {}, + "usedIds": [] + } + } + ] } \ No newline at end of file diff --git a/src/Handler/Exam/Correct.hs b/src/Handler/Exam/Correct.hs index d730ef647..daaa269a0 100644 --- a/src/Handler/Exam/Correct.hs +++ b/src/Handler/Exam/Correct.hs @@ -29,7 +29,8 @@ deriveJSON defaultOptions data CorrectInterfaceResponse = CorrectInterfaceResponseSuccess { cirsUser :: CorrectInterfaceUser - , cirsResults :: Map ExamPartNumber (Maybe Points) + , cirsResults :: Map ExamPartNumber (Maybe ExamResultPoints) + , cirsGrade :: Maybe ExamResultPassedGrade , cirsTime :: UTCTime } | CorrectInterfaceResponseAmbiguous @@ -52,6 +53,7 @@ data CorrectInterfaceRequest = CorrectInterfaceRequest { ciqUser :: Either Text (CryptoID UUID (Key User)) , ciqResults :: Maybe (NonNull (Map ExamPartNumber (Maybe Points))) + , ciqGrade :: Maybe ExamResultPassedGrade } instance FromJSON CorrectInterfaceRequest where @@ -59,6 +61,7 @@ instance FromJSON CorrectInterfaceRequest where ciqUser <- Right <$> o JSON..: "user" <|> Left <$> o JSON..: "user" results <- o JSON..:? "results" ciqResults <- for results $ maybe (fail "Results may not be nullable") return . fromNullable + ciqGrade <- o JSON..:? "grade" return CorrectInterfaceRequest{..} @@ -67,9 +70,13 @@ getECorrectR tid ssh csh examn = do MsgRenderer mr <- getMsgRenderer (Entity eId Exam{..}, examParts) <- runDB $ do - exam@(Entity eId Exam{..}) <- fetchExam tid ssh csh examn + exam@(Entity eId _) <- fetchExam tid ssh csh examn examParts <- sortOn (view $ _entityVal . _examPartNumber) <$> selectList [ ExamPartExam ==. eId ] [ Asc ExamPartName ] return (exam, entityVal <$> examParts) + + -- TODO submit exam results: work in progress + -- mayEditResults <- hasWriteAccessTo $ CExamR tid ssh csh examn EUsersR + let mayEditResults = False let heading = prependCourseTitle tid ssh csh $ (mr . MsgExamCorrectHeading . CI.original) examName @@ -81,6 +88,9 @@ getECorrectR tid ssh csh examn = do participantHeadTooltip = [whamlet| _{MsgExamCorrectHeadParticipantTooltip} |] + examGrades :: [ExamGrade] + examGrades = universeF + examCorrectIdent <- encrypt eId :: Handler CryptoUUIDExam siteLayoutMsg heading $ do @@ -92,10 +102,10 @@ postECorrectR :: TermId -> SchoolId -> CourseShorthand -> ExamName -> Handler Vo postECorrectR tid ssh csh examn = do MsgRenderer mr <- getMsgRenderer - CorrectInterfaceRequest{ciqUser,ciqResults} <- requireCheckJsonBody + CorrectInterfaceRequest{..} <- requireCheckJsonBody response <- runDB $ do - Entity eId _ <- fetchExam tid ssh csh examn + Entity eId Exam{..} <- fetchExam tid ssh csh examn euid <- traverse decrypt ciqUser participantMatches <- E.select . E.from $ \(examRegistration `E.InnerJoin` user) -> do @@ -130,17 +140,18 @@ postECorrectR tid ssh csh examn = do if -- on no-op request, answer with 200 and a set of all participant matches - | is _Nothing ciqResults -> do + | is _Nothing ciqResults, is _Nothing ciqGrade -> do users <- traverse userToResponse participantMatches return CorrectInterfaceResponseNoOp { cirnUsers = Set.fromList users } - -- on match with exactly one exam participant, insert results and answer with 200 - | [match@(Entity uid _)] <- participantMatches, Just results <- ciqResults -> do + -- on match with exactly one exam participant, insert results and/or grade and answer with 200 + | [match@(Entity uid _)] <- participantMatches -> do now <- liftIO getCurrentTime - newExamPartResults <- iforM (toNullable results) $ \partNumber mPartResult -> do - examPartId <- getKeyBy404 $ UniqueExamPartNumber eId partNumber + newExamPartResults <- if + | Just results <- ciqResults -> iforM (toNullable results) $ \partNumber mPartResult -> do + (Entity examPartId ExamPart{..}) <- getBy404 $ UniqueExamPartNumber eId partNumber mOldResult <- getBy $ UniqueExamPartResult examPartId uid if | Just (Entity oldId _) <- mOldResult, is _Nothing mPartResult -> do @@ -150,26 +161,63 @@ postECorrectR tid ssh csh examn = do | Just partResult <- mPartResult -> let mOld = (examPartResultResult . entityVal) <$> mOldResult mNew = ExamAttended <$> mPartResult - resultVal = _entityVal . _examPartResultResult . _ExamAttended + resultVal = _entityVal . _examPartResultResult in if - | mOld /= mNew -> do - newExamPartResult <- upsert ExamPartResult - { examPartResultExamPart = examPartId - , examPartResultUser = uid - , examPartResultResult = ExamAttended partResult - , examPartResultLastChanged = now - } - [ ExamPartResultResult =. ExamAttended partResult - , ExamPartResultLastChanged =. now - ] - audit $ TransactionExamPartResultEdit examPartId uid - return $ newExamPartResult ^? resultVal + | mOld /= mNew -> let + -- cut off part results that exceed the maximum number of points for this exam part for now + -- TODO answer with new failure response type instead + partResult' = if + | Just maxPts <- examPartMaxPoints, maxPts < partResult -> maxPts + | otherwise -> partResult + in do + newExamPartResult <- upsert ExamPartResult + { examPartResultExamPart = examPartId + , examPartResultUser = uid + , examPartResultResult = ExamAttended partResult' + , examPartResultLastChanged = now + } + [ ExamPartResultResult =. ExamAttended partResult' + , ExamPartResultLastChanged =. now + ] + audit $ TransactionExamPartResultEdit examPartId uid + return $ newExamPartResult ^? resultVal | otherwise -> return $ mOldResult ^? _Just . resultVal - | otherwise -> return Nothing + | otherwise -> return Nothing + | otherwise -> return mempty + + newExamResult <- do + mOldResult <- getBy $ UniqueExamResult eId uid + if + | Just (Entity oldId _) <- mOldResult, is _Nothing ciqGrade -> do + delete oldId + audit $ TransactionExamResultDeleted eId uid + return Nothing + | Just result <- ciqGrade -> let + mOld = view passedGrade . examResultResult . entityVal <$> mOldResult + resultGrade = review passedGrade result + passedGrade :: Iso' ExamResultGrade ExamResultPassedGrade + passedGrade = iso (fmap $ bool (Left . view passingGrade) Right examShowGrades) (fmap $ either (review passingGrade) id) + in if + | ciqGrade /= mOld -> do + newResult <- upsert ExamResult + { examResultExam = eId + , examResultUser = uid + , examResultResult = resultGrade + , examResultLastChanged = now + } + [ ExamResultResult =. resultGrade + , ExamResultLastChanged =. now + ] + audit $ TransactionExamResultEdit eId uid + return $ newResult ^? _entityVal . _examResultResult . passedGrade + | otherwise -> return $ mOldResult ^? _Just . _entityVal . _examResultResult . passedGrade + | otherwise -> return Nothing + user <- userToResponse match return CorrectInterfaceResponseSuccess { cirsUser = user , cirsResults = newExamPartResults + , cirsGrade = newExamResult , cirsTime = now } diff --git a/src/Handler/Exam/Form.hs b/src/Handler/Exam/Form.hs index d0e0baf9c..1560e128e 100644 --- a/src/Handler/Exam/Form.hs +++ b/src/Handler/Exam/Form.hs @@ -147,7 +147,7 @@ examCorrectorsForm mPrev = wFormToAForm $ do miLayout' :: MassInputLayout ListLength (Either UserEmail UserId) () miLayout' lLength _ cellWdgts delButtons addWdgts = $(widgetFile "widgets/massinput/examCorrectors/layout") - fmap Set.fromList <$> massInputAccumW miAdd' miCell' miButtonAction' miLayout' ("correctors" :: Text) (fslI MsgExamCorrectors & setTooltip (UniWorXMessages [SomeMessage MsgExamCorrectorsTip, SomeMessage MsgMassInputTip])) False (Set.toList <$> mPrev) + fmap Set.fromList <$> massInputAccumW miAdd' miCell' miButtonAction' miLayout' ("correctors" :: Text) (fslI MsgExamCorrectors & setTooltip (uniworxMessages [MsgExamCorrectorsTip, MsgMassInputTip])) False (Set.toList <$> mPrev) examOccurrenceForm :: Maybe (Set ExamOccurrenceForm) -> AForm Handler (Set ExamOccurrenceForm) examOccurrenceForm prev = wFormToAForm $ do diff --git a/templates/exam-correct.hamlet b/templates/exam-correct.hamlet index 267580182..7b57f6348 100644 --- a/templates/exam-correct.hamlet +++ b/templates/exam-correct.hamlet @@ -20,6 +20,9 @@ $newline never _{MsgExamCorrectHeadPartName name} $nothing _{MsgExamCorrectHeadPart examPartNumber} + $if mayEditResults + + _{MsgExamResult} _{MsgExamCorrectHeadStatus} @@ -32,6 +35,28 @@ $newline never $forall ExamPart{examPartNumber} <- examParts ^{ptsInput examPartNumber} + $if mayEditResults + + + $forall grade <- (toPathPiece <$> examGrades) +