diff options
Diffstat (limited to 'frontend/src/app/_elements/column-table')
4 files changed, 886 insertions, 0 deletions
diff --git a/frontend/src/app/_elements/column-table/column-table.component.css b/frontend/src/app/_elements/column-table/column-table.component.css new file mode 100644 index 00000000..0477b7be --- /dev/null +++ b/frontend/src/app/_elements/column-table/column-table.component.css @@ -0,0 +1,266 @@ +table.fixed { + table-layout: fixed; + display: block; + overflow-x: auto; + white-space: nowrap; + font-size: 12px; + border-radius: 4px; +} + +#divTable { + height: 100%; + overflow-y: auto; +} + +table.fixed td { + overflow: hidden; + max-width: 200px; + min-width: 200px; + vertical-align: middle; + margin: 4px; +} + +table.fixed th { + overflow: hidden; + max-width: 120px; + min-width: 120px; + vertical-align: middle; + background-color: var(--ns-primary-50); + font-size: 14px; +} + +table.fixed th:first-child { + text-align: center; +} + +.columnNames { + background-color: var(--ns-primary-50) !important; +} + +.brighter { + background-color: var(--ns-primary) !important; + border-color: var(--offwhite); +} + +.border-bottom { + border-bottom-color: var(--offwhite) !important; +} + +mat-slider { + width: 300px; +} + +.slider { + background-color: var(--ns-bg-dark-100); +} + +#missingValuesHeader { + font-size: 13px; + line-height: 140% !important; +} + +.verticalAlign { + vertical-align: center; +} + +.cell-align { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + vertical-align: middle; + height: 100%; +} + +.text-left { + text-align: left !important; +} + +table ::ng-deep .mat-form-field-wrapper { + margin-top: -2rem; +} + +.graphics-row { + height: 100px; + padding: 1px; + margin: 0; +} + +.no-pad { + padding: 2px; + margin: 0; +} + +.text-overflow { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.row-height { + height: 30px; + border: none; + outline: none; +} + +.graphic-class { + opacity: 0.5; +} + + +/* TABS STYLE */ + +#folder-table { + border: 1px solid var(--ns-primary); + border-radius: 4px; + height: 70%; +} + +#tabs { + display: flex; + flex-direction: row; + align-items: flex-end; + height: 3.2rem; +} + +#tabs>.folder-tab:not(:first-child) { + margin-left: -5px; +} + +.folder-tab-end { + margin-left: auto; + color: var(--offwhite) !important; + overflow: hidden; +} + +.folder-tab, +.folder-tab-end { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + position: relative; + overflow-x: hidden; + height: 2.5rem; + background-color: var(--ns-bg-dark-100); + border-color: var(--ns-primary); + color: var(--ns-primary); + border-style: solid; + border-width: 1px 1px 0 1px; +} + +.folder-tab:not(:first-child) { + margin-block-start: auto; +} + +.folder-tab { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.selected-tab { + height: 3rem; + background-color: var(--ns-primary); + color: var(--offwhite); +} + +.hover-tab { + height: 3.2rem; +} + +.selected-tab, +.hover-tab { + width: fit-content !important; +} + +.tab-link { + color: var(--offwhite) !important; + text-decoration: none !important; + cursor: pointer; +} + +.tab-link:active { + text-decoration: underline !important; +} + +.selected-tab { + background-color: var(--ns-primary); +} + +.hidden { + /*visibility: hidden; + height: 1px;*/ + display: none; +} + +.bottom-button { + font-size: large; + position: relative; + background-color: var(--ns-primary); + width: 10rem; + height: 2.3rem; + border-color: var(--ns-primary); + border-style: solid; + border-width: 0px 1px 1px 1px; +} + +#footer { + display: flex; + flex-direction: row; + justify-content: center; +} + +.pad-fix { + padding-top: 0.6rem; + padding-bottom: 0; +} + +.long { + height: 3rem; +} + +.col-disabled { + background-color: rgb(0, 45, 69); +} + +.text-disabled { + color: gray; +} + +.header-disabled { + color: gray; +} + +.menu-disabled { + pointer-events: none; + opacity: .5; +} + +col:not(.col-disabled) { + background-color: var(--ns-bg-dark-100); +} + +.col-first { + background-color: rgb(1, 56, 86) !important; +} + + +/* mat-icon rotate */ + +.rotate { + animation: rotation 3s infinite linear; +} + +.rotate:hover { + cursor: pointer; +} + +@keyframes rotation { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +}
\ No newline at end of file diff --git a/frontend/src/app/_elements/column-table/column-table.component.html b/frontend/src/app/_elements/column-table/column-table.component.html new file mode 100644 index 00000000..efc093d2 --- /dev/null +++ b/frontend/src/app/_elements/column-table/column-table.component.html @@ -0,0 +1,249 @@ +<div *ngIf="loaded"> + <div id="tabs"> + <div class="folder-tab p-1 rounded-top" *ngFor="let tab of tabs; let i = index" [style]="'z-index:' + calcZIndex(i) + ' ;'" [ngClass]="{'selected-tab' : selectedTab.index == i, 'hover-tab' : hoveringOverTab?.index == i}"> + <a class="m-1 stretched-link tab-link" (click)="selectTab(i)" (mouseenter)="hoverOverTab(i)" (mouseleave)="hoverOverTab(-1)"> + {{tab.name}} + </a> + </div> + <!-- + <button mat-button class="p-1 folder-tab-end rounded-top"> + Kolone + <mat-icon>keyboard_double_arrow_down</mat-icon> + </button> + --> + </div> + <div id="folder-table" *ngIf="dataset && experiment"> + <!--<div [ngSwitch]="tabToDisplay">--> + <div id="divTable"> + + <div [ngClass]="{'hidden': tabToDisplay != Table.Data}"> + <table class="table text-offwhite fixed bg-blur"> + <colgroup> + <col class="col-first"> + <col *ngFor="let column of dataset.columnInfo; let i = index" [ngClass]="{'col-disabled' : !experiment.inputColumns.includes(column.columnName)}"> + </colgroup> + <thead> + <tr> + <th>#</th> + <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'header-disabled' : !columnsChecked[i]}"> + <div class="cell-align"> + #{{i + 1}} {{colInfo.columnName}} + <mat-checkbox color="primary" [(ngModel)]="columnsChecked[i]" (change)="changeInputColumns($event, colInfo.columnName)"></mat-checkbox> + </div> + </th> + </tr> + </thead> + <tbody> + <tr *ngFor="let row of tableData; let i = index"> + <th>#{{i}}</th> + <td *ngFor="let col of row; let j = index" [ngClass]="{'text-disabled' : !columnsChecked[j]}"> + <div class="text-overflow"> + {{col}} + </div> + </td> + </tr> + </tbody> + </table> + </div> + + <div [ngClass]="{'hidden': tabToDisplay != Table.CorrelationMatrix}"> + <table class="table text-offwhite fixed bg-blur"> + <colgroup> + <col class="col-first"> + <col *ngFor="let column of dataset.columnInfo; let i = index" [ngClass]="{'col-disabled' : !experiment.inputColumns.includes(column.columnName)}"> + </colgroup> + <thead> + <tr> + <th>Kolona</th> + <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'header-disabled' : !columnsChecked[i]}"> + <div class="cell-align"> + #{{i + 1}} {{colInfo.columnName}} + <mat-checkbox color="primary" [(ngModel)]="columnsChecked[i]" (change)="changeInputColumns($event, colInfo.columnName)"></mat-checkbox> + </div> + </th> + </tr> + </thead> + <tbody> + <tr *ngFor="let row of dataset.cMatrix; let i = index"> + <th [ngClass]="{'header-disabled col-disabled' : !columnsChecked[i]}"> + <div class="text-left"> + {{dataset.columnInfo[i].columnName}} + </div> + </th> + <td *ngFor="let corrVal of row; let j = index" [ngClass]="{'text-disabled col-disabled' : !columnsChecked[j] || !columnsChecked[i]}"> + <div class="text-overflow"> + {{corrVal}} + </div> + </td> + </tr> + </tbody> + </table> + </div> + + <div [ngClass]="{'hidden': tabToDisplay != Table.Columns}"> + <table class="table text-offwhite fixed bg-blur"> + <colgroup> + <col class="col-first"> + <col *ngFor="let column of dataset.columnInfo; let i = index" [ngClass]="{'col-disabled' : !experiment.inputColumns.includes(column.columnName)}"> + </colgroup> + <thead> + <tr> + <th>Naziv</th> + <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'header-disabled' : !columnsChecked[i]}"> + <div class="cell-align"> + #{{i + 1}} {{colInfo.columnName}} + <mat-checkbox color="primary" [(ngModel)]="columnsChecked[i]" (change)="changeInputColumns($event, colInfo.columnName)"></mat-checkbox> + </div> + </th> + </tr> + </thead> + <tbody> + <tr> + <th>Tip</th> + <td *ngFor="let colInfo of dataset.columnInfo; let i = index" class="pad-fix" [ngClass]="{'text-disabled' : !columnsChecked[i]}"> + <p class="verticalAlign text-left" style="font-size:13px;" *ngIf="!colInfo.isNumber">Kategorijski</p> + <mat-form-field *ngIf="colInfo.isNumber"> + <mat-select matNativeControl [(value)]="this.experiment.columnTypes[i]" [disabled]="!columnsChecked[i]" (selectionChange)="columnTypeChanged(colInfo.columnName);"> + <mat-option [value]="ColumnType.categorical">Kategorijski</mat-option> + <mat-option [value]="ColumnType.numerical">Numerički</mat-option> + </mat-select> + </mat-form-field> + </td> + </tr> + <tr class="graphics-row"> + <th class="no-pad">Grafik</th> + <td class="no-pad" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'graphic-class' : !columnsChecked[i]}"> + <app-box-plot *ngIf="this.experiment.columnTypes[i] == ColumnType.numerical" [width]="150" [height]="150"></app-box-plot> + <app-pie-chart *ngIf="this.experiment.columnTypes[i] == ColumnType.categorical" [width]="150" [height]="150"></app-pie-chart> + </td> + </tr> + <tr> + <th class="border-bottom">Statistika</th> + <td *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'text-disabled' : !columnsChecked[i]}" class="text-left"> + <span *ngIf="this.experiment.columnTypes[i] == ColumnType.numerical"> + Mean: {{colInfo.mean}}<br> + Median: {{colInfo.median}}<br> + Min: {{colInfo.min}}<br> + Max: {{colInfo.max}}<br> + Q1: {{colInfo.q1}}<br> + Q3: {{colInfo.q3}}<br> + </span> + <div class="text-overflow" *ngIf="this.experiment.columnTypes[i] == ColumnType.categorical && colInfo.uniqueValuesPercent"> + <span *ngFor="let uniqueValue of colInfo.uniqueValues | slice:0:6; let i = index"> + ({{(colInfo.uniqueValuesPercent[i] * 100).toFixed(2)}}%) {{uniqueValue}}<br> + </span> + </div> + </td> + </tr> + <tr style="padding: 0"> + <th class="brighter cell-align long" (click)="openEncodingDialog()"> + <span class="verticalAlign">Enkodiranje</span> + <span class="material-icons-round verticalAlign rotate">settings</span> + </th> + <td *ngFor="let colInfo of dataset.columnInfo; let i = index" class="pad-fix" [ngClass]="{'text-disabled' : !columnsChecked[i]}"> + <mat-form-field> + <mat-select matNativeControl [(value)]="experiment.encodings[i].encoding" [disabled]="!columnsChecked[i]" (selectionChange)="columnTableChangeDetected()"> + <mat-option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" [value]="option"> + {{ optionName }} + </mat-option> + </mat-select> + </mat-form-field> + </td> + </tr> + <tr> + <th class="brighter cell-align" (click)="openMissingValuesDialog()"> + <div id="missingValuesHeader">Nedostajuće<br>vrednosti<br></div> + <span class="material-icons-round rotate">settings</span> + </th> + <td *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'text-disabled' : !columnsChecked[i]}"> + + <div *ngIf="colInfo.numNulls > 0"> + <button class="w-100" mat-raised-button [ngClass]="{ 'menu-disabled' : !columnsChecked[i]}" [matMenuTriggerFor]="menu" id="main_{{colInfo.columnName}}" #nullValMenu> + <div class="cell-align"> + {{nullValOption[i]}} + <mat-icon>arrow_drop_down</mat-icon> + </div> + </button> + <mat-menu #menu="matMenu"> + <!--<button mat-menu-item (click)="MissValsDeleteClicked($event, NullValueOptions.DeleteColumns, i)" value={{colInfo.columnName}}>Obriši kolonu</button>--> + <button mat-menu-item (click)="MissValsDeleteClicked($event, NullValueOptions.DeleteRows, i)" value={{colInfo.columnName}}>Obriši redove ({{colInfo.numNulls}})</button> + <button mat-menu-item [matMenuTriggerFor]="fillWith">Popuni sa</button> + </mat-menu> + + <mat-menu #fillWith="matMenu"> + <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.mean}}>Mean ({{colInfo.mean}})</button> + <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.median}}>Median ({{colInfo.median}})</button> + <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.max}}>Max ({{colInfo.max}})</button> + <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.min}}>Min ({{colInfo.min}})</button> + + <button *ngIf="!colInfo.isNumber" mat-menu-item [matMenuTriggerFor]="uniques">Najčešće vrednosti</button> + + <button mat-menu-item [matMenuTriggerFor]="replaceWith">Unesi vrednost...</button> + </mat-menu> + + <mat-menu #uniques="matMenu"> + <button mat-menu-item *ngFor="let uniqueValue of colInfo.uniqueValues" (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{uniqueValue}}>{{uniqueValue}}</button> + </mat-menu> + + <mat-menu #replaceWith="matMenu"> + <input type="text" id={{colInfo.columnName}} mat-menu-item placeholder="Unesi vrednost..." [value] #enterAValue> + <button [disabled]="getValue(colInfo.columnName) == ''" mat-menu-item value={{getValue(colInfo.columnName)}} (click)="MissValsReplaceClicked($event, colInfo.columnName, i)">Potvrdi unos</button> + </mat-menu> + </div> + <div *ngIf="colInfo.numNulls == 0" class="text-left"> + Nema nedostajućih vrednosti. + </div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + + <div *ngIf="dataset && experiment" class="container-fluid text-offwhite belowColumn mt-3"> + <div class="ns-row"> + <div class="break-2"></div> + + <div class="ns-col rounded"> + <mat-form-field appearance="fill" class="align-items-center justify-content-center pt-3 w-100"> + <mat-label>Izlazna kolona</mat-label> + <mat-select [(value)]="experiment.outputColumn" (selectionChange)="changeOutputColumn(this.experiment.inputColumns[0])"> + <mat-option *ngFor="let inputColumn of experiment.inputColumns" [value]="inputColumn">{{inputColumn}}</mat-option> + </mat-select> + </mat-form-field> + </div> + <div class="ns-col rounded"> + <mat-form-field appearance="fill" class="align-items-center justify-content-center pt-3 w-100"> + <mat-label>Tip problema</mat-label> + <mat-select [(value)]="experiment.type"> + <mat-option [value]="ProblemType.Regression">Regresioni</mat-option> + <mat-option [value]="ProblemType.BinaryClassification">Binarni-klasifikacioni</mat-option> + <mat-option [value]="ProblemType.MultiClassification">Multi-klasifikacioni</mat-option> + </mat-select> + </mat-form-field> + </div> + <div class="break-1"></div> + <div class="ns-col d-flex align-items-center justify-content-center"> + <button *ngIf="experiment._id == ''" mat-button (click)="saveExperiment()" class="bottom-button text-offwhite rounded-bottom"> + <div class="f-row" style="justify-content: space-around; width: 100%;"> + <div>Sačuvaj</div> + <div class="icon-double pt-1"> + <mat-icon>check</mat-icon> + <mat-icon>check</mat-icon> + </div> + </div> + </button> + <button *ngIf="experiment._id != ''" mat-button (click)="updateExperiment()" class="bottom-button text-offwhite rounded-bottom"> + <div class="f-row" style="justify-content: space-around; width: 100%;"> + <div>Sačuvaj izmene</div> + <div class="icon-double pt-1"> + <mat-icon>check</mat-icon> + <mat-icon>check</mat-icon> + </div> + </div> + </button> + </div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_elements/column-table/column-table.component.spec.ts b/frontend/src/app/_elements/column-table/column-table.component.spec.ts new file mode 100644 index 00000000..360a8109 --- /dev/null +++ b/frontend/src/app/_elements/column-table/column-table.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ColumnTableComponent } from './column-table.component'; + +describe('ColumnTableComponent', () => { + let component: ColumnTableComponent; + let fixture: ComponentFixture<ColumnTableComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ColumnTableComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ColumnTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/column-table/column-table.component.ts b/frontend/src/app/_elements/column-table/column-table.component.ts new file mode 100644 index 00000000..c200e674 --- /dev/null +++ b/frontend/src/app/_elements/column-table/column-table.component.ts @@ -0,0 +1,346 @@ +import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChildren } from '@angular/core'; +import Dataset from 'src/app/_data/Dataset'; +import Experiment, { ColumnEncoding, Encoding, ColumnType, NullValueOptions } from 'src/app/_data/Experiment'; +import { DatasetsService } from 'src/app/_services/datasets.service'; +import { EncodingDialogComponent } from 'src/app/_modals/encoding-dialog/encoding-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; +import { MissingvaluesDialogComponent } from 'src/app/_modals/missingvalues-dialog/missingvalues-dialog.component'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { CsvParseService } from 'src/app/_services/csv-parse.service'; +import { ProblemType } from 'src/app/_data/Model'; +import { ExperimentsService } from 'src/app/_services/experiments.service'; +import { SaveExperimentDialogComponent } from 'src/app/_modals/save-experiment-dialog/save-experiment-dialog.component'; +import { AlertDialogComponent } from 'src/app/_modals/alert-dialog/alert-dialog.component'; +import Shared from 'src/app/Shared'; + +@Component({ + selector: 'app-column-table', + templateUrl: './column-table.component.html', + styleUrls: ['./column-table.component.css'] +}) +export class ColumnTableComponent implements AfterViewInit { + + @Input() dataset?: Dataset; + @Input() experiment!: Experiment; + @Output() okPressed: EventEmitter<string> = new EventEmitter(); + @Output() columnTableChanged = new EventEmitter(); + + Object = Object; + Encoding = Encoding; + NullValueOptions = NullValueOptions; + ColumnType = ColumnType; + ProblemType = ProblemType; + tableData?: any[][]; + nullValOption: string[] = []; + + columnsChecked: boolean[] = []; //niz svih kolona + loaded: boolean = false; + + + constructor(private datasetService: DatasetsService, private experimentService: ExperimentsService, public csvParseService: CsvParseService, public dialog: MatDialog) { + //ovo mi nece trebati jer primam dataset iz druge komponente + } + + loadDataset(dataset: Dataset) { + this.dataset = dataset; + + this.setColumnTypeInitial(); + + this.dataset.columnInfo.forEach(column => { + this.columnsChecked.push(true); + }); + + this.resetInputColumns(); + this.resetOutputColumn(); + this.resetColumnEncodings(Encoding.Label); + this.setDeleteRowsForMissingValTreatment(); + + this.nullValOption = []; + this.dataset.columnInfo.forEach(colInfo => { + this.nullValOption.push(`Obriši redove (${colInfo.numNulls})`); + }); + + this.datasetService.getDatasetFilePartial(this.dataset.fileId, 0, 10).subscribe((response: string | undefined) => { + if (response && this.dataset != undefined) { + this.tableData = this.csvParseService.csvToArray(response, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter.toString() == "") ? "," : this.dataset.delimiter); + } + }); + this.loaded = true; + } + + ngAfterViewInit(): void { + + } + + setColumnTypeInitial() { + if (this.dataset != undefined) { + for (let i = 0; i < this.dataset.columnInfo.length; i++) { + this.experiment.columnTypes[i] = (this.dataset.columnInfo[i].isNumber) ? ColumnType.numerical : ColumnType.categorical; + } + } + } + + resetInputColumns() { + if (this.dataset != undefined) { + this.experiment.inputColumns = []; + for (let i = 0; i < this.dataset?.columnInfo.length; i++) { + this.experiment.inputColumns.push(this.dataset.columnInfo[i].columnName); + } + } + } + resetOutputColumn() { + this.experiment.outputColumn = this.experiment.inputColumns[0]; + } + + setDeleteRowsForMissingValTreatment() { + if (this.experiment != undefined) { + this.experiment.nullValues = NullValueOptions.DeleteRows; + this.experiment.nullValuesReplacers = []; + for (let i = 0; i < this.experiment.inputColumns.length; i++) { + this.experiment.nullValuesReplacers.push({ + column: this.experiment.inputColumns[i], + option: NullValueOptions.DeleteRows, + value: "" + }); + } + } + } + + columnTableChangeDetected() { + this.columnTableChanged.emit(); + } + + columnTypeChanged(columnName: string) { + if (this.experiment.outputColumn == columnName) + this.changeOutputColumn(columnName); + else + this.columnTableChangeDetected(); + } + + changeInputColumns(targetMatCheckbox: MatCheckboxChange, columnName: string) { + if (this.experiment != undefined) { + + if (targetMatCheckbox.checked) { + if (this.experiment.inputColumns.filter(x => x == columnName)[0] == undefined) { + this.experiment.inputColumns.push(columnName); + } + } + else { + this.experiment.inputColumns = this.experiment.inputColumns.filter(x => x != columnName); + //console.log("Input columns: ", this.experiment.inputColumns); + //TODO: da se zatamni kolona koja je unchecked + //this.experiment.encodings = this.experiment.encodings.filter(x => x.columnName != columnName); samo na kraju iz enkodinga skloni necekirane + this.experiment.nullValuesReplacers = this.experiment.nullValuesReplacers.filter(x => x.column != columnName); + if (columnName == this.experiment.outputColumn) + this.experiment.outputColumn = this.experiment.inputColumns[0]; + } + this.columnTableChangeDetected(); + } + } + + changeOutputColumn(columnName: string) { + if (this.experiment != undefined && this.dataset != undefined) { + let i = this.dataset.columnInfo.findIndex(x => x.columnName == this.experiment!.outputColumn); + if (this.experiment.columnTypes[i] == ColumnType.numerical) { + this.experiment.type = ProblemType.Regression; + } + else { + if (this.dataset.columnInfo[i].uniqueValues!.length == 2) + this.experiment.type = ProblemType.BinaryClassification; + else + this.experiment.type = ProblemType.MultiClassification; + } + this.columnTableChangeDetected(); + } + } + + resetColumnEncodings(encodingType: Encoding) { + if (this.experiment != undefined && this.dataset != undefined) { + this.experiment.encodings = []; + for (let i = 0; i < this.dataset?.columnInfo.length; i++) { + this.experiment.encodings.push(new ColumnEncoding(this.dataset?.columnInfo[i].columnName, encodingType)); + //console.log(this.experiment.encodings); + } + this.columnTableChangeDetected(); + } + } + openEncodingDialog() { + const dialogRef = this.dialog.open(EncodingDialogComponent, { + width: '300px' + }); + dialogRef.afterClosed().subscribe(selectedEncoding => { + if (selectedEncoding != undefined) + this.resetColumnEncodings(selectedEncoding); + }); + } + + resetMissingValuesTreatment(selectedMissingValuesOption: NullValueOptions) { + if (this.experiment != undefined && this.dataset != undefined) { + + if (selectedMissingValuesOption == NullValueOptions.DeleteColumns) { + this.experiment.nullValues = NullValueOptions.DeleteColumns; + this.experiment.nullValuesReplacers = []; + for (let i = 0; i < this.experiment.inputColumns.length; i++) { + this.experiment.nullValuesReplacers.push({ + column: this.experiment.inputColumns[i], + option: NullValueOptions.DeleteColumns, + value: "" + }); + this.nullValOption[i] = "Obriši kolonu"; + } + } + else if (selectedMissingValuesOption == NullValueOptions.DeleteRows) { + this.experiment.nullValues = NullValueOptions.DeleteRows; + this.experiment.nullValuesReplacers = []; + for (let i = 0; i < this.experiment.inputColumns.length; i++) { + this.experiment.nullValuesReplacers.push({ + column: this.experiment.inputColumns[i], + option: NullValueOptions.DeleteRows, + value: "" + }); + let numOfRowsToDelete = (this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.inputColumns[i])[0]).numNulls; + this.nullValOption[i] = "Obriši redove (" + numOfRowsToDelete + ")"; + } + } + this.columnTableChangeDetected(); + } + } + openMissingValuesDialog() { + const dialogRef = this.dialog.open(MissingvaluesDialogComponent, { + width: '400px' + }); + dialogRef.afterClosed().subscribe(selectedMissingValuesOption => { + if (selectedMissingValuesOption != undefined) + this.resetMissingValuesTreatment(selectedMissingValuesOption); + }); + } + + openSaveExperimentDialog() { + const dialogRef = this.dialog.open(SaveExperimentDialogComponent, { + width: '400px' + }); + dialogRef.afterClosed().subscribe(selectedName => { + this.experiment.name = selectedName; + //napravi odvojene dugmice za save i update -> za update nece da se otvara dijalog za ime + this.experimentService.addExperiment(this.experiment).subscribe((response) => { + this.experiment._id = response._id; + this.okPressed.emit(); + }); + }); + } + + openUpdateExperimentDialog() { + this.experimentService.updateExperiment(this.experiment).subscribe((response) => { + this.experiment = response; + Shared.openDialog("Izmena eksperimenta", "Uspešno ste izmenili podatke o eksperimentu."); + }); + } + + MissValsDeleteClicked(event: Event, replacementType: NullValueOptions, index: number) { + if (this.experiment != undefined && this.dataset != undefined) { + let columnName = (<HTMLInputElement>event.currentTarget).value; + let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0]; + + if (arrayElement == undefined) { + this.experiment.nullValuesReplacers.push({ + column: columnName, + option: (replacementType == NullValueOptions.DeleteColumns) ? NullValueOptions.DeleteColumns : NullValueOptions.DeleteRows, + value: "" + }); + } + else { + arrayElement.option = (replacementType == NullValueOptions.DeleteColumns) ? NullValueOptions.DeleteColumns : NullValueOptions.DeleteRows; + arrayElement.value = ""; + } + + let numOfRowsToDelete = (this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.inputColumns[index])[0]).numNulls; + this.nullValOption[index] = (replacementType == NullValueOptions.DeleteColumns) ? "Obriši kolonu" : "Obriši redove (" + numOfRowsToDelete + ")"; + this.columnTableChangeDetected(); + } + } + + MissValsReplaceClicked(event: Event, columnName: string, index: number) { + if (this.experiment != undefined) { + let fillValue = (<HTMLInputElement>event.currentTarget).value; + let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0]; + + if (arrayElement == undefined) { + this.experiment.nullValuesReplacers.push({ + column: columnName, + option: NullValueOptions.Replace, + value: fillValue + }); + } + else { + arrayElement.option = NullValueOptions.Replace; + arrayElement.value = fillValue; + } + + this.nullValOption[index] = "Popuni sa: " + fillValue; + this.columnTableChangeDetected(); + } + } + getValue(columnName: string): string { + if (<HTMLInputElement>document.getElementById(columnName) != undefined) + return (<HTMLInputElement>document.getElementById(columnName)).value; + return '0'; + } + saveExperiment() { + this.openSaveExperimentDialog(); + } + updateExperiment() { + this.openUpdateExperimentDialog(); + } + + + tabs = [ + new Tab(0, 'Podešavanja kolona', Table.Columns), + new Tab(1, 'Podaci', Table.Data), + new Tab(2, 'Korelaciona matrica', Table.CorrelationMatrix) + ] + + selectedTab: Tab = this.tabs[0]; + hoveringOverTab: (Tab | null) = null; + + tabToDisplay: Table = Table.Columns; + + selectTab(index: number) { + this.selectedTab = this.tabs[index]; + this.tabToDisplay = this.tabs[index].value; + } + + hoverOverTab(index: number) { + if (index < 0) { + this.hoveringOverTab = null; + this.tabToDisplay = this.selectedTab.value; + } else { + this.hoveringOverTab = this.tabs[index]; + this.tabToDisplay = this.tabs[index].value; + } + } + + calcZIndex(i: number) { + let zIndex = (this.tabs.length - i - 1) + if (this.selectedTab.index == i) + zIndex = this.tabs.length + 1; + if (this.hoveringOverTab?.index == i) + zIndex = this.tabs.length + 2; + return zIndex; + } + + Table = Table; +} + +export enum Table { + Columns, + Data, + CorrelationMatrix +} + +export class Tab { + constructor( + public index: number, + public name: string, + public value: Table + ) { } +} |