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

Learning analytics: Add competency details to student course dashboard #8570

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
4598492
Add DB migration for dashboard feature
kaancayli Apr 30, 2024
9c620a6
Implement initial dashboard UI
kaancayli Apr 2, 2024
a948526
Implement initial dashboard UI
kaancayli Apr 2, 2024
c383325
Add translations and bugfixes
kaancayli Apr 3, 2024
25e8aca
Add feature toggle for dashboard
kaancayli Apr 30, 2024
e85e8b3
Add toggle switch for dashboard on course page
kaancayli Apr 30, 2024
f296f38
Clean up the dashboard page for the new UI
kaancayli Apr 30, 2024
ab7e629
Adjust tests for dashboardEnabled
kaancayli Apr 30, 2024
2396566
Fix dashboard visibility
kaancayli Apr 30, 2024
ffb9ece
Add empty css file
kaancayli Apr 30, 2024
a662753
Fix import errors
kaancayli Apr 30, 2024
12e0625
Add work in progress dislaimer to the page. Reorder sidebar elements
kaancayli Apr 30, 2024
c1469ae
Update german translations
kaancayli Apr 30, 2024
71d623a
Delete unnecessary files and code.
kaancayli Apr 30, 2024
ceb248d
Rename Dashboard to StudentCourseAnalyticsDashboard
kaancayli Apr 30, 2024
54aaa22
Add tests
kaancayli Apr 30, 2024
b939136
Bugfix and translation updates
kaancayli Apr 30, 2024
0d812aa
Fix broken FeatureToggleServiceTest.java
kaancayli Apr 30, 2024
10f7780
Fix broken client feature toggle tests.
kaancayli Apr 30, 2024
8386e6b
Remove public modifier from StudentLearningAnalyticsIntegrationTest c…
kaancayli Apr 30, 2024
813198a
Add client tests
kaancayli May 1, 2024
88315b1
Remove duplicate translation key
kaancayli May 2, 2024
0ea83aa
Add one small test
kaancayli May 2, 2024
e42eac9
Merge develop
kaancayli May 10, 2024
fcd0552
feat: competency accordion
kaancayli May 4, 2024
0f86937
feat: competency accordion
kaancayli May 6, 2024
1805c00
Add server side implementation
kaancayli May 7, 2024
40b9ed4
Adjust client side implementation
kaancayli May 8, 2024
29709b6
Sort competencies according to soft due date
kaancayli May 8, 2024
da32a1a
Add show learning path button
kaancayli May 8, 2024
88dc2ae
Scroll active competency to view
kaancayli May 10, 2024
be78d72
Add tests
kaancayli May 10, 2024
0c85650
Solve naming conflicts and import errors due to rebase
kaancayli May 10, 2024
fc1e9ec
Add translations
kaancayli May 10, 2024
adf7e40
Fix border radius
kaancayli May 10, 2024
10bdfb9
Add tooltips
kaancayli May 10, 2024
1412887
Adjust tests
kaancayli May 10, 2024
82fa6d8
Fix exercise completion
kaancayli May 14, 2024
201a25a
Merge hd3-develop into
kaancayli May 15, 2024
30a5647
Address feedback
kaancayli May 15, 2024
268874e
Merge branch 'hd3-develop' of github.com:ls1intum/Artemis into featur…
kaancayli May 16, 2024
fc9fe1e
Merge branch 'hd3-develop' of github.com:ls1intum/Artemis into featur…
kaancayli May 16, 2024
427d51c
Fetch competency details from metrics endpoint
kaancayli May 16, 2024
30eaaac
FE - delete old code
kaancayli May 16, 2024
dc784ab
BE - delete old code
kaancayli May 16, 2024
74f72aa
chore: small refactoring
kaancayli May 16, 2024
655d6de
Restore accidental deletion
kaancayli May 16, 2024
63cc2b7
fix accordion animation and border radius
FelixTJDietrich May 16, 2024
f7c9157
remove duplicate transition property
FelixTJDietrich May 16, 2024
1ae1a06
refactor with some minor improvements
FelixTJDietrich May 16, 2024
47c6dc8
Revert "Restore accidental deletion"
kaancayli May 16, 2024
f812ac3
Merge branch 'feature/learning-analytics/competency-accordion' of git…
kaancayli May 16, 2024
b3306b0
Merge branch 'refs/heads/hd3-develop' into feature/learning-analytics…
FelixTJDietrich May 17, 2024
5bf8cf4
Revert "Restore accidental deletion"
FelixTJDietrich May 17, 2024
fbdd1d8
Merge remote-tracking branch 'origin/feature/learning-analytics/compe…
FelixTJDietrich May 17, 2024
2fe1cff
Revert "Revert "Restore accidental deletion""
FelixTJDietrich May 17, 2024
dddbbdc
fix indentation of idea file
FelixTJDietrich May 17, 2024
3078cdd
fix rounding in course dashboard
FelixTJDietrich May 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<div>
<button
[id]="'competency-accordion-' + competency.id"
class="competency-accordion-header d-flex flex-column flex-sm-row align-items-start align-items-sm-center justify-between gap-2 px-4"
[class.closed]="!open"
(click)="toggle()"
>
<h4 class="fw-medium mb-0 py-2 col-sm-6 d-flex gap-2">
<fa-icon
[icon]="getIcon(competency.taxonomy)"
[fixedWidth]="true"
[ngbTooltip]="'artemisApp.competency.taxonomies.' + competency.taxonomy | artemisTranslate"
container="body"
/>
{{ competency.title }}
</h4>
<div class="w-100 d-flex align-items-center gap-4">
<div class="w-100 d-flex flex-column gap-2">
<div class="d-flex gap-3 align-items-center justify-content-center">
<fa-icon [icon]="faList" [ngbTooltip]="'artemisApp.studentAnalyticsDashboard.competencyAccordion.exercises' | artemisTranslate" />
<ngb-progressbar
class="w-100"
type="success overflow-visible"
[value]="exercisesProgress"
[max]="100"
height="0.75rem"
[ngbTooltip]="'artemisApp.studentAnalyticsDashboard.competencyAccordion.exerciseProgress' | artemisTranslate: { progress: round(exercisesProgress, 1) }"
/>
</div>
<div class="d-flex gap-3 align-items-center justify-content-center">
<fa-icon [icon]="faPdf" [ngbTooltip]="'artemisApp.studentAnalyticsDashboard.competencyAccordion.lectures' | artemisTranslate" />
<ngb-progressbar
class="w-100"
type="success overflow-visible"
[value]="lectureUnitsProgress"
[max]="100"
height="0.75rem"
[ngbTooltip]="'artemisApp.studentAnalyticsDashboard.competencyAccordion.lectureProgress' | artemisTranslate: { progress: round(lectureUnitsProgress, 1) }"
/>
</div>
</div>
<div class="py-2" style="max-width: 65px">
<jhi-competency-rings [confidence]="this.confidence" [progress]="this.progress" [mastery]="this.mastery" [hideTooltip]="false" [playAnimation]="false" />
kaancayli marked this conversation as resolved.
Show resolved Hide resolved
</div>
kaancayli marked this conversation as resolved.
Show resolved Hide resolved
</div>
</button>
<div [class.competency-accordion-body-open]="open" [class.competency-accordion-body-closed]="!open" class="competency-accordion-body">
<button
(click)="navigateToCompetencyDetailPage($event)"
class="btn btn-primary w-100 my-2"
jhiTranslate="artemisApp.studentAnalyticsDashboard.button.viewLecturesAndExercises"
></button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.competency-accordion-header {
background-color: var(--overview-light-background-color);
color: var(--bs-body-color);
cursor: pointer;
padding: 5px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
border-radius: 3px 3px 0 0;
transition: 0.2s;
}

