Skip to content

Commit acfd4aa

Browse files
committed
Merge branch 'feat/central-loader-heatmap' into experiment
2 parents 7502fd9 + a19b087 commit acfd4aa

17 files changed

Lines changed: 545 additions & 204 deletions

TODO-central-loader.md

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,39 @@
22

33

44
## Doing
5-
- Load TeamProgress yaml
6-
- Store teamProgress as part of activityStore under each activity
7-
- How to deal with deleted progress??? (Do later)
8-
- Refactor Circular Heatmap
9-
- Update progress (when slider is moved) when new sector is selected
10-
- show progress for each activity on the slider
11-
- update progress for each activity in the sector, on teams change
125

136
## Next
14-
- Load TeamProgress yaml
15-
- Store in localStorage
16-
- Handle boolean for backwards compatibility
17-
- Merge localStorage and YAML
18-
- Progress Slider
19-
- Data model, contains date, so warning about downgrade can show date
20-
- Handle when previous value does not have a date (backwards compatibility)
21-
- Catch close to save new progresses
22-
- Store TeamProgress to localStorage
23-
- Load localStorage TeamProgress
247
- Export TeamProgress yaml
8+
- Read boolean from backwards compatibility storage
259
- Filters
2610
- filter teams
2711
- filter none => all
2812

2913

3014

3115
## Later
32-
- Add validation for meta.yaml, progress step: include 0% and 100%, and increacing values
3316
- Merge in experiment's way of generating circ heat
3417
- Fix dependsOn that is uuid (e.g. 83057028-0b77-4d2e-8135-40969768ae88)
3518
- Sort linear list of activities (sorted by level, dim)
3619
- Move META_FILE constant from data service to main app
3720
- Filter: tags: Fix update on SPACE key (trouble)
21+
- Circular, Card: Add Complete symbol per activity
22+
23+
- Dependency graph: Add to CircularHeatmap Details
24+
- Dependency graph: Make it clickable
25+
- Matrix: Remember filters, when moving back from details
26+
27+
- Teams: Allow editing teams names in browser
28+
- Teams: Store teams names in localstorage
29+
- Teams: Export teams YAML from teams page
30+
- Teams: View timeline for a team
3831

3932
# Done
33+
- Store TeamProgress to localStorage
34+
- Load localStorage TeamProgress
35+
- Load TeamProgress yaml
36+
- Refactor Circular Heatmap
37+
- Add validation for meta.yaml, progress step: include 0% and 100%
4038
- Load YAML progress
4139
- Navigate to activity-description without site reload
4240
- Refactor Dependecy graph

src/app/component/activity-description/activity-description.component.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ActivatedRoute } from '@angular/router';
44
import { LoaderService } from '../../service/loader/data-loader.service';
55
import * as md from 'markdown-it';
66
import { Activity, ActivityStore } from '../../model/activity-store';
7+
import { DataStore } from 'src/app/model/data-store';
78

