Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adaptive learning: Add source display to standardized competencies #8638

Merged
merged 10 commits into from
May 24, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@

import de.tum.in.www1.artemis.domain.competency.KnowledgeArea;
import de.tum.in.www1.artemis.domain.competency.StandardizedCompetency;
import de.tum.in.www1.artemis.repository.SourceRepository;
import de.tum.in.www1.artemis.repository.competency.KnowledgeAreaRepository;
import de.tum.in.www1.artemis.repository.competency.StandardizedCompetencyRepository;
import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastInstructor;
import de.tum.in.www1.artemis.service.competency.StandardizedCompetencyService;
import de.tum.in.www1.artemis.web.rest.dto.standardizedCompetency.KnowledgeAreaResultDTO;
import de.tum.in.www1.artemis.web.rest.dto.standardizedCompetency.SourceDTO;

/**
* REST controller for managing {@link StandardizedCompetency} entities.
Expand All @@ -37,11 +39,14 @@ public class StandardizedCompetencyResource {

private final KnowledgeAreaRepository knowledgeAreaRepository;

private final SourceRepository sourceRepository;

public StandardizedCompetencyResource(StandardizedCompetencyService standardizedCompetencyService, StandardizedCompetencyRepository standardizedCompetencyRepository,
KnowledgeAreaRepository knowledgeAreaRepository) {
KnowledgeAreaRepository knowledgeAreaRepository, SourceRepository sourceRepository) {
this.standardizedCompetencyService = standardizedCompetencyService;
this.standardizedCompetencyRepository = standardizedCompetencyRepository;
this.knowledgeAreaRepository = knowledgeAreaRepository;
this.sourceRepository = sourceRepository;
}

/**
Expand Down Expand Up @@ -90,4 +95,19 @@ public ResponseEntity<KnowledgeArea> getKnowledgeArea(@PathVariable long knowled

return ResponseEntity.ok().body(knowledgeArea);
}

/**
* GET api/standardized-competencies/sources : Gets all sources
*
* @return the ResponseEntity with status 200 (OK) and with body containing the list of sources
*/
@GetMapping("sources")
@EnforceAtLeastInstructor
public ResponseEntity<List<SourceDTO>> getSources() {
log.debug("REST request to get all sources");

var sourceDTOs = sourceRepository.findAll().stream().map(SourceDTO::of).toList();

return ResponseEntity.ok().body(sourceDTOs);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package de.tum.in.www1.artemis.web.rest.dto.standardizedCompetency;

import de.tum.in.www1.artemis.domain.competency.Source;

/**
* DTO containing source information
*/
public record SourceDTO(long id, String title, String author, String uri) {

/**
* Creates a SourceDTO from the given Source
*
* @param source the Source
* @return the created SourceDTO
*/
public static SourceDTO of(Source source) {
return new SourceDTO(source.getId(), source.getTitle(), source.getAuthor(), source.getUri());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ <h6 class="mb-0">{{ competency.title }}</h6>
</jhi-knowledge-area-tree>
@if (selectedCompetency) {
<div style="background-color: var(--overview-light-background-color)" class="card d-flex flex-grow-1 w-100 h-100 p-3 ms-1">
<jhi-standardized-competency-detail [competency]="selectedCompetency" [knowledgeAreaTitle]="knowledgeAreaTitle" (onClose)="closeCompetencyDetails()" />
<jhi-standardized-competency-detail
[competency]="selectedCompetency"
[knowledgeAreaTitle]="knowledgeAreaTitle"
[sourceString]="sourceString"
(onClose)="closeCompetencyDetails()"
/>
</div>
}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
KnowledgeAreasForImportDTO,
StandardizedCompetencyForTree,
convertToKnowledgeAreaForTree,
sourceToString,
} from 'app/entities/competency/standardized-competency.model';
import { MAX_FILE_SIZE } from 'app/shared/constants/input.constants';
import { AlertService } from 'app/core/util/alert.service';
Expand Down Expand Up @@ -34,6 +35,7 @@ export class AdminImportStandardizedCompetenciesComponent {
protected selectedCompetency?: StandardizedCompetencyForTree;
//the title of the knowledge area belonging to the selected competency
protected knowledgeAreaTitle = '';
protected sourceString = '';
protected importData?: KnowledgeAreasForImportDTO;
protected importCount?: ImportCount;
protected dataSource = new MatTreeNestedDataSource<KnowledgeAreaForTree>();
Expand Down Expand Up @@ -111,11 +113,14 @@ export class AdminImportStandardizedCompetenciesComponent {
}

protected openCompetencyDetails(competency: StandardizedCompetencyForTree, knowledgeAreaTitle: string) {
const source = this.importData?.sources.find((source) => source.id === competency.sourceId);
this.sourceString = source ? sourceToString(source) : '';
this.knowledgeAreaTitle = knowledgeAreaTitle;
this.selectedCompetency = competency;
}

protected closeCompetencyDetails() {
this.sourceString = '';
this.knowledgeAreaTitle = '';
this.selectedCompetency = undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,18 @@ <h4 jhiTranslate="artemisApp.standardizedCompetency.details.titleCreate"></h4>
<div class="alert alert-danger" jhiTranslate="artemisApp.standardizedCompetency.details.error.knowledgeAreaRequired"></div>
}
</div>
<!-- TODO: add source handling in follow-up PR -->
<div class="form-group">
<label for="source-select" jhiTranslate="artemisApp.standardizedCompetency.model.source"></label>
<select id="source-select" class="form-select" formControlName="sourceId">
<option [ngValue]="undefined"></option>
dmytropolityka marked this conversation as resolved.
Show resolved Hide resolved
@for (source of sources; track source.id) {
<option [ngValue]="source.id">
<!-- omit uri to keep the select short -->
{{ source.author ? source.author + '. ' : '' }}"{{ source.title }}"
</option>
}
</select>
</div>
</div>
@if (isEditing) {
<div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { faBan, faPencil, faSave, faTrash } from '@fortawesome/free-solid-svg-icons';
import { KnowledgeArea, StandardizedCompetencyDTO, StandardizedCompetencyValidators } from 'app/entities/competency/standardized-competency.model';
import { KnowledgeArea, Source, StandardizedCompetencyDTO, StandardizedCompetencyValidators } from 'app/entities/competency/standardized-competency.model';
import { ButtonSize, ButtonType } from 'app/shared/components/button.component';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { CompetencyTaxonomy } from 'app/entities/competency.model';
Expand All @@ -13,13 +13,16 @@ import { Observable } from 'rxjs';
export class StandardizedCompetencyEditComponent {
// values for the knowledge area select
@Input() knowledgeAreas: KnowledgeArea[] = [];
// values for the source select
@Input() sources: Source[] = [];
@Input({ required: true }) set competency(competency: StandardizedCompetencyDTO) {
this._competency = competency;
this.form = this.formBuilder.nonNullable.group({
title: [competency.title, [Validators.required, Validators.maxLength(StandardizedCompetencyValidators.TITLE_MAX)]],
description: [competency.description, [Validators.maxLength(StandardizedCompetencyValidators.DESCRIPTION_MAX)]],
taxonomy: [competency.taxonomy],
knowledgeAreaId: [competency.knowledgeAreaId, [Validators.required]],
sourceId: [competency.sourceId],
});
if (!this.isEditing) {
this.form.disable();
Expand Down Expand Up @@ -53,18 +56,19 @@ export class StandardizedCompetencyEditComponent {

private _isEditing: boolean;
private _competency: StandardizedCompetencyDTO;
form: FormGroup<{
protected form: FormGroup<{
title: FormControl<string | undefined>;
description: FormControl<string | undefined>;
taxonomy: FormControl<CompetencyTaxonomy | undefined>;
knowledgeAreaId: FormControl<number | undefined>;
sourceId: FormControl<number | undefined>;
}>;

// icons
readonly faPencil = faPencil;
readonly faTrash = faTrash;
readonly faBan = faBan;
readonly faSave = faSave;
protected readonly faPencil = faPencil;
protected readonly faTrash = faTrash;
protected readonly faBan = faBan;
protected readonly faSave = faSave;
// other constants
protected readonly ButtonSize = ButtonSize;
protected readonly ButtonType = ButtonType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ <h6 class="mb-0">{{ competency.title }}</h6>
<jhi-standardized-competency-edit
[competency]="selectedCompetency"
[knowledgeAreas]="knowledgeAreasForSelect"
[sources]="sourcesForSelect"
[(isEditing)]="isEditing"
(onSave)="saveCompetency($event)"
(onDelete)="deleteCompetency($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { faChevronRight, faDownLeftAndUpRightToCenter, faEye, faFileImport, faPl
import {
KnowledgeAreaDTO,
KnowledgeAreaForTree,
Source,
StandardizedCompetencyDTO,
convertToKnowledgeAreaForTree,
convertToStandardizedCompetencyForTree,
Expand All @@ -11,7 +12,7 @@ import { onError } from 'app/shared/util/global.utils';
import { AdminStandardizedCompetencyService } from 'app/admin/standardized-competencies/admin-standardized-competency.service';
import { HttpErrorResponse } from '@angular/common/http';
import { AlertService } from 'app/core/util/alert.service';
import { Subject, map } from 'rxjs';
import { Subject, forkJoin, map } from 'rxjs';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component';
import { getIcon } from 'app/entities/competency.model';
Expand All @@ -34,6 +35,8 @@ export class StandardizedCompetencyManagementComponent extends StandardizedCompe
protected selectedCompetency?: StandardizedCompetencyDTO;
// the knowledge area displayed in the detail component
protected selectedKnowledgeArea?: KnowledgeAreaDTO;
// the list of sources used for the select in the detail component
protected sourcesForSelect: Source[] = [];

// observable for the error button
private dialogErrorSource = new Subject<string>();
Expand Down Expand Up @@ -63,22 +66,25 @@ export class StandardizedCompetencyManagementComponent extends StandardizedCompe

ngOnInit() {
this.isLoading = true;
this.standardizedCompetencyService
.getAllForTreeView()
.pipe(map((response) => response.body!))
.subscribe({
next: (knowledgeAreas) => {
const knowledgeAreasForTree = knowledgeAreas.map((knowledgeArea) => convertToKnowledgeAreaForTree(knowledgeArea));
this.dataSource.data = knowledgeAreasForTree;
this.treeControl.dataNodes = knowledgeAreasForTree;
knowledgeAreasForTree.forEach((knowledgeArea) => {
this.addSelfAndDescendantsToMap(knowledgeArea);
this.addSelfAndDescendantsToSelectArray(knowledgeArea);
});
this.isLoading = false;
},
error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse),
});
const getKnowledgeAreasObservable = this.standardizedCompetencyService.getAllForTreeView();
const getSourcesObservable = this.standardizedCompetencyService.getSources();
forkJoin([getKnowledgeAreasObservable, getSourcesObservable]).subscribe({
next: ([knowledgeAreasResponse, sourcesResponse]) => {
this.sourcesForSelect = sourcesResponse.body!;
rstief marked this conversation as resolved.
Show resolved Hide resolved

const knowledgeAreas = knowledgeAreasResponse.body!;
const knowledgeAreasForTree = knowledgeAreas.map((knowledgeArea) => convertToKnowledgeAreaForTree(knowledgeArea));
this.dataSource.data = knowledgeAreasForTree;
this.treeControl.dataNodes = knowledgeAreasForTree;
knowledgeAreasForTree.forEach((knowledgeArea) => {
this.addSelfAndDescendantsToMap(knowledgeArea);
this.addSelfAndDescendantsToSelectArray(knowledgeArea);
});
rstief marked this conversation as resolved.
Show resolved Hide resolved

this.isLoading = false;
},
error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse),
});
}

ngOnDestroy(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ <h6 class="mb-0">{{ competency.title }}</h6>
<div style="background-color: var(--overview-light-background-color)" class="card d-flex flex-grow-1 w-100 h-100 p-3 ms-1">
<jhi-standardized-competency-detail
[competency]="selectedCompetency"
[sourceString]="sourceString"
[knowledgeAreaTitle]="selectedCompetency.knowledgeAreaTitle ?? ''"
(onClose)="closeCompetencyDetails()"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { getIcon } from 'app/entities/competency.model';
import { ButtonSize, ButtonType } from 'app/shared/components/button.component';
import { KnowledgeAreaDTO, KnowledgeAreaForTree, StandardizedCompetencyDTO, StandardizedCompetencyForTree } from 'app/entities/competency/standardized-competency.model';
import {
KnowledgeAreaDTO,
KnowledgeAreaForTree,
Source,
StandardizedCompetencyDTO,
StandardizedCompetencyForTree,
sourceToString,
} from 'app/entities/competency/standardized-competency.model';
import { faBan, faDownLeftAndUpRightToCenter, faFileImport, faSort, faTrash, faUpRightAndDownLeftFromCenter } from '@fortawesome/free-solid-svg-icons';
import { ActivatedRoute, Router } from '@angular/router';
import { Component, HostListener, OnInit } from '@angular/core';
import { onError } from 'app/shared/util/global.utils';
import { map } from 'rxjs';
import { forkJoin, map } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';
import { AlertService } from 'app/core/util/alert.service';
import { StandardizedCompetencyFilterPageComponent } from 'app/shared/standardized-competencies/standardized-competency-filter-page.component';
Expand All @@ -32,7 +39,9 @@ interface KnowledgeAreaForImport extends KnowledgeAreaForTree {
export class CourseImportStandardizedCompetenciesComponent extends StandardizedCompetencyFilterPageComponent implements OnInit, ComponentCanDeactivate {
protected selectedCompetencies: StandardizedCompetencyForImport[] = [];
protected selectedCompetency?: StandardizedCompetencyForImport;
protected sourceString = '';
private courseId: number;
private sources: Source[];
protected isLoading = false;
protected isSubmitted = false;

Expand Down Expand Up @@ -62,30 +71,36 @@ export class CourseImportStandardizedCompetenciesComponent extends StandardizedC

ngOnInit(): void {
this.isLoading = true;
this.standardizedCompetencyService
.getAllForTreeView()
.pipe(map((response) => response.body!))
.subscribe({
next: (knowledgeAreas) => {
const knowledgeAreasForImport = knowledgeAreas.map((knowledgeArea) => this.convertToKnowledgeAreaForImport(knowledgeArea));
this.dataSource.data = knowledgeAreasForImport;
this.treeControl.dataNodes = knowledgeAreasForImport;
knowledgeAreasForImport.forEach((knowledgeArea) => {
this.addSelfAndDescendantsToMap(knowledgeArea);
this.addSelfAndDescendantsToSelectArray(knowledgeArea);
});
this.isLoading = false;
},
error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse),
});
const getKnowledgeAreasObservable = this.standardizedCompetencyService.getAllForTreeView();
const getSourcesObservable = this.standardizedCompetencyService.getSources();
forkJoin([getKnowledgeAreasObservable, getSourcesObservable]).subscribe({
next: ([knowledgeAreasResponse, sourcesResponse]) => {
const knowledgeAreas = knowledgeAreasResponse.body!;
const knowledgeAreasForImport = knowledgeAreas.map((knowledgeArea) => this.convertToKnowledgeAreaForImport(knowledgeArea));
this.dataSource.data = knowledgeAreasForImport;
this.treeControl.dataNodes = knowledgeAreasForImport;
knowledgeAreasForImport.forEach((knowledgeArea) => {
this.addSelfAndDescendantsToMap(knowledgeArea);
this.addSelfAndDescendantsToSelectArray(knowledgeArea);
});

this.sources = sourcesResponse.body!;

this.isLoading = false;
},
error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse),
});
this.courseId = Number(this.activatedRoute.snapshot.paramMap.get('courseId'));
}

protected openCompetencyDetails(competency: StandardizedCompetencyForImport) {
const source = this.sources.find((source) => source.id === competency.sourceId);
this.sourceString = source ? sourceToString(source) : '';
this.selectedCompetency = competency;
}

protected closeCompetencyDetails() {
this.sourceString = '';
this.selectedCompetency = undefined;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ export interface StandardizedCompetencyForTree extends StandardizedCompetencyDTO
isVisible: boolean;
}

export function sourceToString(source: Source) {
const author = source.author ?? '';
const title = source.title ?? '';
const uri = source.uri ?? '';

if (!author) {
return `"${title}". ${uri}`;
} else {
return `${author}. "${title}". ${uri}`;
}
}

export function convertToStandardizedCompetencyForTree(competencyDTO: StandardizedCompetencyDTO, isVisible: boolean) {
const competencyForTree: StandardizedCompetencyForTree = { ...competencyDTO, isVisible: isVisible };
return competencyForTree;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ <h4 jhiTranslate="artemisApp.standardizedCompetency.details.title"></h4>
<div class="detail-body d-flex flex-column">
<div class="detail-row">
<h5 jhiTranslate="artemisApp.standardizedCompetency.model.title"></h5>
<p class="mb-0" id="competency-detail-title">{{ competency.title }}</p>
<p class="mb-0">{{ competency.title }}</p>
</div>
<div class="detail-row">
<h5 jhiTranslate="artemisApp.standardizedCompetency.model.description"></h5>
Expand All @@ -20,8 +20,11 @@ <h5 jhiTranslate="artemisApp.standardizedCompetency.model.taxonomy"></h5>
</div>
<div class="detail-row">
<h5 jhiTranslate="artemisApp.standardizedCompetency.model.knowledgeArea"></h5>
<p class="mb-0" id="competency-detail-knowledge-area">{{ knowledgeAreaTitle }}</p>
<p class="mb-0">{{ knowledgeAreaTitle }}</p>
</div>
<div class="detail-row">
<h5 jhiTranslate="artemisApp.standardizedCompetency.model.source"></h5>
<p class="mb-0">{{ sourceString }}</p>
</div>
<!-- TODO: add source handling in follow-up PR -->
</div>
</div>
Loading
Loading