.competency-accordion-header.closed {
border-radius: 3px;
}

.competency-accordion-header:not([disabled]):hover {
background-color: rgba(100, 100, 111, 0.2);
}

.competency-accordion-ring {
width: 95px;
}

.competency-accordion-body {
padding: 0 18px;
background-color: var(--overview-light-background-color);
color: var(--bs-body-color);
overflow: hidden;
width: 100%;
transition:
max-height 0.2s ease-out,
padding 0.2s ease-out;
max-height: 0;
border-radius: 0 0 3px 3px;
}

/* Open state */
.competency-accordion-body-open {
max-height: 250px;
padding: 10px 18px;
}

/* Closed state */
.competency-accordion-body-closed {
max-height: 0;
padding: 0 18px;
}

@keyframes fadeIn {
from {
opacity: 0;
}
to {

Check notice on line 56 in src/main/webapp/app/course/competencies/competency-accordion/competency-accordion.component.scss

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/webapp/app/course/competencies/competency-accordion/competency-accordion.component.scss#L56

Expected empty line before rule (rule-empty-line-before)
opacity: 1;
}
}

.competency-accordion-header,

Check warning on line 61 in src/main/webapp/app/course/competencies/competency-accordion/competency-accordion.component.scss

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/webapp/app/course/competencies/competency-accordion/competency-accordion.component.scss#L61

Expected selector ".competency-accordion-header" to come before selector ".competency-accordion-header:not([disabled]):hover" (no-descending-specificity)
.competency-accordion-body {
margin: 0;
}

.competency-accordion-header .row > div,
.competency-accordion-body .row > div {
padding: 0 18px;
}