89
@Component({
910
selector: 'app-activity-description',
@@ -36,15 +37,17 @@ export class ActivityDescriptionComponent implements OnInit {
3637
// Load data
3738
this.loader
3839
.load()
39-
.then((activityStore: ActivityStore) => {
40+
.then((dataStore: DataStore) => {
4041
// Find the activity with matching UUID (or potentially name)
41-
let activity: Activity = activityStore.getActivity(uuid, name);
42+
if (!dataStore.activityStore) throw Error("TODO: Must handle these");
43+
44+
let activity: Activity = dataStore.activityStore.getActivity(uuid, name);
4245
if (!activity) {
4346
throw new Error('Activity not found');
4447
}
4548

4649
// Get meta data
47-
const meta = this.loader.getMetaStrings();
50+
const meta = dataStore.getMetaStrings();
4851
this.currentActivity = activity;
4952
this.KnowledgeLabel = meta.knowledgeLabels[activity.difficultyOfImplementation.knowledge];
5053
this.TimeLabel = meta.labels[activity.difficultyOfImplementation.time];

src/app/component/circular-heatmap/circular-heatmap.component.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ progress-slider {
129129
.filter-container {
130130
position: relative;
131131
}
132+
133+
.ng-star-inserted span {
134+
font-size: smaller;
135+
font-style: italic;
136+
}
137+
132138
button.filter-toggle {
133139
position: absolute;
134140
top: 2px;

src/app/component/circular-heatmap/circular-heatmap.component.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,16 @@ <h2>Nothing to show</h2>
258258
</label>
259259
<progress-slider
260260
[steps]="progressStates"
261-
[initial]="getTeamProgressState(activity['uuid'], team.key)">
261+
[initial]="getTeamProgressState(activity['uuid'], team.key)"
262+
(progressChange)="onProgressChange(activity['uuid'], team.key, $event)"
262263
></progress-slider>
263264
</li>
264265
</ng-container>
265266
</ul>
267+
<div>
268+
<span >* Has been modified</span><br/>
269+
<span >** Has been modified to less complete stage then before</span>
270+
</div>
266271
</ng-template>
267272
</mat-expansion-panel>
268273
</mat-card-content>

src/app/component/circular-heatmap/circular-heatmap.component.ts

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import {
1717
ModalMessageComponent,
1818
DialogInfo,
1919
} from '../modal-message/modal-message.component';
20-
import { ActivityStore } from 'src/app/model/activity-store';
21-
import { MetaFile, ProgressDefinition } from 'src/app/model/meta';
20+
import { Activity, ActivityStore } from 'src/app/model/activity-store';
21+
import { Uuid, ProgressDefinition, TeamName, ProgressTitle } from 'src/app/model/meta';
2222
import { SectorViewController } from './sector-viewcontroller';
23+
import { DataStore } from 'src/app/model/data-store';
2324

2425

2526
export interface old_activitySchema {
@@ -72,8 +73,8 @@ export class CircularHeatmapComponent implements OnInit {
7273
old_activityDetails: any;
7374
@ViewChildren(MatChip) chips!: QueryList<MatChip>;
7475
matChipsArray: MatChip[] = [];
75-
meta: MetaFile | null = null;
76-
activityStore: ActivityStore | null = null;
76+
// meta: MetaFile | null = null;
77+
dataStore: DataStore | null = null;
7778

7879
// New properties for refactored data
7980
dimLabels: string[] = [];
@@ -99,19 +100,26 @@ export class CircularHeatmapComponent implements OnInit {
99100
// Ensure that Levels and Teams load before MaturityData
100101
// using promises, since ngOnInit does not support async/await
101102
this.loader.load()
102-
.then((activityStore: ActivityStore) => {
103+
.then((dataStore: DataStore) => {
103104
console.log(`${this.perfNow()}s: set filters: ${this.chips?.length}`);
104105
this.matChipsArray = this.chips.toArray();
105-
this.meta = this.loader.meta;
106-
this.filtersTeams = this.buildFilters(this.meta?.teams as string[]);
107-
this.filtersTeamGroups = this.buildFilters(Object.keys(this.meta?.teamGroups || {}));
106+
// this.meta = this.loader.meta;
107+
if (!dataStore.activityStore) {
108+
throw Error("TODO: Ooops! Dette må håndteres");
109+
}
110+
if (!dataStore.progressStore) {
111+
throw Error("TODO: Ooops! Dette må håndteres");
112+
}
113+
114+
this.filtersTeams = this.buildFilters(dataStore.meta?.teams as string[]);
115+
this.filtersTeamGroups = this.buildFilters(Object.keys(dataStore.meta?.teamGroups || {}));
108116
this.filtersTeamGroups['All'] = true;
109117

110-
let progressDefinition: ProgressDefinition = this.meta?.progressDefinition || {};
111-
SectorViewController.init(this.meta?.teams || [], activityStore.getProgress(), progressDefinition);
118+
let progressDefinition: ProgressDefinition = dataStore.meta?.progressDefinition || {};
119+
SectorViewController.init(dataStore.progressStore, dataStore.meta?.teams || [], dataStore?.progressStore?.getProgressData() || {}, progressDefinition);
112120
this.progressStates = SectorViewController.getProgressStates();
113121

114-
this.setYamlData(activityStore);
122+
this.setYamlData(dataStore);
115123

116124
// For now, just draw the sectors (no activities yet)
117125
this.loadCircularHeatMap(
@@ -138,16 +146,16 @@ export class CircularHeatmapComponent implements OnInit {
138146
console.log(message);
139147
}
140148

141-
setYamlData(activityStore: ActivityStore) {
142-
this.activityStore = activityStore;
143-
this.maxLevel = this.loader.getMaxLevel();
144-
this.dimensionLabels = activityStore.getAllDimensionNames();
149+
setYamlData(dataStore: DataStore) {
150+
this.dataStore = dataStore;
151+
this.maxLevel = dataStore.getMaxLevel();
152+
this.dimensionLabels = dataStore?.activityStore?.getAllDimensionNames() || [];
145153

146154
// Prepare all sectors: one for each (dimension, level) pair
147155
this.allSectors = [];
148156
for (let lvl = 1; lvl <= this.maxLevel; lvl++) {
149157
for (let dimName of this.dimensionLabels) {
150-
const activities = activityStore.getActivities(dimName, lvl);
158+
const activities: Activity[] = dataStore?.activityStore?.getActivities(dimName, lvl) || [];
151159
this.allSectors.push(new SectorViewController(
152160
dimName,
153161
'Level ' + lvl,
@@ -174,7 +182,7 @@ export class CircularHeatmapComponent implements OnInit {
174182
this.old_yaml.setURI('./assets/YAML/generated/generated.yaml');
175183
this.old_yaml.getJson().subscribe(async data => {
176184
console.log(`${this.perfNow()}s: LoadMaturityData Downloaded`);
177-
const activityStore: ActivityStore = await this.loader.load();
185+
const activityStore: ActivityStore = new ActivityStore(); // await this.loader.load();
178186
this.old_YamlObject = activityStore.data;
179187
this.OBSOLETE_AddSegmentLabels(this.old_YamlObject);
180188
const localStorageData = this.getDatasetFromBrowserStorage();
@@ -361,16 +369,15 @@ export class CircularHeatmapComponent implements OnInit {
361369
chip.toggleSelected();
362370

363371
Object.keys(this.filtersTeams).forEach(key => {
364-
this.filtersTeams[key] = this.meta?.teamGroups[teamGroup]?.includes(key) || false;
372+
this.filtersTeams[key] = this.dataStore?.meta?.teamGroups[teamGroup]?.includes(key) || false;
365373
});
366374
this.hasTeamsFilter = Object.values(this.filtersTeams).some(v => v === true);
367375
}
368376
}
369377

370378
getTeamProgressState(activityUuid: string, teamName: string): string {
371-
return this.activityStore?.getTeamProgressState(activityUuid, teamName) || '';
372-
return this.selectedSector?.activities?.find(a => a.uuid === activityUuid)?.teamsImplemented[teamName] || '';
373-
379+
return this.dataStore?.progressStore?.getTeamActivityTitle(activityUuid, teamName) || '';
380+
// return this.selectedSector?.activities?.find(a => a.uuid === activityUuid)?.teamsImplemented[teamName] || '';
374381
}
375382

376383
toggleTeamFilter(chip: MatChip) {
@@ -381,8 +388,8 @@ export class CircularHeatmapComponent implements OnInit {
381388

382389
let selectedTeams: string[] = Object.keys(this.filtersTeams).filter(key => this.filtersTeams[key]);
383390

384-
Object.keys(this.meta?.teamGroups || {}).forEach(group => {
385-
let match: boolean = equalArray(selectedTeams, this.meta?.teamGroups[group]);
391+
Object.keys(this.dataStore?.meta?.teamGroups || {}).forEach(group => {
392+
let match: boolean = equalArray(selectedTeams, this.dataStore?.meta?.teamGroups[group]);
386393
this.filtersTeamGroups[group] = match;
387394
});
388395
}
@@ -480,6 +487,28 @@ export class CircularHeatmapComponent implements OnInit {
480487
this.reColorHeatmap();
481488
}
482489

490+
onProgressChange(
491+
activityUuid: Uuid,
492+
teamName: TeamName,
493+
newProgress: ProgressTitle)
494+
{
495+
if (!this.dataStore || !this.dataStore.progressStore || !this.dataStore.activityStore) {
496+
throw Error('Data store or progress store is not initialized.');
497+
}
498+
499+
this.dataStore.progressStore.setTeamActivityProgressState(
500+
activityUuid,
501+
teamName,
502+
newProgress);
503+
// this.reColorHeatmap();
504+
let activity: Activity = this.dataStore.activityStore.getActivityByUuid(activityUuid);
505+
let index = this.dimensionLabels.indexOf(activity.dimension)
506+
+ this.dimensionLabels.length * (activity.level -1) ;
507+
508+
this.recolorSector(index);
509+
// this.recolorSector(activityUuid, teamName);
510+
}
511+
483512
loadCircularHeatMap(
484513
dom_element_to_append_to: string,
485514
dataset: any,
@@ -513,6 +542,8 @@ export class CircularHeatmapComponent implements OnInit {
513542
.segmentLabelHeight(segmentLabelHeight);
514543

515544
chart.accessor(function (d: any) {
545+
if (d.getSectorProgress() > 0)
546+
console.log('Color', d.level, d.dimension, d.value, d.getSectorProgress());
516547
return d.getSectorProgress();
517548
});
518549

@@ -885,6 +916,21 @@ export class CircularHeatmapComponent implements OnInit {
885916
}
886917
}
887918

919+
recolorSector(index: number) {
920+
// console.log('recolorSector', index);
921+
var colorSector = d3
922+
.scaleLinear<string, string>()
923+
.domain([0, 1])
924+
.range(['white', 'green']);
925+
926+
let progressValue: number = this.allSectors[index].getSectorProgress();
927+
d3.select('#index-' + index).attr(
928+
'fill',
929+
colorSector(progressValue)
930+
);
931+
console.log(`Recolor sector ${index} with progress ${(progressValue*100).toFixed(1)}%`);
932+
}
933+
888934
reColorHeatmap() {
889935
console.log('recolor');
890936
var teamsCount = this.old_teamVisible.length;

src/app/component/circular-heatmap/sector-viewcontroller.ts

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Activity } from "src/app/model/activity-store";
2-
import { ActivityProgress, Progress, ProgressDefinition, TeamNames, TeamProgress } from "src/app/model/meta";
2+
import { ActivityProgress, Progress, ProgressDefinition, TeamNames, TeamProgress, Uuid } from "src/app/model/meta";
3+
import { ProgressStore } from "src/app/model/progress-store";
34

45
export class SectorViewController {
6+
static _progressStore: ProgressStore | null;
57
static _allProgress: Progress | null;
68
static _progressValues: ProgressDefinition | null;
79
static _progressStates: string[] | null;
@@ -13,16 +15,17 @@ export class SectorViewController {
1315
activities: Activity[];
1416
progressValue: number;
1517

16-
static init(teamnames: TeamNames, progress: Progress, progressStates: ProgressDefinition) {
18+
static init(progressStore: ProgressStore, teamnames: TeamNames, progress: Progress, progressStates: ProgressDefinition) {
19+
this._progressStore = progressStore;
1720
this._allTeams = teamnames;
1821
this._allProgress = progress;
1922
this._progressValues = progressStates;
2023
this._progressStates = Object.keys(progressStates)
21-
.sort((a, b) => progressStates[b] - progressStates[a]);
24+
.sort((a, b) => progressStates[b] - progressStates[a]);
2225
}
2326

2427
static getProgressStates() {
25-
return this._progressStates?.reverse() || [];
28+
return this._progressStates?.slice().reverse() || [];
2629
}
2730

2831
static setVisibleTeams(teams: TeamNames) {
@@ -54,27 +57,15 @@ export class SectorViewController {
5457
}
5558

5659
// Calculate the progress of an activity, across all visible teams
57-
private getActivityProgress(uuid: string, teams: TeamNames): number {
60+
private getActivityProgress(uuid: Uuid, teams: TeamNames): number {
5861
let activity: ActivityProgress = SectorViewController._allProgress?.[uuid] || {};
5962
let progress: number = 0;
6063
for (const team of teams) {
61-
progress += SectorViewController.getProgressValue(activity[team]);
64+
progress += SectorViewController._progressStore?.getTeamActivityProgressValue(uuid, team) || 0;
6265
}
6366
return progress / teams.length;
6467
}
6568

66-
// Calculate the progress value for a team progress state
67-
static getProgressValue(teamProgress: TeamProgress): number {
68-
if (!this._progressValues) return 0;
69-
if (!teamProgress) return 0;
70-
71-
for (const state of SectorViewController._progressStates || []) {
72-
if (teamProgress[state] !== undefined) {
73-
return this._progressValues[state];
74-
}
75-
}
76-
return 0;
77-
}
7869
}
7970

8071

0 commit comments

Comments
 (0)