File

projects/web-mev/src/app/features/analysis/components/operation/operation.component.ts

Description

Operation Component used for displaying the input parameters of an operation

Implements

OnChanges

Metadata

changeDetection ChangeDetectionStrategy.Default
selector mev-operation
styleUrls ./operation.component.scss
templateUrl ./operation.component.html

Index

Properties
Methods
Inputs
Outputs
Accessors

Constructor

constructor(formBuilder: FormBuilder, apiService: AnalysesService, metadataService: MetadataService, dialog: MatDialog)
Parameters :
Name Type Optional
formBuilder FormBuilder No
apiService AnalysesService No
metadataService MetadataService No
dialog MatDialog No

Inputs

operation
Type : Operation
workspace$
Type : Observable<Workspace>
workspaceId
Type : string

Outputs

executedOperationId
Type : EventEmitter<any>

Methods

convertToFloatObj
convertToFloatObj(obj)

Function is used to convert strings to floats for numeric values before sending the data to execute the operation

Parameters :
Name Optional
obj No
Returns : {}
createForm
createForm(data)

Configure the control setting for different types of input operation parameters The supported types are ObservationSet, FeatureSet, DataResource, BoundedFloat, Integer, Positive Integer, String, OptionString

Parameters :
Name Optional
data No
Returns : void
loadData
loadData()

Load operation data before creating form controls

Returns : void
ngOnChanges
ngOnChanges()
Returns : void
onSubmit
onSubmit()
Returns : void
showPlots
showPlots()

Function is triggered when the user clicks the Show button to visualize normalization results

Returns : void
startAnalysis
startAnalysis()

Function is triggered when the user clicks the Run button to start analysis

Returns : void

Properties

analysesForm
Type : FormGroup
booleanFields
Type : []
Default value : []
boxPlotData
Public dialog
Type : MatDialog
featureFields
Type : []
Default value : []
intFields
Type : []
Default value : []
multipleResourceFields
Type : []
Default value : []
multipleResourcesDropdownSettings
Type : object
Default value : {}
numFields
Type : []
Default value : []
observationFields
Type : []
Default value : []
optionFields
Type : []
Default value : []
resourceFields
Type : []
Default value : []
submitted
Default value : false
textFields
Type : []
Default value : []

Accessors

f
getf()

Convenience getter for easy access to form fields

import {
  Component,
  ChangeDetectionStrategy,
  Input,
  EventEmitter,
  Output,
  OnChanges
} from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { AnalysesService } from '../../services/analysis.service';
import { Workspace } from '@app/features/workspace-manager/models/workspace';
import { Observable } from 'rxjs';
import { Operation } from '../../models/operation';
import { MetadataService } from '@app/core/metadata/metadata.service';
import { AnalysisPlottingResultComponent } from '../analysis-plotting-result/analysis-plotting-result.component';
import { MatDialog } from '@angular/material/dialog';

/**
 * Operation Component
 * used for displaying the input parameters of an operation
 */
@Component({
  selector: 'mev-operation',
  templateUrl: './operation.component.html',
  styleUrls: ['./operation.component.scss'],
  changeDetection: ChangeDetectionStrategy.Default
})
export class OperationComponent implements OnChanges {
  analysesForm: FormGroup;
  submitted = false;
  multipleResourcesDropdownSettings = {};

  @Input() workspaceId: string;
  @Input() workspace$: Observable<Workspace>;
  @Input() operation: Operation;

  @Output() executedOperationId: EventEmitter<any> = new EventEmitter<any>();

  // default settings for analyses fields
  numFields = [];
  intFields = [];
  resourceFields = [];
  multipleResourceFields = [];
  textFields = [];
  optionFields = [];
  booleanFields = [];
  observationFields = [];
  featureFields = [];

  boxPlotData;