@media only screen and (max-width: 600px) {
.competency-accordion-body {
overflow-y: auto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { faFilePdf, faList } from '@fortawesome/free-solid-svg-icons';
import { CompetencyProgress, getConfidence, getIcon, getMastery, getProgress } from 'app/entities/competency.model';
import { Course } from 'app/entities/course.model';
import { Router } from '@angular/router';
import { ICompetencyAccordionToggleEvent } from 'app/shared/competency/interfaces/competency-accordion-toggle-event.interface';
import { CompetencyInformation, StudentMetrics } from 'app/entities/student-metrics.model';
import { round } from 'app/shared/util/utils';

@Component({
selector: 'jhi-competency-accordion',
templateUrl: './competency-accordion.component.html',
styleUrl: './competency-accordion.component.scss',
})
export class CompetencyAccordionComponent implements OnChanges {
@Input() course?: Course;
@Input() competency: CompetencyInformation;
@Input() metrics: StudentMetrics;
@Input() index: number;
@Input() openedIndex?: number;

@Output() accordionToggle = new EventEmitter<ICompetencyAccordionToggleEvent>();

open = false;

protected readonly faList = faList;
protected readonly faPdf = faFilePdf;
protected readonly getIcon = getIcon;
protected readonly getProgress = getProgress;
protected readonly getConfidence = getConfidence;
protected readonly getMastery = getMastery;
protected readonly round = round;

constructor(private router: Router) {}

ngOnChanges(changes: SimpleChanges) {
if (changes.openedIndex && this.index !== this.openedIndex) {
this.open = false;
}
}

toggle() {
this.open = !this.open;
this.accordionToggle.emit({ opened: this.open, index: this.index });
}

getUserProgress(): CompetencyProgress {
const progress = this.metrics.competencyMetrics?.progress[this.competency.id] ?? 0;
const confidence = this.metrics.competencyMetrics?.confidence[this.competency.id] ?? 0;
return { progress, confidence } as CompetencyProgress;
}

get progress() {
return this.getProgress(this.getUserProgress());
}

get lectureUnitsProgress() {
if (this.metrics.lectureUnitStudentMetricsDTO) {
const competencyLectureUnits = this.metrics.competencyMetrics?.lectureUnits[this.competency.id];
const completedLectureUnits = competencyLectureUnits?.filter((lectureUnitId) => this.metrics.lectureUnitStudentMetricsDTO?.completed?.includes(lectureUnitId)).length;
if (competencyLectureUnits && completedLectureUnits) {
const progress = (completedLectureUnits / competencyLectureUnits.length) * 100;
return round(progress, 1);
}
return 0;
}
return 0;
}

get exercisesProgress() {
const competencyExercises = this.metrics.competencyMetrics?.exercises[this.competency.id];
const completedExercises = competencyExercises?.filter((exerciseId) => this.metrics.exerciseMetrics?.completed?.includes(exerciseId)).length;
if (competencyExercises && completedExercises) {
const progress = (completedExercises / competencyExercises.length) * 100;
return round(progress, 1);
}
return 0;
}

get confidence() {
return this.getConfidence(this.getUserProgress(), this.competency.masteryThreshold!);
}

get mastery() {
return this.getMastery(this.getUserProgress(), this.competency.masteryThreshold!);
}

get competencyProgress() {
return {
progress: this.progress,
confidence: this.confidence,
} as CompetencyProgress;
}

navigateToCompetencyDetailPage(event: Event) {
event.stopPropagation();
this.router.navigate(['/courses', this.course!.id, 'competencies', this.competency.id]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<g class="ring ring1">
<circle class="background" cx="50%" cy="50%" r="15.915" stroke-width="3" />
<circle
[class.progressbar-anim]="playAnimation"
class="progressbar"
cx="50%"
cy="50%"
Expand All @@ -16,6 +17,7 @@
<g class="ring ring2">
<circle class="background" cx="50%" cy="50%" r="15.915" stroke-width="4" />
<circle
[class.progressbar-anim]="playAnimation"
class="progressbar"
cx="50%"
cy="50%"
Expand All @@ -29,6 +31,7 @@
<g class="ring ring3">
<circle class="background" cx="50%" cy="50%" r="15.915" stroke-width="6" />
<circle
[class.progressbar-anim]="playAnimation"
class="progressbar"
cx="50%"
cy="50%"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ svg {
transform-origin: 50%;
}
.progressbar {
animation: ring-appear 1s ease-in-out forwards;
transition:
stroke-dasharray 1s ease-in-out,
opacity 1s linear;
&-anim {
animation: ring-appear 1s ease-in-out forwards;
transition:
stroke-dasharray 1s ease-in-out,
opacity 1s linear;
}
stroke-linecap: round;

&.hidden {
Expand All @@ -39,9 +41,11 @@ svg {
stroke: var(--competency-rings-red-bg);
}
.progressbar {
&-anim {
animation-duration: 1.6s;
transition-duration: 1.6s;
}
stroke: var(--competency-rings-red);
animation-duration: 1.6s;
transition-duration: 1.6s;
}
}

Expand All @@ -53,8 +57,10 @@ svg {
}
.progressbar {
stroke: var(--competency-rings-green);
animation-duration: 1.3s;
transition-duration: 1.3s;
&-anim {
animation-duration: 1.3s;
transition-duration: 1.3s;
}
}
}

Expand All @@ -65,9 +71,11 @@ svg {
stroke: var(--competency-rings-blue-bg);
}
.progressbar {
&-anim {
animation-duration: 1s;
transition-duration: 1s;
}
stroke: var(--competency-rings-blue);
animation-duration: 1s;
transition-duration: 1s;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class CompetencyRingsComponent {
@Input() progress = 0;
@Input() confidence = 0;
@Input() mastery = 0;
@Input() playAnimation = true;
@Input() hideTooltip = false;

get progressPercentage(): number {
Expand Down
7 changes: 5 additions & 2 deletions src/main/webapp/app/course/competencies/competency.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { PrerequisiteImportComponent } from 'app/course/competencies/competency-
import { NgxGraphModule } from '@swimlane/ngx-graph';
import { CompetencyRingsComponent } from 'app/course/competencies/competency-rings/competency-rings.component';
import { FormDateTimePickerModule } from 'app/shared/date-time-picker/date-time-picker.module';
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
import { NgbAccordionModule, NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { GenerateCompetenciesComponent } from 'app/course/competencies/generate-competencies/generate-competencies.component';
import { CompetencyRecommendationDetailComponent } from 'app/course/competencies/generate-competencies/competency-recommendation-detail.component';
import { CourseDescriptionFormComponent } from 'app/course/competencies/generate-competencies/course-description-form.component';
Expand All @@ -25,6 +25,7 @@ import { CompetencySearchComponent } from 'app/course/competencies/import-compet
import { ImportCompetenciesTableComponent } from 'app/course/competencies/import-competencies/import-competencies-table.component';
import { TaxonomySelectComponent } from 'app/course/competencies/taxonomy-select/taxonomy-select.component';
import { CompetencyRelationGraphComponent } from 'app/course/competencies/competency-management/competency-relation-graph.component';
import { CompetencyAccordionComponent } from 'app/course/competencies/competency-accordion/competency-accordion.component';
import { CourseImportStandardizedCompetenciesComponent } from 'app/course/competencies/import-standardized-competencies/course-import-standardized-competencies.component';
import { ArtemisStandardizedCompetencyModule } from 'app/shared/standardized-competencies/standardized-competency.module';

Expand All @@ -34,6 +35,7 @@ import { ArtemisStandardizedCompetencyModule } from 'app/shared/standardized-com
FormsModule,
ReactiveFormsModule,
NgxGraphModule,
NgbModule,
ArtemisSharedComponentModule,
RouterModule,
FormDateTimePickerModule,
Expand All @@ -54,6 +56,7 @@ import { ArtemisStandardizedCompetencyModule } from 'app/shared/standardized-com
CourseDescriptionFormComponent,
CompetencyManagementComponent,
CompetencyCardComponent,
CompetencyAccordionComponent,
CompetenciesPopoverComponent,
PrerequisiteImportComponent,
CompetencyImportCourseComponent,
Expand All @@ -62,6 +65,6 @@ import { ArtemisStandardizedCompetencyModule } from 'app/shared/standardized-com
CompetencyRelationGraphComponent,
CourseImportStandardizedCompetenciesComponent,
],
exports: [CompetencyCardComponent, CompetenciesPopoverComponent, CompetencyFormComponent, CompetencyRingsComponent, TaxonomySelectComponent],
exports: [CompetencyCardComponent, CompetencyAccordionComponent, CompetenciesPopoverComponent, CompetencyFormComponent, CompetencyRingsComponent, TaxonomySelectComponent],
})
export class ArtemisCompetenciesModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export class CompetencyService {
tap((res: EntityArrayResponseType) => res?.body?.forEach(this.sendTitlesToEntityTitleService.bind(this))),
);
}

getProgress(competencyId: number, courseId: number, refresh = false) {
let params = new HttpParams();
params = params.set('refresh', refresh.toString());
Expand Down
1 change: 1 addition & 0 deletions src/main/webapp/app/entities/exercise.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export abstract class Exercise implements BaseEntity {
public averageRating?: number;
public numberOfRatings?: number;
public channelName?: string;
public completed?: boolean;

// helper attributes
public secondCorrectionEnabled = false;
Expand Down
Loading
Loading