From 7c569a77c13a051891c846e68d2fa587a4afbd8c Mon Sep 17 00:00:00 2001 From: Viktor Rudko <43116821+vktrrdk@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:58:11 +0100 Subject: [PATCH] =?UTF-8?q?fix(Flavor):=20Custom=20flavor=20now=20part=20o?= =?UTF-8?q?f=20the=20application=20adjustment=20pro=E2=80=A6=20(#5993)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(Flavor): Custom flavor now part of the application adjustment process * more clean implementation * small FlavorType fix --------- Co-authored-by: denbicloud <46009071+denbicloud@users.noreply.github.com> Co-authored-by: dweinholz --- src/app/api-connector/flavor.service.ts | 11 +- .../applications/applications.component.html | 9 +- .../applications/applications.component.ts | 16 +- src/app/pipe-module/pipe-module.module.ts | 3 + .../pipe-module/pipes/has-flavor-type.pipe.ts | 33 ++++ .../modification-request.component.html | 19 +- .../modification-request.component.ts | 4 +- .../application-base-class.component.ts | 173 +++++++++--------- .../baseClass/flavor-type-shortcuts.ts | 6 + 9 files changed, 175 insertions(+), 99 deletions(-) create mode 100644 src/app/pipe-module/pipes/has-flavor-type.pipe.ts create mode 100644 src/app/shared/shared_modules/baseClass/flavor-type-shortcuts.ts diff --git a/src/app/api-connector/flavor.service.ts b/src/app/api-connector/flavor.service.ts index 3406043e9e..b481cee2e0 100644 --- a/src/app/api-connector/flavor.service.ts +++ b/src/app/api-connector/flavor.service.ts @@ -39,8 +39,15 @@ export class FlavorService { ); } - getListOfFlavorsAvailable(project_id: string = '', specific: boolean = false): Observable { - const params: HttpParams = new HttpParams().set('project_id', project_id).set('specific', JSON.stringify(specific)); + getListOfFlavorsAvailable( + project_id: string = '', + specific: boolean = false, + custom: boolean = false, + ): Observable { + const params: HttpParams = new HttpParams() + .set('project_id', project_id) + .set('specific', JSON.stringify(specific)) + .set('custom', custom); return this.http .get(`${ApiSettings.getApiBaseURL()}project_applications/flavors/`, { diff --git a/src/app/applications/applications.component.html b/src/app/applications/applications.component.html index 4e5bef6f67..edeeae35d4 100644 --- a/src/app/applications/applications.component.html +++ b/src/app/applications/applications.component.html @@ -1110,7 +1110,10 @@
@@ -1129,7 +1132,9 @@
diff --git a/src/app/applications/applications.component.ts b/src/app/applications/applications.component.ts index 0276f0bbe0..8c1450565a 100644 --- a/src/app/applications/applications.component.ts +++ b/src/app/applications/applications.component.ts @@ -5,6 +5,7 @@ import { import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; import { Subscription } from 'rxjs'; import { HttpStatusCode } from '@angular/common/http'; +import { FlavorTypeShortcuts } from 'app/shared/shared_modules/baseClass/flavor-type-shortcuts'; import { ApplicationsService } from '../api-connector/applications.service'; import { ApiSettings } from '../api-connector/api-settings.service'; import { Application } from './application.model/application.model'; @@ -136,7 +137,7 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme this.getSubmittedApplicationsAdmin(); this.getApplicationHistory(); this.getComputeCenters(); - this.flavorService.getListOfFlavorsAvailable().subscribe((flavList: Flavor[]): void => { + this.flavorService.getListOfFlavorsAvailable(undefined, undefined, true).subscribe((flavList: Flavor[]): void => { this.flavorList = flavList; }); this.flavorService.getListOfTypesAvailable().subscribe((availableTypes: FlavorType[]): void => { @@ -263,6 +264,19 @@ export class ApplicationsComponent extends ApplicationBaseClassComponent impleme return false; } + checkIfTypeGotSimpleVMFlavorOrIsCustom(type: FlavorType): boolean { + for (const flav of this.flavorList) { + if ( + (flav.type.shortcut === type.shortcut && flav.simple_vm) + || type.shortcut === FlavorTypeShortcuts.CUSTOM_FLAVOR + ) { + return true; + } + } + + return false; + } + /** * Checks if the key given represents a flavor and if so returns the respective Flavor * diff --git a/src/app/pipe-module/pipe-module.module.ts b/src/app/pipe-module/pipe-module.module.ts index 7582c44040..1cf3c30200 100644 --- a/src/app/pipe-module/pipe-module.module.ts +++ b/src/app/pipe-module/pipe-module.module.ts @@ -17,6 +17,7 @@ import { HasStatusNotInListPipe } from './pipes/has-status-not-in-list.pipe'; import { SignificancePipe } from '../shared/shared_modules/components/maintenance-notification/significance-pipe/significance.pipe'; import { SocialConsentGivenPipe } from './pipes/social-consent-given.pipe'; import { IsMigratedProjectPipe } from './pipes/isMigratedProject'; +import { HasFlavorTypeOrIsNotCustomPipe } from './pipes/has-flavor-type.pipe'; /** * Pipemodule @@ -43,6 +44,7 @@ import { IsMigratedProjectPipe } from './pipes/isMigratedProject'; SignificancePipe, SocialConsentGivenPipe, IsMigratedProjectPipe, + HasFlavorTypeOrIsNotCustomPipe, ], exports: [ FlavorCounterPipe, @@ -65,6 +67,7 @@ import { IsMigratedProjectPipe } from './pipes/isMigratedProject'; SignificancePipe, SocialConsentGivenPipe, IsMigratedProjectPipe, + HasFlavorTypeOrIsNotCustomPipe, ], imports: [CommonModule], providers: [], diff --git a/src/app/pipe-module/pipes/has-flavor-type.pipe.ts b/src/app/pipe-module/pipes/has-flavor-type.pipe.ts new file mode 100644 index 0000000000..2d01493353 --- /dev/null +++ b/src/app/pipe-module/pipes/has-flavor-type.pipe.ts @@ -0,0 +1,33 @@ +// eslint-disable-next-line max-classes-per-file +import { Pipe, PipeTransform } from '@angular/core'; +import { Application } from 'app/applications/application.model/application.model'; +import { FlavorTypeShortcuts } from 'app/shared/shared_modules/baseClass/flavor-type-shortcuts'; +import { Flavor } from 'app/virtualmachines/virtualmachinemodels/flavor'; +import { FlavorType } from 'app/virtualmachines/virtualmachinemodels/flavorType'; + +/** + * Pipe which compares status. + */ +@Pipe({ + name: 'hasFlavorTypeOrIsNotCustom', +}) +export class HasFlavorTypeOrIsNotCustomPipe implements PipeTransform { + transform(project: Application, flavorType: FlavorType): boolean { + const hasFlavorTypeFlavor: boolean = project.flavors.some( + (flavor: Flavor): boolean => flavor.type.shortcut === flavorType.shortcut, + ); + + return hasFlavorTypeFlavor || flavorType.shortcut !== FlavorTypeShortcuts.CUSTOM_FLAVOR; + } +} +/** + * Pipe which checks if status is in a list. + */ +@Pipe({ + name: 'statusInList', +}) +export class StatusInListPipe implements PipeTransform { + transform(status: string, status_list_to_compare: string[]): boolean { + return status_list_to_compare.indexOf(status) !== -1; + } +} diff --git a/src/app/projectmanagement/modals/modification-request/modification-request.component.html b/src/app/projectmanagement/modals/modification-request/modification-request.component.html index 3f5f8c488e..c9a138ecc1 100644 --- a/src/app/projectmanagement/modals/modification-request/modification-request.component.html +++ b/src/app/projectmanagement/modals/modification-request/modification-request.component.html @@ -29,7 +29,10 @@
@@ -63,7 +66,7 @@
name="flavor.name + '_old'" id="{{ flavor.name }}_old" type="text" - value="{{ project | flavorCounter : flavor }}" + value="{{ project | flavorCounter: flavor }}" placeholder="0" [disabled]="flavor.disabled || adjustment" aria-describedby="{{ flavor.name }}_old_help" @@ -103,8 +106,8 @@
placeholder="e.g 1" [ngModel]=" adjustment - ? (project.project_modification_request | flavorCounter : flavor) - : (temp_project_modification | flavorCounter : flavor) + ? (project.project_modification_request | flavorCounter: flavor) + : (temp_project_modification | flavorCounter: flavor) " #name="ngModel" (change)="checkFlavorPairs(flavor, $event)" @@ -112,8 +115,8 @@
[attr.appInteger]="adjustment ? null : true" value="{{ adjustment - ? (project.project_modification_request | flavorCounter : flavor) - : (temp_project_modification | flavorCounter : flavor) + ? (project.project_modification_request | flavorCounter: flavor) + : (temp_project_modification | flavorCounter: flavor) }}" [attr.readonly]="flavor.disabled || adjustment ? true : null" aria-describedby="{{ flavor.name }}_help" @@ -144,12 +147,12 @@
step="1" attr.data-test-id="adjusted_{{ flavor.type.shortcut + '_' + i }}" placeholder="e.g 1" - [ngModel]="adjusted_project_modification | flavorCounter : flavor" + [ngModel]="adjusted_project_modification | flavorCounter: flavor" #name="ngModel" (change)="checkFlavorPairsAdjustment(flavor, $event)" appMinAmount="0" appInteger - value="{{ adjusted_project_modification | flavorCounter : flavor }}" + value="{{ adjusted_project_modification | flavorCounter: flavor }}" aria-describedby="adjust_{{ flavor.name }}_help" [ngClass]="{ 'is-invalid': name?.invalid, diff --git a/src/app/projectmanagement/modals/modification-request/modification-request.component.ts b/src/app/projectmanagement/modals/modification-request/modification-request.component.ts index 1fefad9759..4f672b71bb 100644 --- a/src/app/projectmanagement/modals/modification-request/modification-request.component.ts +++ b/src/app/projectmanagement/modals/modification-request/modification-request.component.ts @@ -3,6 +3,7 @@ import { } from '@angular/core'; import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; import { Subscription } from 'rxjs'; +import { HasFlavorTypeOrIsNotCustomPipe } from 'app/pipe-module/pipes/has-flavor-type.pipe'; import { ApplicationModification } from '../../../applications/application_modification.model'; import { ResultComponent } from '../result/result.component'; import { Application } from '../../../applications/application.model/application.model'; @@ -16,7 +17,7 @@ import { CreditsService } from '../../../api-connector/credits.service'; selector: 'app-modification-request', templateUrl: './modification-request.component.html', styleUrls: ['./modification-request.component.scss'], - providers: [FlavorService, CreditsService], + providers: [FlavorService, CreditsService, HasFlavorTypeOrIsNotCustomPipe], }) export class ModificationRequestComponent implements OnInit, OnDestroy { CLOUD_PORTAL_SUPPORT_MAIL: string = CLOUD_PORTAL_SUPPORT_MAIL; @@ -116,6 +117,7 @@ export class ModificationRequestComponent implements OnInit, OnDestroy { this.shown_flavors[flavor.type.long_name].push(flavor); } } + this.checkFlavorDifferences(); }), ); diff --git a/src/app/shared/shared_modules/baseClass/application-base-class.component.ts b/src/app/shared/shared_modules/baseClass/application-base-class.component.ts index 1795c573e5..55fc87ad99 100644 --- a/src/app/shared/shared_modules/baseClass/application-base-class.component.ts +++ b/src/app/shared/shared_modules/baseClass/application-base-class.component.ts @@ -8,6 +8,8 @@ import { FlavorType } from '../../../virtualmachines/virtualmachinemodels/flavor import { FlavorService } from '../../../api-connector/flavor.service'; import { FacilityService } from '../../../api-connector/facility.service'; import { UserService } from '../../../api-connector/user.service'; +import { FlavorTypeShortcuts } from './flavor-type-shortcuts'; +import { VolumeActionStates } from '../../../virtualmachines/volumes/volume-action-states.enum'; /** * Application base component.. @@ -19,53 +21,54 @@ import { UserService } from '../../../api-connector/user.service'; }) export class ApplicationBaseClassComponent extends AbstractBaseClass { /** - * If all Applications are loaded, important for the loader. - * - * @type {boolean} - */ + * If all Applications are loaded, important for the loader. + * + * @type {boolean} + */ isLoaded: boolean = false; + FlavorTypeShortcuts: typeof FlavorTypeShortcuts = FlavorTypeShortcuts; /** - * Selected Application. - */ + * Selected Application. + */ selectedApplication: Application; /** - * All available compute centers. - * - * @type {Array} - */ + * All available compute centers. + * + * @type {Array} + */ computeCenters: ComputecenterComponent[] = []; /** - * List of flavor types. - */ + * List of flavor types. + */ typeList: FlavorType[]; /** - * Total number of cores. - * - * @type {number} - */ + * Total number of cores. + * + * @type {number} + */ totalNumberOfCores: number = 0; /** - * Total number of ram. - * - * @type {number} - */ + * Total number of ram. + * + * @type {number} + */ totalRAM: number = 0; /** - * Total number of GPUs - */ + * Total number of GPUs + */ totalGPU: number = 0; newFlavors: { - [id: string]: { - counter: number - flavor: Flavor - } - } = {}; + [id: string]: { + counter: number + flavor: Flavor + } + } = {}; GPU_SHORTCUT = 'GPU'; HMF_SHORTCUT = 'HMF'; @@ -73,57 +76,57 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { extension_request: boolean = false; /** - * If shortname is valid. - * - * @type {boolean} - */ + * If shortname is valid. + * + * @type {boolean} + */ public wronginput: boolean = false; /** - * - */ + * + */ constantStrings: Object; /** - * List of flavors. - */ + * List of flavors. + */ flavorList: Flavor[] = []; extraResourceCommentRequired: boolean = false; /** - * If all userApplications are loaded, important for the loader. - * - * @type {boolean} - */ + * If all userApplications are loaded, important for the loader. + * + * @type {boolean} + */ isLoaded_userApplication: boolean = false; public project_application_pi_approved: boolean = false; /** - * Name of the project. - */ + * Name of the project. + */ public projectName: string; public project_application_report_allowed: boolean = false; /** - * Applications of the user viewing the Application overview. - * - * @type {Array} - */ + * Applications of the user viewing the Application overview. + * + * @type {Array} + */ user_applications: Application[] = []; constructor( - protected userService: UserService, - protected applicationsService: ApplicationsService, - protected facilityService: FacilityService, - private cdRef: ChangeDetectorRef, + protected userService: UserService, + protected applicationsService: ApplicationsService, + protected facilityService: FacilityService, + private cdRef: ChangeDetectorRef, ) { super(); } /** - * Gets all available compute centers and saves them in the computeCenters attribute. - */ + * Gets all available compute centers and saves them in the computeCenters attribute. + */ getComputeCenters(): void { this.facilityService.getComputeCenters().subscribe((result: [{ [key: string]: string }]): void => { for (const cc of result) { @@ -159,8 +162,8 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { if ( (entry?.flavor?.type?.shortcut.toUpperCase() === this.GPU_SHORTCUT - || entry?.flavor?.type?.shortcut.toUpperCase() === this.HMF_SHORTCUT) - && entry.counter > 0 + || entry?.flavor?.type?.shortcut.toUpperCase() === this.HMF_SHORTCUT) + && entry.counter > 0 ) { this.extraResourceCommentRequired = true; this.cdRef.detectChanges(); @@ -186,11 +189,11 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * Get details of member like name and email by elixir. - * - * @param application - * @param collapse_id - */ + * Get details of member like name and email by elixir. + * + * @param application + * @param collapse_id + */ public getMemberDetailsByElixirIdIfCollapsed(application: Application, collapse_id: string): void { if (!this.getCollapseStatus(collapse_id)) { this.getMemberDetailsByElixirId(application); @@ -211,11 +214,11 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * Get status name by status id. - * - * @param id - * @returns - */ + * Get status name by status id. + * + * @param id + * @returns + */ public getStatusById(id: number): string { const dummy: string = 'Unknown'; if (Application_States_Strings[Application_States[id]]) { @@ -226,20 +229,20 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * Sets the selected application. - * - * @param application - */ + * Sets the selected application. + * + * @param application + */ setSelectedApplication(application: Application): void { this.selectedApplication = application; } /** - * Uses the param types to safe the available FlavorTypes to the array typeList. - * Also it fills the array collapseList with booleans of value 'false' so all flavor-categories are shown in the application form. - * - * @param types array of all available FlavorTypes - */ + * Uses the param types to safe the available FlavorTypes to the array typeList. + * Also it fills the array collapseList with booleans of value 'false' so all flavor-categories are shown in the application form. + * + * @param types array of all available FlavorTypes + */ setListOfTypes(types: FlavorType[]): void { let index: number = -1; for (let i: number = 0; i < types.length; i += 1) { @@ -258,17 +261,17 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * Check if short name is valid. - * - * @param shortname - */ + * Check if short name is valid. + * + * @param shortname + */ public checkShortname(shortname: string): void { this.wronginput = !/^[a-zA-Z0-9\s]*$/.test(shortname); } /** - * Fills the array constantStrings with values dependent of keys which are used to indicate inputs from the application-form - */ + * Fills the array constantStrings with values dependent of keys which are used to indicate inputs from the application-form + */ generateConstants(): void { this.constantStrings = []; this.constantStrings['project_application_shortname'] = 'Shortname: '; @@ -305,13 +308,13 @@ export class ApplicationBaseClassComponent extends AbstractBaseClass { } /** - * This function concatenates a given key combined with a given value to a string - * which is used on the confirmation-modal. - * - * @param key the key to access a string in the array constantStrings - * @param val the value that is concatenated with the string from the array and an optional addition (depending on the key) - * @returns the concatenated string for the confirmation-modal - */ + * This function concatenates a given key combined with a given value to a string + * which is used on the confirmation-modal. + * + * @param key the key to access a string in the array constantStrings + * @param val the value that is concatenated with the string from the array and an optional addition (depending on the key) + * @returns the concatenated string for the confirmation-modal + */ matchString(key: string, val: string): string { if (key in this.constantStrings) { switch (key) { diff --git a/src/app/shared/shared_modules/baseClass/flavor-type-shortcuts.ts b/src/app/shared/shared_modules/baseClass/flavor-type-shortcuts.ts new file mode 100644 index 0000000000..e88fb2c59e --- /dev/null +++ b/src/app/shared/shared_modules/baseClass/flavor-type-shortcuts.ts @@ -0,0 +1,6 @@ +export enum FlavorTypeShortcuts { + HIGH_MEMORY_FLAVOR = 'hmf', + GRAPHIC_PROCESSING_UNIT = 'GPU', + STANDARD_FLAVOR = 'std', + CUSTOM_FLAVOR = 'cstm', +}