feat: assessments module

This commit is contained in:
Thea Schöbl
2022-03-17 09:59:52 +00:00
parent eea8d6d339
commit e68d1b73f9
51 changed files with 3372 additions and 222 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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 {}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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<SCAssessment[]>;
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<SCAssessment[]> {
// 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;
}
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@@ -0,0 +1,32 @@
<!--
~ 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 <https://www.gnu.org/licenses/>.
-->
<ion-header>
<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>{{ 'data.detail.TITLE' | translate }}</ion-title>
</ion-toolbar>
</ion-header>
<stapps-data-detail
[externalData]="true"
(loadItem)="getItem($event)"
[defaultHeader]="false"
>
<ng-template let-item>
<assessment-detail [item]="item"></assessment-detail>
</ng-template>
</stapps-data-detail>

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
stapps-data-detail {
height: 100%;
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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<void>();
/**
* Emits when scroll view should reset to top
*/
@Input() resetToTop?: Observable<void>;
/**
* 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;
}

View File

@@ -0,0 +1,32 @@
<!--
~ 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 <https://www.gnu.org/licenses/>.
-->
<stapps-data-list
[items]="items"
[loading]="loading"
[singleType]="singleType"
[resetToTop]="resetToTop"
(loadmore)="loadMore.emit($event)"
>
<ng-template let-item>
<assessments-list-item
[item]="item"
[hideThumbnail]="singleType"
></assessments-list-item>
</ng-template>
<ng-container header>
<ng-content select="[header]"></ng-content>
</ng-container>
</stapps-data-list>

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}

View File

@@ -0,0 +1,24 @@
<!--
~ 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 <https://www.gnu.org/licenses/>.
-->
<stapps-data-list-item
[item]="item"
[hideThumbnail]="hideThumbnail"
[favoriteButton]="false"
>
<ng-template let-data>
<stapps-assessment-list-item [item]="data"></stapps-assessment-list-item>
</ng-template>
</stapps-data-list-item>

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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<SCThings[] | undefined>;
/**
* 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();
}
}

View File

@@ -0,0 +1,28 @@
<!--
~ 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 <https://www.gnu.org/licenses/>.
-->
<stapps-simple-data-list
[singleType]="singleType"
[items]="_items"
[listHeader]="listHeader"
[autoRouting]="false"
>
<ng-template let-item>
<assessments-list-item
[item]="item"
[hideThumbnail]="singleType"
></assessments-list-item>
</ng-template>
</stapps-simple-data-list>

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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<SCCourseOfStudy | undefined>;
}
>
>;
assessmentKeys: string[] = [];
routingSubscription: Subscription;
@ViewChild('segment') segmentView!: HTMLIonSegmentElement;
sharedAxisChoreographer: SharedAxisChoreographer<string> =
new SharedAxisChoreographer<string>('', []);
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<SCCourseOfStudy>,
})),
);
} catch (error) {
this.logger.error(error);
this.assessments = Promise.resolve({});
}
});
}
}

View File

@@ -0,0 +1,49 @@
<ion-header>
<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-back-button></ion-back-button>
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title>{{ 'assessments.TITLE' | translate }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-segment
#segment
(ionChange)="sharedAxisChoreographer.changeViewForState(segment.value)"
value=""
>
<ion-segment-button *ngFor="let key of assessmentKeys" [value]="key">
<div *ngIf="assessments | async as assessments">
<ion-label
*ngIf="
assessments[key].courseOfStudy | async as course;
else defaultLabel
"
>
{{ 'name' | thingTranslate: course }} ({{
'academicDegree' | thingTranslate: course
}})
</ion-label>
</div>
<ng-template #defaultLabel>
<ion-label>{{ key }}</ion-label>
</ng-template>
</ion-segment-button>
</ion-segment>
<ion-content>
<div
[ngSwitch]="sharedAxisChoreographer.currentValue"
[@materialSharedAxisX]="sharedAxisChoreographer.animationState"
(@materialSharedAxisX.done)="sharedAxisChoreographer.animationDone()"
*ngIf="assessments | async as items"
class="content"
>
<course-of-study-assessment
[assessments]="items[sharedAxisChoreographer.currentValue].assessments"
[courseOfStudy]="
items[sharedAxisChoreographer.currentValue].courseOfStudy | async
"
></course-of-study-assessment>
</div>
</ion-content>

View File

@@ -0,0 +1,3 @@
.content {
height: 100%;
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@@ -0,0 +1,30 @@
<!--
~ 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 <https://www.gnu.org/licenses/>.
-->
<ion-label [color]="passed ? undefined : 'danger'"
>{{
(_item.grade | isNumeric)
? (_item.grade
| numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1')
: ''
}}
{{ 'status' | thingTranslate: _item | titlecase }},
{{ 'attempt' | propertyNameTranslate: _item }}
{{ _item.attempt }}
</ion-label>
<ion-note>
{{ _item.ects }}
{{ 'ects' | propertyNameTranslate: _item }}</ion-note
>

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}

View File

@@ -0,0 +1,18 @@
<ion-note *ngIf="item.courseOfStudy as courseOfStudy">
{{ $any('courseOfStudy' | propertyNameTranslate: item) | titlecase }}:
{{ 'name' | thingTranslate: $any(courseOfStudy) }}
({{ 'academicDegree' | thingTranslate: $any(courseOfStudy) }})
</ion-note>
<ion-list class="container">
<ion-item lines="none">
<assessment-base-info [item]="item"></assessment-base-info>
</ion-item>
<h2 *ngIf="item.superAssessments">
{{ $any('superAssessments' | propertyNameTranslate: item) | titlecase }}
</h2>
<assessments-simple-data-list
*ngIf="item.superAssessments"
[items]="$any(item.superAssessments)"
[singleType]="true"
></assessments-simple-data-list>
</ion-list>

View File

@@ -0,0 +1,4 @@
stapps-data-list {
height: 100px;
width: 100%;
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}

View File

@@ -0,0 +1,5 @@
<h2 class="name">
{{ 'name' | thingTranslate: item }}
{{ item.date ? (item.date | amDateFormat) : '' }}
</h2>
<assessment-base-info [item]="item"></assessment-base-info>

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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');
}
}

View File

@@ -0,0 +1,37 @@
<!--
~ 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 <https://www.gnu.org/licenses/>.
-->
<assessments-data-list
[singleType]="true"
[items]="_assessments"
[loading]="false"
>
<div header>
<section>
<h3>{{ 'assessments.courseOfStudyAssessments.PROGRESS' | translate }}</h3>
<p>
{{ $any('grade' | propertyNameTranslate: 'assessment') | titlecase }}:
{{
grade
| numberLocalized: 'minimumFractionDigits:1,maximumFractionDigits:1'
}}
</p>
<p>{{ 'ects' | propertyNameTranslate: 'assessment' }}: {{ ects }}</p>
</section>
<h3>
{{ 'assessments.courseOfStudyAssessments.ASSESSMENTS' | translate }}
</h3>
</div>
</assessments-data-list>

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/