  constructor(
    private formBuilder: FormBuilder,
    private apiService: AnalysesService,
    private metadataService: MetadataService,
    public dialog: MatDialog
  ) {
    this.multipleResourcesDropdownSettings = {
      text: '',
      selectAllText: 'Select All',
      unSelectAllText: 'Unselect All',
      classes: 'resource-dropdown'
    };
  }

  ngOnChanges(): void {
    this.loadData();
  }

  /**
   * Configure the control setting for different types of input operation parameters
   * The supported types are ObservationSet, FeatureSet, DataResource, BoundedFloat, Integer,
   * Positive Integer, String, OptionString
   */
  createForm(data) {
    const inputs = data.inputs;
    const controlsConfig = {};
    this.numFields = [];
    this.intFields = [];
    this.resourceFields = [];
    this.multipleResourceFields = [];
    this.textFields = [];
    this.booleanFields = [];
    this.optionFields = [];
    this.observationFields = [];
    this.featureFields = [];

    for (const key in inputs) {
      if (inputs.hasOwnProperty(key)) {
        const input = inputs[key];
        const field_type = input.spec.attribute_type;

        switch (field_type) {
          case 'ObservationSet': {
            const observationField = {
              key: key,
              name: input.name,
              desc: input.description,
              required: input.required,
              sets: []
            };
            const availableObsSets = this.metadataService.getCustomObservationSets();
            observationField.sets = availableObsSets.map(set => {
              const newSet = set.elements.map(elem => {
                const o = { id: elem.id };
                return o;
              });
              return { ...set, elements: newSet };
            });
            this.observationFields.push(observationField);
            const configObservationSetsField = [
              '',
              [...(input.required ? [Validators.required] : [])]
            ];
            controlsConfig[key] = configObservationSetsField;
            break;
          }
          case 'FeatureSet': {
            const featureField = {
              key: key,
              name: input.name,
              desc: input.description,
              required: input.required,
              sets: []
            };
            const availableFeatSets = this.metadataService.getCustomFeatureSets();
            featureField.sets = availableFeatSets.map(set => {
              const newSet = set.elements.map(elem => {
                const o = { id: elem.id };
                return o;
              });
              return { ...set, elements: newSet };
            });
            this.featureFields.push(featureField);
            const configFeatureSetsField = [
              '',
              [...(input.required ? [Validators.required] : [])]
            ];
            controlsConfig[key] = configFeatureSetsField;
            break;
          }
          case 'DataResource': {
            const resourceField = {
              key: key,
              name: input.name,
              resource_types: input.spec.resource_types,
              desc: input.description,
              required: input.required,
              files: [],
              selectedFiles: []
            };
            this.apiService
              .getAvailableResourcesByParam(
                input.spec.resource_types,
                this.workspaceId
              )
              .subscribe(data => {
                resourceField.files = data;
                if (input.spec.many === false) {
                  this.resourceFields.push(resourceField);
                } else {
                  this.multipleResourceFields.push(resourceField);
                }
              });

            const configResourceField = [
              '',
              [...(input.required ? [Validators.required] : [])]
            ];
            controlsConfig[key] = configResourceField;
            break;
          }
          case 'BoundedFloat': {
            const numField = {
              key: key,
              name: input.name,
              min: input.spec.min,
              max: input.spec.max,
              desc: input.description,
              required: input.required
            };
            this.numFields.push(numField);

            const configNumField = [
              input.spec.default_value,
              [
                ...(input.required ? [Validators.required] : []),
                Validators.min(input.spec.min),
                Validators.max(input.spec.max),
                Validators.pattern(/^-?\d*(\.\d+)?$/)
              ]
            ];
            controlsConfig[key] = configNumField;
            break;
          }
          case 'Integer': {
            const intField = {
              key: key,
              name: input.name,
              min: 0,
              desc: input.description,
              required: input.required
            };
            this.intFields.push(intField);

            const configIntField = [
              input.spec.default_value,
              [
                ...(input.required ? [Validators.required] : []),
                Validators.min(input.spec.min),
                Validators.pattern(/^[0-9]\d*$/)
              ]
            ];
            controlsConfig[key] = configIntField;
            break;
          }
          case 'PositiveInteger': {
            const posIntField = {
              key: key,
              name: input.name,
              min: 1,
              desc: input.description,
              required: input.required
            };
            this.intFields.push(posIntField);

            const configPosIntField = [
              input.spec.default_value,
              [
                ...(input.required ? [Validators.required] : []),
                Validators.min(input.spec.min),
                Validators.pattern(/^[1-9]\d*$/)
              ]
            ];
            controlsConfig[key] = configPosIntField;
            break;
          }
          case 'String': {
            const textField = {
              key: key,
              name: input.name,
              desc: input.description,
              required: input.required
            };
            this.textFields.push(textField);

            const configTextField = [
              input.spec.default_value,
              [...(input.required ? [Validators.required] : [])]
            ];
            controlsConfig[key] = configTextField;
            break;
          }
          case 'OptionString': {
            const optionField = {
              key: key,
              name: input.name,
              desc: input.description,
              required: input.required,
              options: input.spec.options,
              selectedOptions: []
            };
            this.optionFields.push(optionField);
            const configOptionField = [
              input.spec.default_value,
              [...(input.required ? [Validators.required] : [])]
            ];
            controlsConfig[key] = configOptionField;
            break;
          }
          case 'Boolean': {
            const booleanField = {
              key: key,
              name: input.name,
              desc: input.description,
              required: input.required
            };
            this.booleanFields.push(booleanField);

            const configBooleanField = [
              input.spec.default_value,
              [...(input.required ? [Validators.required] : [])]
            ];
            controlsConfig[key] = configBooleanField;
            break;
          }
          default: {
            break;
          }
        }
      }
    }

    controlsConfig['job_name'] = [
      '',
      [...(this.operation.mode !== 'client' ? [Validators.required] : [])]
    ];
    this.analysesForm = this.formBuilder.group(controlsConfig);
  }

