Skip to content

Commit

Permalink
Merge pull request #353 from NREL/issue-265
Browse files Browse the repository at this point in the history
Issue 265: Move KPI/KPM selection to facility level
  • Loading branch information
RLiNREL authored Jan 6, 2025
2 parents 3d4a91e + 9c1c366 commit a728ece
Show file tree
Hide file tree
Showing 82 changed files with 436 additions and 288 deletions.
7 changes: 6 additions & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { EnergyEquipmentIdbService } from './indexed-db/energy-equipment-idb.ser
import { ProcessEquipmentIdbService } from './indexed-db/process-equipment-idb.service';
import { KeyPerformanceMetricImpactsIdbService } from './indexed-db/key-performance-metric-impacts-idb.service';
import { ToastNotificationsService } from './core-components/toast-notifications/toast-notifications.service';
import { UpdateDbEntriesService } from './indexed-db/update-db-entries.service';

@Component({
selector: 'app-root',
Expand All @@ -36,7 +37,8 @@ export class AppComponent {
private energyEquipmentIdbService: EnergyEquipmentIdbService,
private processEquipmentIdbService: ProcessEquipmentIdbService,
private keyPerformanceMetricImpactIdbService: KeyPerformanceMetricImpactsIdbService,
private toastNotificationService: ToastNotificationsService) {
private toastNotificationService: ToastNotificationsService,
private updateDbEntriesService: UpdateDbEntriesService) {
}

async ngOnInit() {
Expand All @@ -51,6 +53,9 @@ export class AppComponent {
console.log('init')
await this.userIdbService.initializeData();
console.log('users init..');
//update db entries
let user: IdbUser = this.userIdbService.user.getValue();
await this.updateDbEntriesService.updateDbEntries(user);
//companies
await this.companyIdbService.setCompanies();
console.log('companies init..');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { ImportBackupModalComponent } from './import-backup-modal.component';
import { UserIdbService } from 'src/app/indexed-db/user-idb.service';
import { DbChangesService } from 'src/app/indexed-db/db-changes.service';
import { BackupDataService } from 'src/app/shared/shared-services/backup-data.service';
import { stubServiceProviders } from 'src/app/spec-helpers/spec-test-service-stub';

describe('ImportBackupModalComponent', () => {
let component: ImportBackupModalComponent;
let fixture: ComponentFixture<ImportBackupModalComponent>;

beforeEach(async () => {
let userDbService: Partial<UserIdbService> = {};
let dbChangesService: Partial<DbChangesService> = {};
let backupDataService: Partial<BackupDataService> = {};

await TestBed.configureTestingModule({
declarations: [ImportBackupModalComponent],
providers: [
{ provide: UserIdbService, useValue: userDbService },
{ provide: BackupDataService, useValue: backupDataService},
{ provide: DbChangesService, useValue: dbChangesService}

]
providers: stubServiceProviders
})
.compileComponents();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { IdbUser } from 'src/app/models/user';
import { Subscription } from 'rxjs';
import { ImportBackupModalService } from './import-backup-modal.service';
import { environment } from 'src/environments/environment';
import { UpdateDbEntriesService } from 'src/app/indexed-db/update-db-entries.service';

@Component({
selector: 'app-import-backup-modal',
Expand All @@ -32,7 +33,8 @@ export class ImportBackupModalComponent implements OnInit, OnDestroy {
private backupDataService: BackupDataService,
private dbChangesService: DbChangesService,
private router: Router,
private importBackupModalService: ImportBackupModalService
private importBackupModalService: ImportBackupModalService,
private updateDbEntriesService: UpdateDbEntriesService
) {

}
Expand Down Expand Up @@ -124,6 +126,8 @@ export class ImportBackupModalComponent implements OnInit, OnDestroy {
async addToCurrentUser(importFile: BackupFile) {
// Add backup data to current user
await this.backupDataService.importUserBackupFile(importFile, this.currentUser.guid);
this.currentUser.kpiFacilityMigrationDone = false;
await this.updateDbEntriesService.updateDbEntries(this.currentUser);
await this.dbChangesService.selectUser(this.currentUser, false);
}

Expand Down
16 changes: 2 additions & 14 deletions src/app/core-components/navbar/navbar.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { NavbarComponent } from './navbar.component';
import { UserIdbService } from 'src/app/indexed-db/user-idb.service';
import { RouterTestingModule } from '@angular/router/testing';
import { LoadingService } from '../loading/loading.service';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { BackupDataService } from 'src/app/shared/shared-services/backup-data.service';
import { ImportBackupModalComponent } from '../import-backup-modal/import-backup-modal.component';
import { DbChangesService } from 'src/app/indexed-db/db-changes.service';
import { FeedbackPageComponent } from '../feedback-page/feedback-page.component';
import { stubServiceProviders } from 'src/app/spec-helpers/spec-test-service-stub';

describe('NavbarComponent', () => {
let component: NavbarComponent;
let fixture: ComponentFixture<NavbarComponent>;

beforeEach(() => {
let userDbService: Partial<UserIdbService> = {};
let loadingService: Partial<LoadingService> = {};
let backupDataService: Partial<BackupDataService> = {};
let dbChangesService: Partial<DbChangesService> = {};
TestBed.configureTestingModule({
imports: [RouterTestingModule, FontAwesomeModule],
declarations: [NavbarComponent, ImportBackupModalComponent, FeedbackPageComponent],
providers: [
{ provide: UserIdbService, useValue: userDbService },
{ provide: LoadingService, useValue: loadingService },
{ provide: BackupDataService, useValue: backupDataService},
{ provide: DbChangesService, useValue: dbChangesService}
]
providers: stubServiceProviders
});
fixture = TestBed.createComponent(NavbarComponent);
component = fixture.componentInstance;
Expand Down
6 changes: 5 additions & 1 deletion src/app/core-components/welcome/welcome.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { BackupDataService, BackupFile } from 'src/app/shared/shared-services/ba
import { DbChangesService } from 'src/app/indexed-db/db-changes.service';
import { Router } from '@angular/router';
import { ToastNotificationsService } from '../toast-notifications/toast-notifications.service';
import { UpdateDbEntriesService } from 'src/app/indexed-db/update-db-entries.service';

@Component({
selector: 'app-welcome',
Expand Down Expand Up @@ -60,7 +61,8 @@ export class WelcomeComponent {
private backupDataService: BackupDataService,
private dbChangesService: DbChangesService,
private toastNotificationService: ToastNotificationsService,
private router: Router
private router: Router,
private updateDbEntriesService: UpdateDbEntriesService
) {

}
Expand Down Expand Up @@ -129,6 +131,8 @@ export class WelcomeComponent {
let fileData: string = reader.result as string;
let tmpBackupFile: BackupFile = JSON.parse(fileData);
let updatedBackupFile: BackupFile = await this.backupDataService.importUserBackupFile(tmpBackupFile, this.user.guid);
this.user.kpiFacilityMigrationDone = false;
await this.updateDbEntriesService.updateDbEntries(this.user);
await this.dbChangesService.selectUser(this.user, false);
this.loadingService.setLoadingStatus(false);
let exampleVisit: IdbOnSiteVisit = updatedBackupFile.onSiteVisits[0];
Expand Down
8 changes: 4 additions & 4 deletions src/app/indexed-db/key-performance-indicators-idb.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ export class KeyPerformanceIndicatorsIdbService {
});
}

getKpiFromKpm(companyGuid: string, performanceMetricValue: KeyPerformanceIndicatorValue): IdbKeyPerformanceIndicator {
getKpiFromKpm(facilityId: string, performanceMetricValue: KeyPerformanceIndicatorValue): IdbKeyPerformanceIndicator {
let keyPerformanceIndicators: Array<IdbKeyPerformanceIndicator> = this.keyPerformanceIndicators.getValue();
return keyPerformanceIndicators.find(kpi => {
return kpi.companyId == companyGuid && kpi.optionValue == performanceMetricValue;
return kpi.facilityId == facilityId && kpi.optionValue == performanceMetricValue;
});
}

async addKpmToKpi(companyId: string, performanceMetricToAdd: KeyPerformanceMetric | KeyPerformanceMetricOption, userId: string): Promise<KeyPerformanceMetric> {
async addKpmToKpi(companyId: string, performanceMetricToAdd: KeyPerformanceMetric | KeyPerformanceMetricOption, userId: string, facilityId: string): Promise<KeyPerformanceMetric> {
let addedMetric: KeyPerformanceMetric;
let keyPerformanceIndicator: IdbKeyPerformanceIndicator = this.getKpiFromKpm(companyId, performanceMetricToAdd.kpiValue);
if (keyPerformanceIndicator) {
Expand All @@ -112,7 +112,7 @@ export class KeyPerformanceIndicatorsIdbService {
let kpiOption: KeyPerformanceIndicatorOption = KeyPerformanceIndicatorOptions.find(option => {
return option.optionValue == performanceMetricToAdd.kpiValue
});
keyPerformanceIndicator = getNewKeyPerformanceIndicator(userId, companyId, kpiOption, false);
keyPerformanceIndicator = getNewKeyPerformanceIndicator(userId, companyId, kpiOption, false, facilityId);
addedMetric = keyPerformanceIndicator.performanceMetrics.find(_metric => {
return (_metric.value == performanceMetricToAdd.value);
});
Expand Down
19 changes: 19 additions & 0 deletions src/app/indexed-db/update-db-entries.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { TestBed } from '@angular/core/testing';

import { UpdateDbEntriesService } from './update-db-entries.service';
import { stubServiceProviders } from '../spec-helpers/spec-test-service-stub';

describe('UpdateDbEntriesService', () => {
let service: UpdateDbEntriesService;

beforeEach(() => {
TestBed.configureTestingModule({
providers: stubServiceProviders
});
service = TestBed.inject(UpdateDbEntriesService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
96 changes: 96 additions & 0 deletions src/app/indexed-db/update-db-entries.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Injectable } from '@angular/core';
import { KeyPerformanceIndicatorsIdbService } from './key-performance-indicators-idb.service';
import { KeyPerformanceMetricImpactsIdbService } from './key-performance-metric-impacts-idb.service';
import { CompanyIdbService } from './company-idb.service';
import { FacilityIdbService } from './facility-idb.service';
import { IdbCompany } from '../models/company';
import { firstValueFrom } from 'rxjs';
import { IdbFacility } from '../models/facility';
import { IdbKeyPerformanceIndicator } from '../models/keyPerformanceIndicator';
import { IdbUser } from '../models/user';
import { UserIdbService } from './user-idb.service';
import { IdbKeyPerformanceMetricImpact } from '../models/keyPerformanceMetricImpact';
import { getGUID } from '../shared/helpFunctions';

@Injectable({
providedIn: 'root'
})
export class UpdateDbEntriesService {

constructor(
private keyPerformanceIndicatorsIdbService: KeyPerformanceIndicatorsIdbService,
private keyPerformanceMetricImpactsIdbService: KeyPerformanceMetricImpactsIdbService,
private facilityIdbService: FacilityIdbService,
private userIdbService: UserIdbService
) { }

async updateDbEntries(user: IdbUser): Promise<IdbUser> {
let userNeedsUpdate: boolean = false;
if (!user.kpiFacilityMigrationDone) {
await this.updateToFacilityKPI();
user.kpiFacilityMigrationDone = true;
userNeedsUpdate = true;
}

if (userNeedsUpdate) {
user = await firstValueFrom(this.userIdbService.updateWithObservable(user));
this.userIdbService.user.next(user);
}
return user;
}

//migration of KPIs to facility level
async updateToFacilityKPI() {
let keyPerformanceIndicators: Array<IdbKeyPerformanceIndicator> = await firstValueFrom(this.keyPerformanceIndicatorsIdbService.getAll());
//get kpis without facility ids
let noFacilityKeyPerformanceIndicators: Array<IdbKeyPerformanceIndicator> = keyPerformanceIndicators.filter(indicator => {
return indicator.facilityId == undefined;
});
if (noFacilityKeyPerformanceIndicators.length > 0) {
let facilities: Array<IdbFacility> = await firstValueFrom(this.facilityIdbService.getAll());
let keyPerformanceMetricImpacts: Array<IdbKeyPerformanceMetricImpact> = await firstValueFrom(this.keyPerformanceMetricImpactsIdbService.getAll());

for (let i = 0; i < noFacilityKeyPerformanceIndicators.length; i++) {
let kpi: IdbKeyPerformanceIndicator = noFacilityKeyPerformanceIndicators[i];
// move kpi to all facilities in company
let kpiFacilities: Array<IdbFacility> = facilities.filter(facility => { return facility.companyId == kpi.companyId });
//if multiple facilities in a company
//need to create copies of kpis with uniq guids

for (let f = 0; f < kpiFacilities.length; f++) {
let facility: IdbFacility = kpiFacilities[f];
let facilityMetricImpacts: Array<IdbKeyPerformanceMetricImpact> = keyPerformanceMetricImpacts.filter(impact => { return impact.facilityId == facility.guid });
kpi.facilityId = facility.guid;
if (f == 0) {
await firstValueFrom(this.keyPerformanceIndicatorsIdbService.updateWithObservable(kpi));
} else {
//create new kpis when multiple facilities;
let originalKpiGuid: string = kpi.guid;
let originalKpmGuids: Array<{ originalGuid: string, newGuid: string }> = kpi.performanceMetrics.map(metric => { return { originalGuid: metric.guid, newGuid: undefined } });

let newKpiGuid: string = getGUID();
kpi.guid = newKpiGuid;
let associatedImpacts: Array<IdbKeyPerformanceMetricImpact> = facilityMetricImpacts.filter(impact => { return impact.kpiGuid == originalKpiGuid });
//add new kpi with updated guids
for (let m = 0; m < kpi.performanceMetrics.length; m++) {
let newGuid: string = getGUID();
let kpmGuidIndex: number = originalKpmGuids.findIndex(kpmGuid => { return kpmGuid.originalGuid == kpi.performanceMetrics[m].guid });
originalKpmGuids[kpmGuidIndex].newGuid = newGuid;
kpi.performanceMetrics[m].guid = newGuid;
};
delete kpi.id;
await firstValueFrom(this.keyPerformanceIndicatorsIdbService.addWithObservable(kpi));
//updates guid for associated impacts
for (let a = 0; a < associatedImpacts.length; a++) {
let impact: IdbKeyPerformanceMetricImpact = associatedImpacts[a];
impact.kpiGuid = newKpiGuid;
let newKpmGuid: string = originalKpmGuids.find(ogGuid => { return ogGuid.originalGuid == impact.kpmGuid }).newGuid;
impact.kpmGuid = newKpmGuid;
await firstValueFrom(this.keyPerformanceMetricImpactsIdbService.updateWithObservable(impact));
}
}
}
}
}
}
}
4 changes: 3 additions & 1 deletion src/app/models/keyPerformanceIndicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ import { IdbEntry, getNewIdbEntry } from "./idbEntry";
export interface IdbKeyPerformanceIndicator extends IdbEntry, KeyPerformanceIndicatorOption {
userId: string,
companyId: string,
facilityId: string,
isCustom: boolean,
description: string,
performanceMetrics: Array<KeyPerformanceMetric>
}


export function getNewKeyPerformanceIndicator(userId: string, companyId: string, keyPerformanceIndicatorOption: KeyPerformanceIndicatorOption, isCustom: boolean): IdbKeyPerformanceIndicator {
export function getNewKeyPerformanceIndicator(userId: string, companyId: string, keyPerformanceIndicatorOption: KeyPerformanceIndicatorOption, isCustom: boolean, facilityId: string): IdbKeyPerformanceIndicator {
let idbEntry: IdbEntry = getNewIdbEntry();
return {
...idbEntry,
userId: userId,
companyId: companyId,
facilityId: facilityId,
...keyPerformanceIndicatorOption,
isCustom: isCustom,
description: undefined,
Expand Down
4 changes: 3 additions & 1 deletion src/app/models/user.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { IdbEntry, getNewIdbEntry } from "./idbEntry";

export interface IdbUser extends IdbEntry {
skipSplashScreen: boolean
skipSplashScreen: boolean,
kpiFacilityMigrationDone: boolean
}

export function getNewIdbUser(): IdbUser {
let idbEntry: IdbEntry = getNewIdbEntry();
return {
...idbEntry,
kpiFacilityMigrationDone: true,
skipSplashScreen: false
}
}
Loading

0 comments on commit a728ece

Please sign in to comment.