From e68d1b73f94b36abcefe9b2bf42e98de00b69188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Thu, 17 Mar 2022 09:59:52 +0000 Subject: [PATCH] feat: assessments module --- src/app/app.module.ts | 2 + .../assessments/assessment-mock-data.json | 2005 +++++++++++++++++ .../modules/assessments/assessments.module.ts | 78 + .../assessments/assessments.provider.ts | 85 + .../detail/assessments-detail.component.ts | 66 + .../detail/assessments-detail.html | 32 + .../detail/assessments-detail.scss | 18 + .../list/assessments-data-list.component.ts | 51 + .../list/assessments-data-list.html | 32 + .../list/assessments-data-list.scss | 0 .../list/assessments-list-item.component.ts | 34 + .../list/assessments-list-item.html | 24 + .../list/assessments-list-item.scss | 0 .../assessments-simple-data-list.component.ts | 70 + .../list/assessments-simple-data-list.html | 28 + .../list/assessments-simple-data-list.scss | 0 .../page/assessments-page.component.ts | 125 + .../assessments/page/assessments-page.html | 49 + .../assessments/page/assessments-page.scss | 3 + .../assessment-base-info.component.ts | 33 + .../assessment/assessment-base-info.html | 30 + .../assessment/assessment-base-info.scss | 0 .../assessment/assessment-detail.component.ts | 26 + .../types/assessment/assessment-detail.html | 18 + .../types/assessment/assessment-detail.scss | 4 + .../assessment-list-item.component.ts | 26 + .../assessment/assessment-list-item.html | 5 + .../assessment/assessment-list-item.scss | 0 .../course-of-study-assessment.component.ts | 45 + .../course-of-study-assessment.html | 37 + .../course-of-study-assessment.scss | 14 + src/app/modules/auth/auth-guard.service.ts | 4 + .../detail/data-detail-content.component.ts | 25 +- .../data/detail/data-detail-content.html | 181 +- .../data/detail/data-detail.component.spec.ts | 28 +- .../data/detail/data-detail.component.ts | 80 +- src/app/modules/data/detail/data-detail.html | 10 +- .../data/list/data-list-item.component.ts | 29 +- src/app/modules/data/list/data-list-item.html | 50 +- .../modules/data/list/data-list.component.ts | 30 +- src/app/modules/data/list/data-list.html | 21 +- .../data/list/simple-data-list.component.ts | 39 +- .../modules/data/list/simple-data-list.html | 19 +- .../daia-availability.component.spec.ts | 23 +- .../hebis-detail.component.spec.ts | 27 +- .../hebis-detail/hebis-detail.component.ts | 25 +- src/app/modules/profile/profile.module.ts | 15 + src/app/translation/common-string-pipes.ts | 28 + src/app/translation/thing-translate.module.ts | 6 + src/assets/i18n/de.json | 7 + src/assets/i18n/en.json | 7 + 51 files changed, 3372 insertions(+), 222 deletions(-) create mode 100644 src/app/modules/assessments/assessment-mock-data.json create mode 100644 src/app/modules/assessments/assessments.module.ts create mode 100644 src/app/modules/assessments/assessments.provider.ts create mode 100644 src/app/modules/assessments/detail/assessments-detail.component.ts create mode 100644 src/app/modules/assessments/detail/assessments-detail.html create mode 100644 src/app/modules/assessments/detail/assessments-detail.scss create mode 100644 src/app/modules/assessments/list/assessments-data-list.component.ts create mode 100644 src/app/modules/assessments/list/assessments-data-list.html create mode 100644 src/app/modules/assessments/list/assessments-data-list.scss create mode 100644 src/app/modules/assessments/list/assessments-list-item.component.ts create mode 100644 src/app/modules/assessments/list/assessments-list-item.html create mode 100644 src/app/modules/assessments/list/assessments-list-item.scss create mode 100644 src/app/modules/assessments/list/assessments-simple-data-list.component.ts create mode 100644 src/app/modules/assessments/list/assessments-simple-data-list.html create mode 100644 src/app/modules/assessments/list/assessments-simple-data-list.scss create mode 100644 src/app/modules/assessments/page/assessments-page.component.ts create mode 100644 src/app/modules/assessments/page/assessments-page.html create mode 100644 src/app/modules/assessments/page/assessments-page.scss create mode 100644 src/app/modules/assessments/types/assessment/assessment-base-info.component.ts create mode 100644 src/app/modules/assessments/types/assessment/assessment-base-info.html create mode 100644 src/app/modules/assessments/types/assessment/assessment-base-info.scss create mode 100644 src/app/modules/assessments/types/assessment/assessment-detail.component.ts create mode 100644 src/app/modules/assessments/types/assessment/assessment-detail.html create mode 100644 src/app/modules/assessments/types/assessment/assessment-detail.scss create mode 100644 src/app/modules/assessments/types/assessment/assessment-list-item.component.ts create mode 100644 src/app/modules/assessments/types/assessment/assessment-list-item.html create mode 100644 src/app/modules/assessments/types/assessment/assessment-list-item.scss create mode 100644 src/app/modules/assessments/types/course-of-study/course-of-study-assessment.component.ts create mode 100644 src/app/modules/assessments/types/course-of-study/course-of-study-assessment.html create mode 100644 src/app/modules/assessments/types/course-of-study/course-of-study-assessment.scss diff --git a/src/app/app.module.ts b/src/app/app.module.ts index dd3ad7ab..f92edfb7 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -63,6 +63,7 @@ import {AuthModule} from './modules/auth/auth.module'; import {BackgroundModule} from './modules/background/background.module'; import {LibraryModule} from './modules/library/library.module'; import {StorageProvider} from './modules/storage/storage.provider'; +import {AssessmentsModule} from './modules/assessments/assessments.module'; registerLocaleData(localeDe); @@ -129,6 +130,7 @@ export function createTranslateLoader(http: HttpClient) { AboutModule, AppRoutingModule, AuthModule, + AssessmentsModule, BackgroundModule, BrowserModule, BrowserAnimationsModule, diff --git a/src/app/modules/assessments/assessment-mock-data.json b/src/app/modules/assessments/assessment-mock-data.json new file mode 100644 index 00000000..ba9eb131 --- /dev/null +++ b/src/app/modules/assessments/assessment-mock-data.json @@ -0,0 +1,2005 @@ +{ + "data": [ + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Modellierung", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Modellierung", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "c1657d10-990e-5829-8c70-f667ddc1ffee" + } + ], + "type": "assessment", + "uid": "02f065a6-6c02-58ab-97d9-a3febdbc91a1", + "origin": { + "indexed": "2022-02-10T10:08:58.871Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Analysis und Numerische Mathematik für die Informatik", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Analysis und Numerische Mathematik für die Informatik", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "4eeb5aca-6027-5321-bfa7-24525e394259" + } + ], + "type": "assessment", + "uid": "81e2b362-e91c-5474-9725-228c8ef40b55", + "origin": { + "indexed": "2022-02-10T10:08:58.873Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 2, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Analysis und Numerische Mathematik für die Informatik", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Analysis und Numerische Mathematik für die Informatik", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "4eeb5aca-6027-5321-bfa7-24525e394259" + } + ], + "type": "assessment", + "uid": "ce7a5bff-04df-5f84-a95e-7c1ca3d2a801", + "origin": { + "indexed": "2022-02-10T10:08:58.873Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Lineare Algebra und Diskrete Mathematik für die Informatik", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Lineare Algebra und Diskrete Mathematik für die Informatik", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "39eae175-4939-5412-b5cc-3bb4b4e20318" + } + ], + "type": "assessment", + "uid": "fbb55e06-30b7-53a9-b986-0fe6afb3b7e7", + "origin": { + "indexed": "2022-02-10T10:08:58.875Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 2, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Lineare Algebra und Diskrete Mathematik für die Informatik", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Lineare Algebra und Diskrete Mathematik für die Informatik", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "39eae175-4939-5412-b5cc-3bb4b4e20318" + } + ], + "type": "assessment", + "uid": "4a2f1b6b-60ec-53b4-8327-b7321e584940", + "origin": { + "indexed": "2022-02-10T10:08:58.875Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 3, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "2.0", + "name": "Rechnertechnologie und kombinatorische Schaltungen 2", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Rechnertechnologie und kombinatorische Schaltungen", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "8eb1264d-2255-5d7e-a0d7-331b431cad49" + } + ], + "type": "assessment", + "uid": "0d563fcd-4340-5967-987a-c253b13f916e", + "origin": { + "indexed": "2022-02-10T10:08:58.877Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "angemeldet", + "name": "Grundlagen der Programmierung", + "status": "angemeldet", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Einführung in die Praktische Informatik", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "b66c2f80-b922-527d-8ca8-d8a9c6495fcc" + } + ], + "type": "assessment", + "uid": "04f6f5b6-378c-5abd-a8cb-9af46dd3ad13", + "origin": { + "indexed": "2022-02-10T10:08:58.879Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Grundlagen der Programmierung", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Einführung in die Praktische Informatik", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "b66c2f80-b922-527d-8ca8-d8a9c6495fcc" + } + ], + "type": "assessment", + "uid": "3aaf7e3d-6ee4-5d73-8deb-e8ea161fd827", + "origin": { + "indexed": "2022-02-10T10:08:58.879Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 6, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "bestanden", + "name": "Einführung in die Programmierung", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Einführung in die Praktische Informatik", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "b66c2f80-b922-527d-8ca8-d8a9c6495fcc" + } + ], + "type": "assessment", + "uid": "9ba48785-a356-5ddc-9fb5-b9d2b8cf7b2b", + "origin": { + "indexed": "2022-02-10T10:08:58.879Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Algorithmen und Datenstrukturen 1a", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Algorithmen und Datenstrukturen 1", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "0dda2834-9384-590f-a627-d26348d18644" + } + ], + "type": "assessment", + "uid": "f99c958d-6f6a-57a9-bcf7-1513af0abf5f", + "origin": { + "indexed": "2022-02-10T10:08:58.881Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 2, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Algorithmen und Datenstrukturen 1a", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Algorithmen und Datenstrukturen 1", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "0dda2834-9384-590f-a627-d26348d18644" + } + ], + "type": "assessment", + "uid": "9b9e50a1-f17a-51c5-b824-866cb11a4405", + "origin": { + "indexed": "2022-02-10T10:08:58.881Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 3, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "3.0", + "name": "Algorithmen und Datenstrukturen 1b", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Algorithmen und Datenstrukturen 1", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "0dda2834-9384-590f-a627-d26348d18644" + } + ], + "type": "assessment", + "uid": "a2ea7847-d831-5864-b482-21d4f8e2a5af", + "origin": { + "indexed": "2022-02-10T10:08:58.881Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Algorithmen und Datenstrukturen 2", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Algorithmen und Datenstrukturen 2", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3d7abf92-a7e5-5c8e-9946-efe251fd87a6" + } + ], + "type": "assessment", + "uid": "fba5c456-be63-5c33-89ef-b5bd7cd7df45", + "origin": { + "indexed": "2022-02-10T10:08:58.883Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 2, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Algorithmen und Datenstrukturen 2", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Algorithmen und Datenstrukturen 2", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3d7abf92-a7e5-5c8e-9946-efe251fd87a6" + } + ], + "type": "assessment", + "uid": "25cedfbb-873a-50ef-a539-bc3714c41376", + "origin": { + "indexed": "2022-02-10T10:08:58.883Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 3, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Algorithmen und Datenstrukturen 2", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Algorithmen und Datenstrukturen 2", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3d7abf92-a7e5-5c8e-9946-efe251fd87a6" + } + ], + "type": "assessment", + "uid": "354dde99-c7d6-587b-a9eb-6f4e1259fdea", + "origin": { + "indexed": "2022-02-10T10:08:58.884Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 4.5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "2.0", + "name": "Automaten und Rechnerarchitekturen 1", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Automaten und Rechnerarchitekturen", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "fba77601-4028-58e0-bdd5-4b0c09eaba9b" + } + ], + "type": "assessment", + "uid": "2338dd1b-9209-56d6-a78c-f6b9e9798da2", + "origin": { + "indexed": "2022-02-10T10:08:58.886Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Informatik", + "type": "course of study", + "uid": "e6713da3-0c02-50b2-8ead-fd504390e797" + }, + "grade": "5.0", + "name": "Programmierung von Datenbanken", + "status": "nicht bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 16.5, + "categories": [ + "university assessment" + ], + "grade": "2.28", + "name": "Basismodule", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "3de4af04-21b1-5534-9386-24ddaad8f0cd" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Programmierung von Datenbanken", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "1955058e-bcf7-5137-b7bd-367bc90c9a00" + } + ], + "type": "assessment", + "uid": "e7cf84bc-5923-56d8-a68b-b09e5235666c", + "origin": { + "indexed": "2022-02-10T10:08:58.888Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Mathematik", + "type": "course of study", + "uid": "3df383bd-ee44-5cff-8fae-8f5b67048062" + }, + "grade": "2.0", + "name": "Analysis 2", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 4, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden ", + "name": "Pflichtbereich", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "30c0bc07-7cb5-51e1-81fa-e5c98a30e874" + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden", + "name": "Analysis 2", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "45b06996-4087-5dd0-93c7-6e8c1c123752" + } + ], + "type": "assessment", + "uid": "45e8a520-9283-5c97-9017-5d188375ace1", + "origin": { + "indexed": "2022-02-10T10:08:58.895Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 4, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Mathematik", + "type": "course of study", + "uid": "3df383bd-ee44-5cff-8fae-8f5b67048062" + }, + "grade": "bestanden", + "name": "Einführung in die computerorientierte Mathematik", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 4, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden ", + "name": "Pflichtbereich", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "30c0bc07-7cb5-51e1-81fa-e5c98a30e874" + }, + { + "attempt": 1, + "ects": 9, + "categories": [ + "university assessment" + ], + "grade": "1.7", + "name": "Einführung in die computerorientierte Mathematik", + "status": "bestanden", + "type": "assessment", + "uid": "f4ed5cd4-de5a-51fa-b9b8-6cc34cdf3a2b" + } + ], + "type": "assessment", + "uid": "a46c1ff7-b1d8-5418-b4f0-c057314e1e74", + "origin": { + "indexed": "2022-02-10T10:08:58.897Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Bachelor", + "name": "Mathematik", + "type": "course of study", + "uid": "3df383bd-ee44-5cff-8fae-8f5b67048062" + }, + "grade": "1.7", + "name": "Einführung in die computerorientierte Mathematik", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 4, + "categories": [ + "university assessment" + ], + "grade": "Prüfung vorhanden ", + "name": "Pflichtbereich", + "status": "Prüfung vorhanden", + "type": "assessment", + "uid": "30c0bc07-7cb5-51e1-81fa-e5c98a30e874" + }, + { + "attempt": 1, + "ects": 9, + "categories": [ + "university assessment" + ], + "grade": "1.7", + "name": "Einführung in die computerorientierte Mathematik", + "status": "bestanden", + "type": "assessment", + "uid": "f4ed5cd4-de5a-51fa-b9b8-6cc34cdf3a2b" + } + ], + "type": "assessment", + "uid": "c5e7f1e1-7659-5604-8724-20dc02f038d5", + "origin": { + "indexed": "2022-02-10T10:08:58.897Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 2, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Auflage 1", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 7, + "categories": [ + "university assessment" + ], + "grade": "2.3", + "name": "Auflagen", + "status": "bestanden", + "type": "assessment", + "uid": "4fdff0f5-9607-56ae-b259-432ca0e90561" + } + ], + "type": "assessment", + "uid": "2089da55-576d-5475-a92e-af62cd030105", + "origin": { + "indexed": "2022-02-10T10:08:58.905Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "2.3", + "name": "Auflage 2", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 7, + "categories": [ + "university assessment" + ], + "grade": "2.3", + "name": "Auflagen", + "status": "bestanden", + "type": "assessment", + "uid": "4fdff0f5-9607-56ae-b259-432ca0e90561" + } + ], + "type": "assessment", + "uid": "8d135f4c-50a2-5b8a-ba53-e064599f14d3", + "origin": { + "indexed": "2022-02-10T10:08:58.905Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 9, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "2.3", + "name": "Algebraische Geometrie 1", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 13, + "categories": [ + "university assessment" + ], + "grade": "1.9", + "name": "Algebraische Geometrie", + "status": "bestanden", + "type": "assessment", + "uid": "44ec2aac-e649-5bdc-8ce1-9c0a1625bb2c" + } + ], + "type": "assessment", + "uid": "1bbc6a21-bd17-5261-8c5a-a42cc36130af", + "origin": { + "indexed": "2022-02-10T10:08:58.911Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 4, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "1.3", + "name": "Seminar Algebraische Geometrie", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 13, + "categories": [ + "university assessment" + ], + "grade": "1.9", + "name": "Algebraische Geometrie", + "status": "bestanden", + "type": "assessment", + "uid": "44ec2aac-e649-5bdc-8ce1-9c0a1625bb2c" + } + ], + "type": "assessment", + "uid": "f69d8c2a-7caa-5656-9e53-9d0f7383f4b8", + "origin": { + "indexed": "2022-02-10T10:08:58.911Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "1.7", + "name": "Algebraische Geometrie II", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "1.7", + "name": "Algebraische Geometrie", + "status": "bestanden", + "type": "assessment", + "uid": "7daa9f83-e96d-59e6-a239-56ed5507b101" + } + ], + "type": "assessment", + "uid": "64857690-f17a-5a5f-b2dc-810035bcd7d7", + "origin": { + "indexed": "2022-02-10T10:08:58.914Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "2.0", + "name": "Gebäude", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "2.0", + "name": "Lineare algebraische Gruppen", + "status": "bestanden", + "type": "assessment", + "uid": "4872413c-836a-5330-88af-fa0188283d56" + } + ], + "type": "assessment", + "uid": "9677491c-7987-5cc5-af73-fa7a5037a07c", + "origin": { + "indexed": "2022-02-10T10:08:58.917Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "2.3", + "name": "Poendliche Gruppen", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "2.3", + "name": "Algebraische Zahlentheorie", + "status": "bestanden", + "type": "assessment", + "uid": "6da0189e-cf3e-5ba6-be1b-3a73c773c0c4" + } + ], + "type": "assessment", + "uid": "340c0511-823d-5d0c-9e00-1a89f13e52cf", + "origin": { + "indexed": "2022-02-10T10:08:58.920Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "2.0", + "name": "Triangulierungen", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "2.0", + "name": "Diskrete Geometrie und algebraische Kombinatorik", + "status": "bestanden", + "type": "assessment", + "uid": "65224543-965d-5ba8-b542-3e77f56ed33e" + } + ], + "type": "assessment", + "uid": "fcda5951-2dc9-5df9-bebc-d889c38a637e", + "origin": { + "indexed": "2022-02-10T10:08:58.923Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "3.0", + "name": "Zufällige Dynamische Systeme", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "3.0", + "name": "Dynamische Systeme", + "status": "bestanden", + "type": "assessment", + "uid": "04a422ff-3001-5231-addb-f122130435e8" + } + ], + "type": "assessment", + "uid": "989d58bb-a2d5-5b79-a580-f1826646c5d7", + "origin": { + "indexed": "2022-02-10T10:08:58.927Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "1.3", + "name": "Lin. und nichtlin. einparametrige Halbgruppen", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "1.3", + "name": "Fortgeschrittene Funktionsanalysis", + "status": "bestanden", + "type": "assessment", + "uid": "4a22debb-a541-58b6-9e36-7b8fe2ff99c9" + } + ], + "type": "assessment", + "uid": "6b2701bf-00b8-56b6-9a30-9608ecb67e77", + "origin": { + "indexed": "2022-02-10T10:08:58.930Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "1.3", + "name": "Schwache Konvergenz", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "1.3", + "name": "Stochastik", + "status": "bestanden", + "type": "assessment", + "uid": "47d78cce-0dd0-5570-ac07-193fc58a9831" + } + ], + "type": "assessment", + "uid": "1e0954fe-59ef-5edb-90d9-892da1ed8ff4", + "origin": { + "indexed": "2022-02-10T10:08:58.934Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "2.7", + "name": "Algebraische Topologie II", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "2.7", + "name": "Topologie", + "status": "bestanden", + "type": "assessment", + "uid": "124a1e54-45c5-5140-bdb8-23e56e7f501b" + } + ], + "type": "assessment", + "uid": "e8e81454-76fa-5ed8-b737-b37e771b9a53", + "origin": { + "indexed": "2022-02-10T10:08:58.939Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 3, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Anleitung zur Statistischen Beratung", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "bestanden ", + "name": "Ergänzungsmodul", + "status": "bestanden", + "type": "assessment", + "uid": "9abe75e5-ea4a-57f4-a69d-1260e62f2f91" + } + ], + "type": "assessment", + "uid": "0361bb70-a95e-5720-a82d-89b29124cd23", + "origin": { + "indexed": "2022-02-10T10:08:58.942Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 2, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Präsentation zum Statistischen Praktikum", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "bestanden ", + "name": "Ergänzungsmodul", + "status": "bestanden", + "type": "assessment", + "uid": "9abe75e5-ea4a-57f4-a69d-1260e62f2f91" + } + ], + "type": "assessment", + "uid": "c6c61f74-c872-5864-9e5c-62ca6fe35785", + "origin": { + "indexed": "2022-02-10T10:08:58.942Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 3, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Anleitung zur wissenschaftlichen Arbeit", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 3, + "categories": [ + "university assessment" + ], + "grade": "bestanden ", + "name": "Anleitung zum wissenschaftlichen Arbeiten", + "status": "bestanden", + "type": "assessment", + "uid": "4dfc3123-f100-5bb7-8c72-b1985e3f1bce" + } + ], + "type": "assessment", + "uid": "deacc78d-892e-5710-b250-a5413aa2fe3c", + "origin": { + "indexed": "2022-02-10T10:08:58.945Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "2.3", + "name": "System Erde", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 24, + "categories": [ + "university assessment" + ], + "grade": "1.8", + "name": "Geowissenschaften: Mineralogie und Kristallographie", + "status": "bestanden", + "type": "assessment", + "uid": "aafe7538-64a8-5388-bb36-02dd65539d45" + }, + { + "attempt": 1, + "ects": 7, + "categories": [ + "university assessment" + ], + "grade": "2.3", + "name": "Geowissenschaften 1", + "status": "bestanden", + "type": "assessment", + "uid": "a2836507-9a8d-5e52-b4cb-836c81f1faab" + } + ], + "type": "assessment", + "uid": "aeda34cc-f10b-54ca-a8f9-3aca3aa5e1a5", + "origin": { + "indexed": "2022-02-10T10:08:58.949Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 2, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Geländeübung", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 24, + "categories": [ + "university assessment" + ], + "grade": "1.8", + "name": "Geowissenschaften: Mineralogie und Kristallographie", + "status": "bestanden", + "type": "assessment", + "uid": "aafe7538-64a8-5388-bb36-02dd65539d45" + }, + { + "attempt": 1, + "ects": 7, + "categories": [ + "university assessment" + ], + "grade": "2.3", + "name": "Geowissenschaften 1", + "status": "bestanden", + "type": "assessment", + "uid": "a2836507-9a8d-5e52-b4cb-836c81f1faab" + } + ], + "type": "assessment", + "uid": "e2b74321-ea67-59f9-a3ff-8b22adb486fa", + "origin": { + "indexed": "2022-02-10T10:08:58.949Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 3, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "3.0", + "name": "Geomaterialien: Minerale", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 24, + "categories": [ + "university assessment" + ], + "grade": "1.8", + "name": "Geowissenschaften: Mineralogie und Kristallographie", + "status": "bestanden", + "type": "assessment", + "uid": "aafe7538-64a8-5388-bb36-02dd65539d45" + }, + { + "attempt": 1, + "ects": 6, + "categories": [ + "university assessment" + ], + "grade": "2.3", + "name": "Geomaterialien", + "status": "bestanden", + "type": "assessment", + "uid": "790a165b-f992-58d3-8cfa-00aa50d01546" + } + ], + "type": "assessment", + "uid": "d6ddf1b7-856f-5eb7-a31a-b5e87a6d213d", + "origin": { + "indexed": "2022-02-10T10:08:58.951Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 3, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "1.7", + "name": "Geomaterialien: Gesteine", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 24, + "categories": [ + "university assessment" + ], + "grade": "1.8", + "name": "Geowissenschaften: Mineralogie und Kristallographie", + "status": "bestanden", + "type": "assessment", + "uid": "aafe7538-64a8-5388-bb36-02dd65539d45" + }, + { + "attempt": 1, + "ects": 6, + "categories": [ + "university assessment" + ], + "grade": "2.3", + "name": "Geomaterialien", + "status": "bestanden", + "type": "assessment", + "uid": "790a165b-f992-58d3-8cfa-00aa50d01546" + } + ], + "type": "assessment", + "uid": "cfe44a6d-51bd-5ddc-a86d-5259bf65079c", + "origin": { + "indexed": "2022-02-10T10:08:58.951Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 3, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Geophysik 1", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 24, + "categories": [ + "university assessment" + ], + "grade": "1.8", + "name": "Geowissenschaften: Mineralogie und Kristallographie", + "status": "bestanden", + "type": "assessment", + "uid": "aafe7538-64a8-5388-bb36-02dd65539d45" + }, + { + "attempt": 1, + "ects": 7, + "categories": [ + "university assessment" + ], + "grade": "1.0", + "name": "Geophysik", + "status": "bestanden", + "type": "assessment", + "uid": "34f636c7-26d0-5ba9-925f-46e3e2ca252f" + } + ], + "type": "assessment", + "uid": "fd662afe-0614-52d9-8299-fd10e3fc5824", + "origin": { + "indexed": "2022-02-10T10:08:58.954Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 4, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Geophysik 2", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 24, + "categories": [ + "university assessment" + ], + "grade": "1.8", + "name": "Geowissenschaften: Mineralogie und Kristallographie", + "status": "bestanden", + "type": "assessment", + "uid": "aafe7538-64a8-5388-bb36-02dd65539d45" + }, + { + "attempt": 1, + "ects": 7, + "categories": [ + "university assessment" + ], + "grade": "1.0", + "name": "Geophysik", + "status": "bestanden", + "type": "assessment", + "uid": "34f636c7-26d0-5ba9-925f-46e3e2ca252f" + } + ], + "type": "assessment", + "uid": "ace3117b-65df-5d83-9d4d-492adfffe4db", + "origin": { + "indexed": "2022-02-10T10:08:58.954Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 0, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "1.0", + "name": "Modulprüfung Geophysik", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 24, + "categories": [ + "university assessment" + ], + "grade": "1.8", + "name": "Geowissenschaften: Mineralogie und Kristallographie", + "status": "bestanden", + "type": "assessment", + "uid": "aafe7538-64a8-5388-bb36-02dd65539d45" + }, + { + "attempt": 1, + "ects": 7, + "categories": [ + "university assessment" + ], + "grade": "1.0", + "name": "Geophysik", + "status": "bestanden", + "type": "assessment", + "uid": "34f636c7-26d0-5ba9-925f-46e3e2ca252f" + } + ], + "type": "assessment", + "uid": "b02fc43f-39a3-5d61-bb08-94c7acc3b505", + "origin": { + "indexed": "2022-02-10T10:08:58.954Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 4, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Seismologie", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 24, + "categories": [ + "university assessment" + ], + "grade": "1.8", + "name": "Geowissenschaften: Mineralogie und Kristallographie", + "status": "bestanden", + "type": "assessment", + "uid": "aafe7538-64a8-5388-bb36-02dd65539d45" + }, + { + "attempt": 1, + "ects": 4, + "categories": [ + "university assessment" + ], + "grade": "bestanden", + "name": "Vertiefung Geophysik: Seismologie", + "status": "bestanden", + "type": "assessment", + "uid": "5243616e-250e-5dc7-9571-d03668ee8c4f" + } + ], + "type": "assessment", + "uid": "0d234944-1973-5cf4-947d-24b9e0c83b53", + "origin": { + "indexed": "2022-02-10T10:08:58.955Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 2, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Oberseminar", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "bestanden ", + "name": "Kolloquiumsmodul", + "status": "bestanden", + "type": "assessment", + "uid": "2b156923-46d0-507c-84b2-a58c7ac72922" + } + ], + "type": "assessment", + "uid": "41854dbe-fb03-5529-a581-a81c283bb33d", + "origin": { + "indexed": "2022-02-10T10:08:58.959Z", + "name": "QIS/HIS", + "type": "remote" + } + }, + { + "attempt": 1, + "ects": 3, + "categories": [ + "university assessment" + ], + "courseOfStudy": { + "academicDegree": "Master", + "name": "Mathematik", + "type": "course of study", + "uid": "72324d64-ff69-5ee1-a1a3-62e4c6a3e5e1" + }, + "grade": "bestanden", + "name": "Vortrag zur Masterarbeit", + "status": "bestanden", + "superAssessments": [ + { + "attempt": 1, + "ects": 5, + "categories": [ + "university assessment" + ], + "grade": "bestanden ", + "name": "Kolloquiumsmodul", + "status": "bestanden", + "type": "assessment", + "uid": "2b156923-46d0-507c-84b2-a58c7ac72922" + } + ], + "type": "assessment", + "uid": "685b635b-b3c2-56b0-81d6-cebbf5ca19ae", + "origin": { + "indexed": "2022-02-10T10:08:58.959Z", + "name": "QIS/HIS", + "type": "remote" + } + } + ] +} diff --git a/src/app/modules/assessments/assessments.module.ts b/src/app/modules/assessments/assessments.module.ts new file mode 100644 index 00000000..0e83a6e7 --- /dev/null +++ b/src/app/modules/assessments/assessments.module.ts @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {NgModule} from '@angular/core'; +import {AssessmentListItemComponent} from './types/assessment/assessment-list-item.component'; +import {AssessmentBaseInfoComponent} from './types/assessment/assessment-base-info.component'; +import {AssessmentDetailComponent} from './types/assessment/assessment-detail.component'; +import {CommonModule} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {IonicModule} from '@ionic/angular'; +import {TranslateModule} from '@ngx-translate/core'; +import {DataModule} from '../data/data.module'; +import {ThingTranslateModule} from '../../translation/thing-translate.module'; +import {CourseOfStudyAssessmentComponent} from './types/course-of-study/course-of-study-assessment.component'; +import {AssessmentsPageComponent} from './page/assessments-page.component'; +import {RouterModule} from '@angular/router'; +import {AuthGuardService} from '../auth/auth-guard.service'; +import {MomentModule} from 'ngx-moment'; +import {AssessmentsListItemComponent} from './list/assessments-list-item.component'; +import {AssessmentsDataListComponent} from './list/assessments-data-list.component'; +import {AssessmentsDetailComponent} from './detail/assessments-detail.component'; +import {AssessmentsProvider} from './assessments.provider'; +import {AssessmentsSimpleDataListComponent} from './list/assessments-simple-data-list.component'; +import {ProtectedRoutes} from '../auth/protected.routes'; + +const routes: ProtectedRoutes = [ + { + path: 'assessments', + component: AssessmentsPageComponent, + data: {authProvider: 'default'}, + canActivate: [AuthGuardService], + }, + { + path: 'assessments/detail/:uid', + component: AssessmentsDetailComponent, + data: {authProvider: 'default'}, + canActivate: [AuthGuardService], + }, +]; + +@NgModule({ + declarations: [ + AssessmentListItemComponent, + AssessmentBaseInfoComponent, + AssessmentDetailComponent, + AssessmentsListItemComponent, + CourseOfStudyAssessmentComponent, + AssessmentsPageComponent, + AssessmentsDataListComponent, + AssessmentsDetailComponent, + AssessmentsSimpleDataListComponent, + ], + imports: [ + CommonModule, + FormsModule, + IonicModule, + RouterModule.forChild(routes), + TranslateModule, + DataModule, + ThingTranslateModule, + MomentModule, + ], + providers: [AssessmentsProvider], + exports: [], +}) +export class AssessmentsModule {} diff --git a/src/app/modules/assessments/assessments.provider.ts b/src/app/modules/assessments/assessments.provider.ts new file mode 100644 index 00000000..40a2c8c9 --- /dev/null +++ b/src/app/modules/assessments/assessments.provider.ts @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Injectable} from '@angular/core'; +import {ConfigProvider} from '../config/config.provider'; +import {SCAssessment} from '@openstapps/core'; +import {DefaultAuthService} from '../auth/default-auth.service'; +import {HttpClient} from '@angular/common/http'; + +@Injectable({ + providedIn: 'root', +}) +export class AssessmentsProvider { + assessmentPath = 'assessments'; + + // usually this wouldn't be necessary, but the assessment service + // is very aggressive about too many requests being made to the server + cache?: Promise; + + cacheTimestamp = 0; + + // 15 minutes + cacheMaxAge = 15 * 60 * 1000; + + constructor( + readonly configProvider: ConfigProvider, + readonly defaultAuth: DefaultAuthService, + readonly http: HttpClient, + ) {} + + async getAssessments( + accessToken?: string | null, + forceFetch = false, + ): Promise { + // again, this is a hack to get around the fact that the assessment service + // is very aggressive how many requests you can make, so it can happen + // during development that simply by reloading pages over and over again + // the assessment service will block you + if (accessToken === 'mock' && !this.cache) { + this.cacheTimestamp = Date.now(); + this.cache = import('./assessment-mock-data.json').then( + it => it.data as SCAssessment[], + ); + } + + if ( + this.cache && + !forceFetch && + Date.now() - this.cacheTimestamp < this.cacheMaxAge + ) { + return await this.cache; + } + + const url = this.configProvider.config.app.features.extern?.hisometry.url; + if (!url) throw new Error('Config lacks url for hisometry'); + + this.cache = this.http + .get<{data: SCAssessment[]}>(`${url}/${this.assessmentPath}`, { + headers: { + Authorization: `Bearer ${ + accessToken ?? (await this.defaultAuth.getValidToken()).accessToken + }`, + }, + }) + .toPromise() + .then(it => { + this.cacheTimestamp = Date.now(); + return it?.data ?? []; + }); + + return this.cache; + } +} diff --git a/src/app/modules/assessments/detail/assessments-detail.component.ts b/src/app/modules/assessments/detail/assessments-detail.component.ts new file mode 100644 index 00000000..eb6a2867 --- /dev/null +++ b/src/app/modules/assessments/detail/assessments-detail.component.ts @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, ViewChild} from '@angular/core'; +import {flatMap} from 'lodash-es'; +import {ActivatedRoute} from '@angular/router'; +import {AssessmentsProvider} from '../assessments.provider'; +import { + DataDetailComponent, + ExternalDataLoadEvent, +} from '../../data/detail/data-detail.component'; +import {ViewWillEnter} from '@ionic/angular'; + +@Component({ + selector: 'assessments-detail', + templateUrl: 'assessments-detail.html', + styleUrls: ['assessments-detail.scss'], +}) +export class AssessmentsDetailComponent implements ViewWillEnter { + constructor( + readonly route: ActivatedRoute, + readonly assessmentsProvider: AssessmentsProvider, + ) {} + + @ViewChild(DataDetailComponent) + detailComponent: DataDetailComponent; + + getItem(event: ExternalDataLoadEvent) { + this.assessmentsProvider + .getAssessments( + this.route.snapshot.queryParamMap.get('token'), + event.forceReload, + ) + .then(assessments => { + const assessment = assessments.find(it => it.uid === event.uid); + event.resolve( + assessment + ? assessment + : flatMap(assessments, it => + Array.isArray(it.superAssessments) + ? it.superAssessments.map(superAssessment => ({ + ...superAssessment, + origin: it.origin, + })) + : [], + ).find(it => it?.uid === event.uid), + ); + }); + } + + async ionViewWillEnter() { + await this.detailComponent.ionViewWillEnter(); + } +} diff --git a/src/app/modules/assessments/detail/assessments-detail.html b/src/app/modules/assessments/detail/assessments-detail.html new file mode 100644 index 00000000..629ddd8f --- /dev/null +++ b/src/app/modules/assessments/detail/assessments-detail.html @@ -0,0 +1,32 @@ + + + + + + + + {{ 'data.detail.TITLE' | translate }} + + + + + + + diff --git a/src/app/modules/assessments/detail/assessments-detail.scss b/src/app/modules/assessments/detail/assessments-detail.scss new file mode 100644 index 00000000..791b679f --- /dev/null +++ b/src/app/modules/assessments/detail/assessments-detail.scss @@ -0,0 +1,18 @@ +/*! + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +stapps-data-detail { + height: 100%; +} diff --git a/src/app/modules/assessments/list/assessments-data-list.component.ts b/src/app/modules/assessments/list/assessments-data-list.component.ts new file mode 100644 index 00000000..3d541888 --- /dev/null +++ b/src/app/modules/assessments/list/assessments-data-list.component.ts @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, EventEmitter, Input, Output} from '@angular/core'; +import {SCThings} from '@openstapps/core'; +import {Observable} from 'rxjs'; + +@Component({ + selector: 'assessments-data-list', + templateUrl: './assessments-data-list.html', + styleUrls: ['./assessments-data-list.scss'], +}) +export class AssessmentsDataListComponent { + /** + * All SCThings to display + */ + @Input() items?: SCThings[]; + + /** + * Output binding to trigger pagination fetch + */ + // eslint-disable-next-line @angular-eslint/no-output-rename + @Output('loadmore') loadMore = new EventEmitter(); + + /** + * Emits when scroll view should reset to top + */ + @Input() resetToTop?: Observable; + + /** + * Indicates whether the list is to display SCThings of a single type + */ + @Input() singleType = false; + + /** + * Signalizes that the data is being loaded + */ + @Input() loading = true; +} diff --git a/src/app/modules/assessments/list/assessments-data-list.html b/src/app/modules/assessments/list/assessments-data-list.html new file mode 100644 index 00000000..4af0676c --- /dev/null +++ b/src/app/modules/assessments/list/assessments-data-list.html @@ -0,0 +1,32 @@ + + + + + + + + + + diff --git a/src/app/modules/assessments/list/assessments-data-list.scss b/src/app/modules/assessments/list/assessments-data-list.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/assessments/list/assessments-list-item.component.ts b/src/app/modules/assessments/list/assessments-list-item.component.ts new file mode 100644 index 00000000..1f0177c6 --- /dev/null +++ b/src/app/modules/assessments/list/assessments-list-item.component.ts @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, Input} from '@angular/core'; +import {SCThings} from '@openstapps/core'; + +@Component({ + selector: 'assessments-list-item', + templateUrl: 'assessments-list-item.html', + styleUrls: ['assessments-list-item.scss'], +}) +export class AssessmentsListItemComponent { + /** + * Whether the list item should show a thumbnail + */ + @Input() hideThumbnail = false; + + /** + * An item to show + */ + @Input() item: SCThings; +} diff --git a/src/app/modules/assessments/list/assessments-list-item.html b/src/app/modules/assessments/list/assessments-list-item.html new file mode 100644 index 00000000..9971ce2c --- /dev/null +++ b/src/app/modules/assessments/list/assessments-list-item.html @@ -0,0 +1,24 @@ + + + + + + + diff --git a/src/app/modules/assessments/list/assessments-list-item.scss b/src/app/modules/assessments/list/assessments-list-item.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/assessments/list/assessments-simple-data-list.component.ts b/src/app/modules/assessments/list/assessments-simple-data-list.component.ts new file mode 100644 index 00000000..69555e29 --- /dev/null +++ b/src/app/modules/assessments/list/assessments-simple-data-list.component.ts @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, Input, OnDestroy, OnInit} from '@angular/core'; +import {SCThings} from '@openstapps/core'; +import {Subscription} from 'rxjs'; +import {DataRoutingService} from '../../data/data-routing.service'; +import {ActivatedRoute, Router} from '@angular/router'; + +@Component({ + selector: 'assessments-simple-data-list', + templateUrl: 'assessments-simple-data-list.html', + styleUrls: ['assessments-simple-data-list.scss'], +}) +export class AssessmentsSimpleDataListComponent implements OnInit, OnDestroy { + /** + * All SCThings to display + */ + _items?: Promise; + + /** + * Indicates whether or not the list is to display SCThings of a single type + */ + @Input() singleType = false; + + /** + * List header + */ + @Input() listHeader?: string; + + @Input() set items(items: SCThings[] | undefined) { + this._items = new Promise(resolve => resolve(items)); + } + + subscriptions: Subscription[] = []; + + constructor( + readonly dataRoutingService: DataRoutingService, + readonly router: Router, + readonly activatedRoute: ActivatedRoute, + ) {} + + ngOnInit() { + this.subscriptions.push( + this.dataRoutingService.itemSelectListener().subscribe(thing => { + void this.router.navigate(['assessments', 'detail', thing.uid], { + queryParams: { + token: this.activatedRoute.snapshot.queryParamMap.get('token'), + }, + }); + }), + ); + } + + ngOnDestroy() { + for (const subscription of this.subscriptions) subscription.unsubscribe(); + } +} diff --git a/src/app/modules/assessments/list/assessments-simple-data-list.html b/src/app/modules/assessments/list/assessments-simple-data-list.html new file mode 100644 index 00000000..574e8d9f --- /dev/null +++ b/src/app/modules/assessments/list/assessments-simple-data-list.html @@ -0,0 +1,28 @@ + + + + + + + diff --git a/src/app/modules/assessments/list/assessments-simple-data-list.scss b/src/app/modules/assessments/list/assessments-simple-data-list.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/assessments/page/assessments-page.component.ts b/src/app/modules/assessments/page/assessments-page.component.ts new file mode 100644 index 00000000..1e88c3be --- /dev/null +++ b/src/app/modules/assessments/page/assessments-page.component.ts @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import { + AfterViewInit, + Component, + OnDestroy, + OnInit, + ViewChild, +} from '@angular/core'; +import {AssessmentsProvider} from '../assessments.provider'; +import {SCAssessment, SCCourseOfStudy} from '@openstapps/core'; +import {groupBy, mapValues} from 'lodash-es'; +import {ActivatedRoute, Router} from '@angular/router'; +import {Subscription} from 'rxjs'; +import {NGXLogger} from 'ngx-logger'; +import {materialSharedAxisX} from '../../../animation/material-motion'; +import {SharedAxisChoreographer} from '../../../animation/animation-choreographer'; +import {DataProvider, DataScope} from '../../data/data.provider'; +import {DataRoutingService} from '../../data/data-routing.service'; + +@Component({ + selector: 'app-assessments-page', + templateUrl: 'assessments-page.html', + styleUrls: ['assessments-page.scss'], + animations: [materialSharedAxisX], +}) +export class AssessmentsPageComponent + implements OnInit, AfterViewInit, OnDestroy +{ + assessments: Promise< + Record< + string, + { + assessments: SCAssessment[]; + courseOfStudy: Promise; + } + > + >; + + assessmentKeys: string[] = []; + + routingSubscription: Subscription; + + @ViewChild('segment') segmentView!: HTMLIonSegmentElement; + + sharedAxisChoreographer: SharedAxisChoreographer = + new SharedAxisChoreographer('', []); + + constructor( + readonly logger: NGXLogger, + readonly assessmentsProvider: AssessmentsProvider, + readonly dataProvider: DataProvider, + readonly activatedRoute: ActivatedRoute, + readonly dataRoutingService: DataRoutingService, + readonly router: Router, + ) {} + + ngAfterViewInit() { + this.segmentView.value = this.sharedAxisChoreographer.currentValue; + } + + ngOnDestroy() { + this.routingSubscription.unsubscribe(); + } + + ngOnInit() { + this.routingSubscription = this.dataRoutingService + .itemSelectListener() + .subscribe(thing => { + void this.router.navigate(['assessments', 'detail', thing.uid], { + queryParams: { + token: this.activatedRoute.snapshot.queryParamMap.get('token'), + }, + }); + }); + + this.activatedRoute.queryParams.subscribe(parameters => { + try { + this.assessments = this.assessmentsProvider + .getAssessments(parameters.token) + .then(assessments => + groupBy(assessments, it => it.courseOfStudy?.uid ?? 'unknown'), + ) + .then(it => { + this.assessmentKeys = Object.keys(it); + this.sharedAxisChoreographer = new SharedAxisChoreographer( + this.assessmentKeys[0], + this.assessmentKeys, + ); + if (this.segmentView) { + this.segmentView.value = + this.sharedAxisChoreographer.currentValue; + } + return it; + }) + .then(groups => + mapValues(groups, (group, uid) => ({ + assessments: group, + courseOfStudy: this.dataProvider + .get(uid, DataScope.Remote) + .catch( + () => group[0].courseOfStudy, + ) as Promise, + })), + ); + } catch (error) { + this.logger.error(error); + this.assessments = Promise.resolve({}); + } + }); + } +} diff --git a/src/app/modules/assessments/page/assessments-page.html b/src/app/modules/assessments/page/assessments-page.html new file mode 100644 index 00000000..cb32fbb6 --- /dev/null +++ b/src/app/modules/assessments/page/assessments-page.html @@ -0,0 +1,49 @@ + + + + + + + {{ 'assessments.TITLE' | translate }} + + + + + +
+ + {{ 'name' | thingTranslate: course }} ({{ + 'academicDegree' | thingTranslate: course + }}) + +
+ + {{ key }} + +
+
+ +
+ +
+
diff --git a/src/app/modules/assessments/page/assessments-page.scss b/src/app/modules/assessments/page/assessments-page.scss new file mode 100644 index 00000000..51671299 --- /dev/null +++ b/src/app/modules/assessments/page/assessments-page.scss @@ -0,0 +1,3 @@ +.content { + height: 100%; +} diff --git a/src/app/modules/assessments/types/assessment/assessment-base-info.component.ts b/src/app/modules/assessments/types/assessment/assessment-base-info.component.ts new file mode 100644 index 00000000..2a50f233 --- /dev/null +++ b/src/app/modules/assessments/types/assessment/assessment-base-info.component.ts @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, Input} from '@angular/core'; +import {SCAssessment} from '@openstapps/core'; + +@Component({ + selector: 'assessment-base-info', + templateUrl: 'assessment-base-info.html', + styleUrls: ['assessment-base-info.scss'], +}) +export class AssessmentBaseInfoComponent { + _item: SCAssessment; + + passed = false; + + @Input() set item(item: SCAssessment) { + this._item = item; + this.passed = !/^(5[,.]0)|FX?$/i.test(item.grade); + } +} diff --git a/src/app/modules/assessments/types/assessment/assessment-base-info.html b/src/app/modules/assessments/types/assessment/assessment-base-info.html new file mode 100644 index 00000000..06c60ac2 --- /dev/null +++ b/src/app/modules/assessments/types/assessment/assessment-base-info.html @@ -0,0 +1,30 @@ + + +{{ + (_item.grade | isNumeric) + ? (_item.grade + | numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1') + : '' + }} + {{ 'status' | thingTranslate: _item | titlecase }}, + {{ 'attempt' | propertyNameTranslate: _item }} + {{ _item.attempt }} + + + {{ _item.ects }} + {{ 'ects' | propertyNameTranslate: _item }} diff --git a/src/app/modules/assessments/types/assessment/assessment-base-info.scss b/src/app/modules/assessments/types/assessment/assessment-base-info.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/assessments/types/assessment/assessment-detail.component.ts b/src/app/modules/assessments/types/assessment/assessment-detail.component.ts new file mode 100644 index 00000000..4d6993b7 --- /dev/null +++ b/src/app/modules/assessments/types/assessment/assessment-detail.component.ts @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, Input} from '@angular/core'; +import {SCAssessment} from '@openstapps/core'; + +@Component({ + selector: 'assessment-detail', + templateUrl: 'assessment-detail.html', + styleUrls: ['assessment-detail.scss'], +}) +export class AssessmentDetailComponent { + @Input() item: SCAssessment; +} diff --git a/src/app/modules/assessments/types/assessment/assessment-detail.html b/src/app/modules/assessments/types/assessment/assessment-detail.html new file mode 100644 index 00000000..7ce233ee --- /dev/null +++ b/src/app/modules/assessments/types/assessment/assessment-detail.html @@ -0,0 +1,18 @@ + + {{ $any('courseOfStudy' | propertyNameTranslate: item) | titlecase }}: + {{ 'name' | thingTranslate: $any(courseOfStudy) }} + ({{ 'academicDegree' | thingTranslate: $any(courseOfStudy) }}) + + + + + +

+ {{ $any('superAssessments' | propertyNameTranslate: item) | titlecase }} +

+ +
diff --git a/src/app/modules/assessments/types/assessment/assessment-detail.scss b/src/app/modules/assessments/types/assessment/assessment-detail.scss new file mode 100644 index 00000000..ba814612 --- /dev/null +++ b/src/app/modules/assessments/types/assessment/assessment-detail.scss @@ -0,0 +1,4 @@ +stapps-data-list { + height: 100px; + width: 100%; +} diff --git a/src/app/modules/assessments/types/assessment/assessment-list-item.component.ts b/src/app/modules/assessments/types/assessment/assessment-list-item.component.ts new file mode 100644 index 00000000..acc059e7 --- /dev/null +++ b/src/app/modules/assessments/types/assessment/assessment-list-item.component.ts @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, Input} from '@angular/core'; +import {SCAssessment} from '@openstapps/core'; + +@Component({ + selector: 'stapps-assessment-list-item', + templateUrl: './assessment-list-item.html', + styleUrls: ['./assessment-list-item.scss'], +}) +export class AssessmentListItemComponent { + @Input() item: SCAssessment; +} diff --git a/src/app/modules/assessments/types/assessment/assessment-list-item.html b/src/app/modules/assessments/types/assessment/assessment-list-item.html new file mode 100644 index 00000000..858c535f --- /dev/null +++ b/src/app/modules/assessments/types/assessment/assessment-list-item.html @@ -0,0 +1,5 @@ +

+ {{ 'name' | thingTranslate: item }} + {{ item.date ? (item.date | amDateFormat) : '' }} +

+ diff --git a/src/app/modules/assessments/types/assessment/assessment-list-item.scss b/src/app/modules/assessments/types/assessment/assessment-list-item.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/assessments/types/course-of-study/course-of-study-assessment.component.ts b/src/app/modules/assessments/types/course-of-study/course-of-study-assessment.component.ts new file mode 100644 index 00000000..974c0f6e --- /dev/null +++ b/src/app/modules/assessments/types/course-of-study/course-of-study-assessment.component.ts @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +import {Component, Input} from '@angular/core'; +import {SCAssessment, SCCourseOfStudyWithoutReferences} from '@openstapps/core'; +import {sum, sumBy} from 'lodash-es'; + +@Component({ + selector: 'course-of-study-assessment', + templateUrl: 'course-of-study-assessment.html', + styleUrls: ['course-of-study-assessment.scss'], +}) +export class CourseOfStudyAssessmentComponent { + @Input() courseOfStudy: SCCourseOfStudyWithoutReferences | null; + + _assessments: SCAssessment[]; + + grade = 0; + + ects = 0; + + @Input() set assessments(value: SCAssessment[]) { + this._assessments = value; + + const grades = this._assessments + // TODO: find out if this is correct + .filter(assessment => assessment.status === 'bestanden') + .map(assessment => Number(assessment.grade)) + .filter(grade => !Number.isNaN(grade)); + this.grade = grades.length > 0 ? sum(grades) / grades.length : 0; + this.ects = sumBy(this._assessments, 'ects'); + } +} diff --git a/src/app/modules/assessments/types/course-of-study/course-of-study-assessment.html b/src/app/modules/assessments/types/course-of-study/course-of-study-assessment.html new file mode 100644 index 00000000..095a175f --- /dev/null +++ b/src/app/modules/assessments/types/course-of-study/course-of-study-assessment.html @@ -0,0 +1,37 @@ + + + +
+
+

{{ 'assessments.courseOfStudyAssessments.PROGRESS' | translate }}

+

+ {{ $any('grade' | propertyNameTranslate: 'assessment') | titlecase }}: + {{ + grade + | numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1' + }} +

+

{{ 'ects' | propertyNameTranslate: 'assessment' }}: {{ ects }}

+
+

+ {{ 'assessments.courseOfStudyAssessments.ASSESSMENTS' | translate }} +

+
+
diff --git a/src/app/modules/assessments/types/course-of-study/course-of-study-assessment.scss b/src/app/modules/assessments/types/course-of-study/course-of-study-assessment.scss new file mode 100644 index 00000000..4c9fd44c --- /dev/null +++ b/src/app/modules/assessments/types/course-of-study/course-of-study-assessment.scss @@ -0,0 +1,14 @@ +/*! + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ diff --git a/src/app/modules/auth/auth-guard.service.ts b/src/app/modules/auth/auth-guard.service.ts index 5b89d594..84da591e 100644 --- a/src/app/modules/auth/auth-guard.service.ts +++ b/src/app/modules/auth/auth-guard.service.ts @@ -18,6 +18,10 @@ export class AuthGuardService implements CanActivate { route: ActivatedProtectedRouteSnapshot, _state: RouterStateSnapshot, ) { + if (route.queryParamMap.get('token')) { + return true; + } + try { await this.authHelper .getProvider(route.data.authProvider) diff --git a/src/app/modules/data/detail/data-detail-content.component.ts b/src/app/modules/data/detail/data-detail-content.component.ts index 7cd96354..374e04a4 100644 --- a/src/app/modules/data/detail/data-detail-content.component.ts +++ b/src/app/modules/data/detail/data-detail-content.component.ts @@ -1,19 +1,20 @@ /* - * Copyright (C) 2019 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ -import {Component, Input} from '@angular/core'; +import {Component, Input, TemplateRef} from '@angular/core'; import {SCThings} from '@openstapps/core'; +import {DataListContext} from '../list/data-list.component'; /** * TODO @@ -28,4 +29,6 @@ export class DataDetailContentComponent { * TODO */ @Input() item: SCThings; + + @Input() contentTemplateRef?: TemplateRef>; } diff --git a/src/app/modules/data/detail/data-detail-content.html b/src/app/modules/data/detail/data-detail-content.html index 94db7bae..523090b0 100644 --- a/src/app/modules/data/detail/data-detail-content.html +++ b/src/app/modules/data/detail/data-detail-content.html @@ -1,86 +1,99 @@ -
- - - - - - - - - - - - - - - - - - - - - - - -
-

{{ item.name }}

- {{ item.type }} -
-
-
-
-
- -
-
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+

{{ item.name }}

+ {{ item.type }} +
+
+
+
+
+ +
+
+
diff --git a/src/app/modules/data/detail/data-detail.component.spec.ts b/src/app/modules/data/detail/data-detail.component.spec.ts index 8cf2f852..8714c1cc 100644 --- a/src/app/modules/data/detail/data-detail.component.spec.ts +++ b/src/app/modules/data/detail/data-detail.component.spec.ts @@ -1,18 +1,19 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */ /* - * Copyright (C) 2018, 2019 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ + +/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */ import {CUSTOM_ELEMENTS_SCHEMA, DebugElement} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {ActivatedRoute, RouterModule} from '@angular/router'; @@ -118,9 +119,10 @@ describe('DataDetailComponent', () => { }); it('should get a data item', () => { - comp.getItem(sampleThing.uid); + comp.getItem(sampleThing.uid, false); expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith( sampleThing.uid, + false, ); }); @@ -128,6 +130,7 @@ describe('DataDetailComponent', () => { comp.ionViewWillEnter(); expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith( sampleThing.uid, + false, ); }); @@ -135,6 +138,7 @@ describe('DataDetailComponent', () => { await comp.refresh(refresher); expect(DataDetailComponent.prototype.getItem).toHaveBeenCalledWith( sampleThing.uid, + true, ); expect(refresher.complete).toHaveBeenCalled(); }); diff --git a/src/app/modules/data/detail/data-detail.component.ts b/src/app/modules/data/detail/data-detail.component.ts index a6c380f8..cedcd1b4 100644 --- a/src/app/modules/data/detail/data-detail.component.ts +++ b/src/app/modules/data/detail/data-detail.component.ts @@ -1,20 +1,27 @@ /* - * Copyright (C) 2018, 2019 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ -import {Component} from '@angular/core'; +import { + Component, + ContentChild, + EventEmitter, + Input, + Output, + TemplateRef, +} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; -import {IonRefresher} from '@ionic/angular'; +import {IonRefresher, ViewWillEnter} from '@ionic/angular'; import {LangChangeEvent, TranslateService} from '@ngx-translate/core'; import { SCLanguageCode, @@ -26,6 +33,13 @@ import {DataProvider, DataScope} from '../data.provider'; import {FavoritesService} from '../../favorites/favorites.service'; import {take} from 'rxjs/operators'; import {Network} from '@capacitor/network'; +import {DataListContext} from '../list/data-list.component'; + +export interface ExternalDataLoadEvent { + uid: SCUuid; + forceReload: boolean; + resolve: (item: SCThings | null | undefined) => void; +} /** * A Component to display an SCThing detailed @@ -35,7 +49,7 @@ import {Network} from '@capacitor/network'; styleUrls: ['data-detail.scss'], templateUrl: 'data-detail.html', }) -export class DataDetailComponent { +export class DataDetailComponent implements ViewWillEnter { /** * The associated item * @@ -53,6 +67,25 @@ export class DataDetailComponent { */ isDisconnected: Promise; + @ContentChild(TemplateRef) contentTemplateRef: TemplateRef< + DataListContext + >; + + @Input() externalData = false; + + /** + * This is kind of a stupid situation where we would + * like to use the default header in overriding elements + * such as the assessment detail page, however the ionic + * back button will not work if the header is in a subcomponent + * which then means we have to copy and paste the header from + * here into the overriding component. + */ + @Input() defaultHeader = true; + + @Output() loadItem: EventEmitter = + new EventEmitter(); + /** * Type guard for SCSavableThing */ @@ -90,11 +123,22 @@ export class DataDetailComponent { * Provides data item with given UID * * @param uid Unique identifier of a thing + * @param forceReload Indicating whether cached data should be ignored */ - async getItem(uid: SCUuid) { + async getItem(uid: SCUuid, forceReload: boolean) { try { - const item = await this.dataProvider.get(uid, DataScope.Remote); - this.item = DataDetailComponent.isSCSavableThing(item) ? item.data : item; + const item = await (this.externalData + ? new Promise(resolve => + this.loadItem.emit({uid, forceReload, resolve}), + ) + : this.dataProvider.get(uid, DataScope.Remote)); + + this.item = !item + ? // eslint-disable-next-line unicorn/no-null + null + : DataDetailComponent.isSCSavableThing(item) + ? item.data + : item; } catch { // eslint-disable-next-line unicorn/no-null this.item = null; @@ -106,7 +150,7 @@ export class DataDetailComponent { */ async ionViewWillEnter() { const uid = this.route.snapshot.paramMap.get('uid') || ''; - await this.getItem(uid ?? ''); + await this.getItem(uid ?? '', false); // fallback to the saved item (from favorites) if (this.item === null) { this.favoritesService @@ -126,7 +170,7 @@ export class DataDetailComponent { * @param refresher Refresher component that triggers the update */ async refresh(refresher: IonRefresher) { - await this.getItem(this.route.snapshot.paramMap.get('uid') ?? ''); + await this.getItem(this.route.snapshot.paramMap.get('uid') ?? '', true); await refresher.complete(); } } diff --git a/src/app/modules/data/detail/data-detail.html b/src/app/modules/data/detail/data-detail.html index 1324fc86..1db918cd 100644 --- a/src/app/modules/data/detail/data-detail.html +++ b/src/app/modules/data/detail/data-detail.html @@ -1,4 +1,4 @@ - + @@ -8,11 +8,12 @@ + - + diff --git a/src/app/modules/data/list/data-list-item.component.ts b/src/app/modules/data/list/data-list-item.component.ts index 9966f69e..af2cff43 100644 --- a/src/app/modules/data/list/data-list-item.component.ts +++ b/src/app/modules/data/list/data-list-item.component.ts @@ -1,20 +1,21 @@ /* - * Copyright (C) 2018-2021 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ -import {Component, Input} from '@angular/core'; +import {Component, ContentChild, Input, TemplateRef} from '@angular/core'; import {SCThings} from '@openstapps/core'; import {DataRoutingService} from '../data-routing.service'; +import {DataListContext} from './data-list.component'; /** * Shows data items in lists such es search result @@ -35,6 +36,12 @@ export class DataListItemComponent { */ @Input() item: SCThings; + @Input() favoriteButton = true; + + @ContentChild(TemplateRef) contentTemplateRef: TemplateRef< + DataListContext + >; + constructor(private readonly dataRoutingService: DataRoutingService) {} /** diff --git a/src/app/modules/data/list/data-list-item.html b/src/app/modules/data/list/data-list-item.html index 5517e0bd..3dc68598 100644 --- a/src/app/modules/data/list/data-list-item.html +++ b/src/app/modules/data/list/data-list-item.html @@ -8,66 +8,83 @@ + + +
+ +
+
+
+ + + + +
@@ -87,5 +104,4 @@ >
- - + diff --git a/src/app/modules/data/list/data-list.component.ts b/src/app/modules/data/list/data-list.component.ts index 46ed10a1..fadb9000 100644 --- a/src/app/modules/data/list/data-list.component.ts +++ b/src/app/modules/data/list/data-list.component.ts @@ -1,20 +1,21 @@ /* - * Copyright (C) 2021 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ import {CdkVirtualScrollViewport} from '@angular/cdk/scrolling'; import { Component, + ContentChild, EventEmitter, HostListener, Input, @@ -23,11 +24,16 @@ import { OnInit, Output, SimpleChanges, + TemplateRef, ViewChild, } from '@angular/core'; import {SCThings} from '@openstapps/core'; import {BehaviorSubject, Observable, Subscription} from 'rxjs'; +export interface DataListContext { + $implicit: T; +} + /** * Shows the list of items */ @@ -47,6 +53,10 @@ export class DataListComponent implements OnChanges, OnInit, OnDestroy { */ @Input() items?: SCThings[]; + @ContentChild(TemplateRef) listItemTemplateRef: TemplateRef< + DataListContext + >; + /** * Stream of SCThings for virtual scroll to consume */ diff --git a/src/app/modules/data/list/data-list.html b/src/app/modules/data/list/data-list.html index bd871310..225b9d00 100644 --- a/src/app/modules/data/list/data-list.html +++ b/src/app/modules/data/list/data-list.html @@ -6,12 +6,16 @@ (scrolledIndexChange)="scrolled($event)" [style.display]="items && items.length ? 'block' : 'none'" > + - + + + @@ -28,3 +32,10 @@ *ngFor="let skeleton of [].constructor(skeletonItems)" > + + + + diff --git a/src/app/modules/data/list/simple-data-list.component.ts b/src/app/modules/data/list/simple-data-list.component.ts index 8bf36e85..bec8a807 100644 --- a/src/app/modules/data/list/simple-data-list.component.ts +++ b/src/app/modules/data/list/simple-data-list.component.ts @@ -1,22 +1,30 @@ /* - * Copyright (C) 2021 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ -import {Component, Input, OnDestroy, OnInit} from '@angular/core'; +import { + Component, + ContentChild, + Input, + OnDestroy, + OnInit, + TemplateRef, +} from '@angular/core'; import {SCThings} from '@openstapps/core'; import {Subscription} from 'rxjs'; import {Router} from '@angular/router'; import {DataRoutingService} from '../data-routing.service'; +import {DataListContext} from './data-list.component'; /** * Shows the list of items @@ -30,18 +38,24 @@ export class SimpleDataListComponent implements OnInit, OnDestroy { /** * All SCThings to display */ - @Input() items?: SCThings[]; + @Input() items?: Promise; /** * Indicates whether or not the list is to display SCThings of a single type */ @Input() singleType = false; + @Input() autoRouting = true; + /** * List header */ @Input() listHeader?: string; + @ContentChild(TemplateRef) listItemTemplateRef: TemplateRef< + DataListContext + >; + /** * Items that display the skeleton list */ @@ -58,6 +72,7 @@ export class SimpleDataListComponent implements OnInit, OnDestroy { ) {} ngOnInit(): void { + if (!this.autoRouting) return; this.subscriptions.push( this.dataRoutingService.itemSelectListener().subscribe(item => { void this.router.navigate(['data-detail', item.uid]); diff --git a/src/app/modules/data/list/simple-data-list.html b/src/app/modules/data/list/simple-data-list.html index 20778d73..2934ccc6 100644 --- a/src/app/modules/data/list/simple-data-list.html +++ b/src/app/modules/data/list/simple-data-list.html @@ -1,11 +1,14 @@ - + + + @@ -26,3 +29,9 @@ + + + diff --git a/src/app/modules/hebis/daia-availability/daia-availability.component.spec.ts b/src/app/modules/hebis/daia-availability/daia-availability.component.spec.ts index 9afda024..c912a596 100644 --- a/src/app/modules/hebis/daia-availability/daia-availability.component.spec.ts +++ b/src/app/modules/hebis/daia-availability/daia-availability.component.spec.ts @@ -1,18 +1,19 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */ /* - * Copyright (C) 2018, 2019 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ + +/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */ import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {ActivatedRoute, RouterModule} from '@angular/router'; diff --git a/src/app/modules/hebis/hebis-detail/hebis-detail.component.spec.ts b/src/app/modules/hebis/hebis-detail/hebis-detail.component.spec.ts index 5a1055d7..51de1074 100644 --- a/src/app/modules/hebis/hebis-detail/hebis-detail.component.spec.ts +++ b/src/app/modules/hebis/hebis-detail/hebis-detail.component.spec.ts @@ -1,17 +1,19 @@ /* - * Copyright (C) 2018, 2019 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ + +/* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-non-null-assertion,@typescript-eslint/no-explicit-any */ import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; @@ -110,9 +112,10 @@ describe('HebisDetailComponent', () => { it('should create component', () => expect(comp).toBeDefined()); it('should get a data item', () => { - comp.getItem(sampleThing.uid); + comp.getItem(sampleThing.uid, false); expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith( sampleThing.uid, + false, ); }); @@ -120,6 +123,7 @@ describe('HebisDetailComponent', () => { comp.ionViewWillEnter(); expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith( sampleThing.uid, + false, ); }); @@ -127,6 +131,7 @@ describe('HebisDetailComponent', () => { await comp.refresh(refresher); expect(HebisDetailComponent.prototype.getItem).toHaveBeenCalledWith( sampleThing.uid, + true, ); expect(refresher.complete).toHaveBeenCalled(); }); diff --git a/src/app/modules/hebis/hebis-detail/hebis-detail.component.ts b/src/app/modules/hebis/hebis-detail/hebis-detail.component.ts index ce053f12..2f79fff4 100644 --- a/src/app/modules/hebis/hebis-detail/hebis-detail.component.ts +++ b/src/app/modules/hebis/hebis-detail/hebis-detail.component.ts @@ -1,16 +1,16 @@ /* - * Copyright (C) 2018-2022 StApps - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation, version 3. + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . */ import {Component} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; @@ -56,15 +56,16 @@ export class HebisDetailComponent extends DataDetailComponent { */ async ionViewWillEnter() { const uid = this.route.snapshot.paramMap.get('uid') || ''; - await this.getItem(uid ?? ''); + await this.getItem(uid ?? '', false); } /** * Provides data item with given UID * * @param uid Unique identifier of a thing + * @param _forceReload Ignore any cached data */ - async getItem(uid: SCUuid) { + async getItem(uid: SCUuid, _forceReload: boolean) { this.hebisDataProvider.hebisSearch({query: uid, page: 0}).then(result => { // eslint-disable-next-line unicorn/no-null this.item = (result.data && result.data[0]) || null; diff --git a/src/app/modules/profile/profile.module.ts b/src/app/modules/profile/profile.module.ts index fae13a46..6a502943 100644 --- a/src/app/modules/profile/profile.module.ts +++ b/src/app/modules/profile/profile.module.ts @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2022 StApps + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licens for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {FormsModule} from '@angular/forms'; diff --git a/src/app/translation/common-string-pipes.ts b/src/app/translation/common-string-pipes.ts index 572b17d5..18ae162f 100644 --- a/src/app/translation/common-string-pipes.ts +++ b/src/app/translation/common-string-pipes.ts @@ -356,6 +356,34 @@ export class MetersLocalizedPipe implements PipeTransform, OnDestroy { } } +@Injectable() +@Pipe({ + name: 'isNaN', + pure: true, +}) +export class IsNaNPipe implements PipeTransform { + transform(value: unknown): boolean { + return Number.isNaN(value); + } +} + +@Injectable() +@Pipe({ + name: 'isNumeric', + pure: true, +}) +export class IsNumericPipe implements PipeTransform { + transform(value: unknown): boolean { + return !Number.isNaN( + typeof value === 'number' + ? value + : typeof value === 'string' + ? Number.parseFloat(value) + : Number.NaN, + ); + } +} + @Injectable() @Pipe({ name: 'numberLocalized', diff --git a/src/app/translation/thing-translate.module.ts b/src/app/translation/thing-translate.module.ts index 5f4c5ef2..c2cb56f7 100644 --- a/src/app/translation/thing-translate.module.ts +++ b/src/app/translation/thing-translate.module.ts @@ -25,6 +25,8 @@ import { DurationLocalizedPipe, ToUnixPipe, EntriesPipe, + IsNaNPipe, + IsNumericPipe, } from './common-string-pipes'; import { ThingTranslateDefaultParser, @@ -56,6 +58,8 @@ export interface ThingTranslateModuleConfig { SentenceCasePipe, ToUnixPipe, EntriesPipe, + IsNaNPipe, + IsNumericPipe, ], exports: [ ArrayJoinPipe, @@ -71,6 +75,8 @@ export interface ThingTranslateModuleConfig { SentenceCasePipe, ToUnixPipe, EntriesPipe, + IsNaNPipe, + IsNumericPipe, ], }) export class ThingTranslateModule { diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json index 0ea9cb6a..cd3c78f7 100644 --- a/src/assets/i18n/de.json +++ b/src/assets/i18n/de.json @@ -15,6 +15,13 @@ "UNKNOWN": "Unbekannter Fehler." } }, + "assessments": { + "TITLE": "Noten", + "courseOfStudyAssessments": { + "PROGRESS": "Fortschritt", + "ASSESSMENTS": "Bewertungen" + } + }, "auth": { "messages": { "default": { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index b9c67519..aedac993 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -15,6 +15,13 @@ "UNKNOWN": "Unknown problem." } }, + "assessments": { + "TITLE": "Grades", + "courseOfStudyAssessments": { + "PROGRESS": "Progress", + "ASSESSMENTS": "Assessments" + } + }, "auth": { "messages": { "default": {