  /**
   * Load operation data before creating form controls
   */
  loadData() {
    if (this.operation.mode === 'client') {
      this.createForm(this.operation);
    } else {
      this.apiService.getOperation(this.operation.id).subscribe(data => {
        this.createForm(data);
      });
    }
  }

  onSubmit() {
    this.submitted = true;
  }

  /**
   * Function is used to convert strings to floats for numeric values
   * before sending the data to execute the operation
   */
  convertToFloatObj(obj) {
    const res = {};
    for (const key in obj) {
      res[key] =
        isNaN(obj[key]) || typeof obj[key] === 'boolean'
          ? obj[key]
          : parseFloat(obj[key]);
    }
    return res;
  }

  /**
   * Function is triggered when the user clicks the Run button
   * to start analysis
   */
  startAnalysis() {
    const inputs = this.convertToFloatObj(this.analysesForm.value);
    this.apiService
      .executeOperation(this.operation.id, this.workspaceId, inputs)
      .subscribe(data => {
        this.executedOperationId.emit(data.executed_operation_id);
      });
  }

  /**
   * Function is triggered when the user clicks the Show button
   * to visualize normalization results
   */
  showPlots() {
    const inputs = this.convertToFloatObj(this.analysesForm.value);
    this.boxPlotData = { ...inputs };

    this.dialog.open(AnalysisPlottingResultComponent, {
      data: {
        boxPlotData: { ...inputs }
      }
    });
  }

  /**
   * Convenience getter for easy access to form fields
   */
  get f() {
    return this.analysesForm.controls;
  }
}
<h3>{{ operation.name }}</h3>
<p>{{ operation.description }} </p>

<form *ngIf="analysesForm" [formGroup]="analysesForm" (ngSubmit)="onSubmit()">

  <!-- OPERATION NAME -->
  <ng-container *ngIf="operation.mode !== 'client'">
    <div class="label">
      Operation name:
      <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
        matTooltip="Type operation name" aria-label="Info tooltip about the field">info
      </mat-icon>
    </div>

    <mat-form-field class="form-control" color="accent">
      <input matInput formControlName="job_name" name="job_name">
    </mat-form-field>
  </ng-container>

  <!-- SINGLE SELECT LISTS FOR RESOURCES -->
  <div *ngFor="let resourceField of resourceFields">
    <div class="label">
      {{ resourceField.name }}
      <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
        matTooltip="{{ resourceField.desc}}" aria-label="Info tooltip about the field">info
      </mat-icon>
    </div>
    <mat-form-field class="form-control" color="accent">
      <mat-select [formControlName]="resourceField.key">
        <mat-option *ngFor="let file of resourceField.files" [value]="file.id">
          {{ file.name }}
        </mat-option>
      </mat-select>
      <div *ngIf="submitted && f[resourceField.key].errors">
        <mat-error *ngIf="f[resourceField.key].errors.required">
          This field is required
        </mat-error>
      </div>
    </mat-form-field>
  </div>



  <!-- MULTIPLE SELECT LISTS FOR RESOURCES -->
  <div *ngFor="let multipleResourceField of multipleResourceFields">
    <div class="form-control form-control--multiselect">
      <div class="label">
        {{ multipleResourceField.name }}
        <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
          matTooltip="{{ multipleResourceField.desc}}" aria-label="Info tooltip about the field">info
        </mat-icon>
      </div>
      <angular2-multiselect [formControlName]="multipleResourceField.key" [data]="multipleResourceField.files"
        [(ngModel)]="multipleResourceField.selectedFiles" [settings]="multipleResourcesDropdownSettings">
        <c-item>
          <ng-template let-item="item">
            <label>{{ item.name }} Type: {{ item.readable_resource_type || 'N/A' }}</label>
          </ng-template>
        </c-item>
        <c-badge>
          <ng-template let-item="item">
            <label>{{ item.name }}</label>
          </ng-template>
        </c-badge>
      </angular2-multiselect>
    </div>
  </div>




  <!-- NUMERIC FIELDS -->

  <div *ngFor="let numField of numFields">
    <div class="label">
      {{ numField.name }}
      <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
        matTooltip="{{ numField.desc}}" aria-label="Info tooltip about the field">info
      </mat-icon>
    </div>
    <mat-form-field class="form-control" color="accent">
      <input matInput [formControlName]="numField.key" [name]="numField.key">
      <div *ngIf="submitted && f[numField.key].errors">
        <mat-error *ngIf="f[numField.key].errors.required">
          This field is required
        </mat-error>
        <mat-error *ngIf="f[numField.key].errors.min || f[numField.key].errors.max || f[numField.key].errors.pattern">
          The value should be a number between {{ numField.min }} and {{ numField.max }}.
        </mat-error>
      </div>
    </mat-form-field>
  </div>

  <!-- INTEGER FIELDS -->

  <div *ngFor="let intField of intFields">
    <div class="label">
      {{ intField.name }}
      <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
        matTooltip="{{ intField.desc}}" aria-label="Info tooltip about the field">info
      </mat-icon>
    </div>
    <mat-form-field class="form-control" color="accent">
      <input matInput [formControlName]="intField.key" [name]="intField.key">
      <div *ngIf="f[intField.key].value && f[intField.key].errors">
        <mat-error *ngIf="f[intField.key].errors.required">
          This field is required
        </mat-error>
        <mat-error *ngIf="f[intField.key].errors.min || f[intField.key].errors.max || f[intField.key].errors.pattern">
          The value should be an integer greater than or equal to {{ intField.min }}
        </mat-error>
      </div>
    </mat-form-field>
  </div>


  <!-- TEXT FIELDS -->

  <div *ngFor="let textField of textFields">
    <div class="label">
      {{ textField.name }}
      <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
        matTooltip="{{ textField.desc}}" aria-label="Info tooltip about the field">info
      </mat-icon>
    </div>

    <mat-form-field class="form-control" color="accent">
      <input matInput [formControlName]="textField.key" [name]="textField.key">
      <div *ngIf="submitted && f[textField.key].errors">
        <mat-error *ngIf="f[textField.key].errors.required">
          This field is required
        </mat-error>
      </div>
    </mat-form-field>
  </div>


  <!-- SINGLE SELECT LISTS FOR OBSERVATION SETS -->
  <div *ngFor="let observationField of observationFields">
    <div class="label">
      {{ observationField.name }}
      <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
        matTooltip="{{ observationField.desc}}" aria-label="Info tooltip about the field">info
      </mat-icon>
    </div>

    <mat-form-field class="form-control" color="accent">
      <mat-select [formControlName]="observationField.key">
        <mat-option *ngFor="let set of observationField.sets" [value]="set">
          {{ set.name }}
        </mat-option>
      </mat-select>
      <div *ngIf="submitted && f[observationField.key].errors">
        <mat-error *ngIf="f[observationField.key].errors.required">
          This field is required
        </mat-error>
      </div>
    </mat-form-field>
  </div>

  <!-- SINGLE SELECT LISTS FOR FEATURE SETS -->
  <div *ngFor="let featureField of featureFields">
    <div class="label">
      {{ featureField.name }}
      <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
        matTooltip="{{ featureField.desc}}" aria-label="Info tooltip about the field">info
      </mat-icon>
    </div>

    <mat-form-field class="form-control" color="accent">
      <mat-select [formControlName]="featureField.key">
        <mat-option *ngFor="let set of featureField.sets" [value]="set">
          {{ set.name }}
        </mat-option>
      </mat-select>
      <div *ngIf="submitted && f[featureField.key].errors">
        <mat-error *ngIf="f[featureField.key].errors.required">
          This field is required
        </mat-error>
      </div>
    </mat-form-field>
  </div>


  <!-- SINGLE SELECT LISTS FOR OPERATION OPTIONS -->
  <div *ngFor="let optionField of optionFields">
    <div class="label">
      {{ optionField.name }}
      <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
        matTooltip="{{ optionField.desc}}" aria-label="Info tooltip about the field">info
      </mat-icon>
    </div>
    <mat-form-field class="form-control" color="accent">
      <mat-select [formControlName]="optionField.key">
        <mat-option *ngFor="let option of optionField.options" [value]="option">
          {{ option }}
        </mat-option>
      </mat-select>
      <div *ngIf="submitted && f[optionField.key].errors">
        <mat-error *ngIf="f[optionField.key].errors.required">
          This field is required
        </mat-error>
      </div>
    </mat-form-field>
  </div>


  <!-- CHECKBOXES FOR BOOLEAN FIELDS -->
  <ng-container *ngFor="let booleanField of booleanFields">
    <div class="form-control" color="accent">
      <mat-checkbox [formControlName]="booleanField.key" [checked]="booleanField.checked">
      </mat-checkbox>
      <span class="label">
        {{ booleanField.name }}
        <mat-icon class="analyses-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined"
          matTooltip="{{ booleanField.desc}}" aria-label="Info tooltip about the field">info
        </mat-icon>
      </span>
    </div>
  </ng-container>

  <div *ngIf="operation.mode !== 'client'; else plotContainer">
    <button mat-raised-button color="accent" type="submit" [disabled]="!analysesForm.valid"
      (click)="startAnalysis()">Run
    </button>
  </div>

  <ng-template #plotContainer>
    <button mat-raised-button color="accent" [disabled]="!analysesForm.valid" (click)="showPlots()">Show plots
    </button>

  </ng-template>

</form>

./operation.component.scss

.label {
  padding-right: 10px;
  font-weight: bolder;
}

.analyses-header__icon {
  font-size: 14px;
}

.form-control {
  width: 500px;
  padding-bottom: 50px;
}

mat-checkbox {
  padding-right: 10px;
}
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""