diff options
Diffstat (limited to 'frontend/src')
61 files changed, 800 insertions, 1423 deletions
diff --git a/frontend/src/app/_data/Dataset.ts b/frontend/src/app/_data/Dataset.ts index 4ff0a471..a962fe6b 100644 --- a/frontend/src/app/_data/Dataset.ts +++ b/frontend/src/app/_data/Dataset.ts @@ -1,7 +1,6 @@ import { FolderFile } from "./FolderFile"; export default class Dataset extends FolderFile { - _id: string = ''; constructor( name: string = 'Novi izvor podataka', public description: string = '', @@ -29,15 +28,15 @@ export class ColumnInfo { public columnName: string = '', public isNumber: boolean = false, public numNulls: number = 0, - public uniqueValues?: string[], - public uniqueValuesCount?: number[], - public uniqueValuesPercent?: number[], - public median?: number, - public mean?: number, - public min?: number, - public max?: number, - public q1?: number, - public q3?: number, + public uniqueValues: string[]=[], + public uniqueValuesCount: number[]=[], + public uniqueValuesPercent: number[]=[], + public median: number=0, + public mean: number=0, + public min: number=0, + public max: number=0, + public q1: number=0, + public q3: number=0, ) { /*if (isNumber) this.columnType = ColumnType.numerical; diff --git a/frontend/src/app/_data/Experiment.ts b/frontend/src/app/_data/Experiment.ts index cff77535..c140e100 100644 --- a/frontend/src/app/_data/Experiment.ts +++ b/frontend/src/app/_data/Experiment.ts @@ -1,9 +1,9 @@ +import { FolderFile } from "./FolderFile"; import { ProblemType } from "./Model"; -export default class Experiment { - _id: string = ''; +export default class Experiment extends FolderFile { uploaderId: string = ''; constructor( - public name: string = 'Novi eksperiment', + name: string = 'Novi eksperiment', public description: string = '', public type: ProblemType = ProblemType.Regression, public datasetId: string = '', @@ -11,12 +11,14 @@ export default class Experiment { public outputColumn: string = '', public nullValues: NullValueOptions = NullValueOptions.DeleteRows, public nullValuesReplacers: NullValReplacer[] = [], - public dateCreated: Date = new Date(), - public lastUpdated: Date = new Date(), + dateCreated: Date = new Date(), + lastUpdated: Date = new Date(), public modelIds: string[] = [], public columnTypes: ColumnType[] = [], public encodings: ColumnEncoding[] = []//[{columnName: "", columnEncoding: Encoding.Label}] - ) { } + ) { + super(name, dateCreated, lastUpdated) + } _columnsSelected: boolean = false; } diff --git a/frontend/src/app/_data/FolderFile.ts b/frontend/src/app/_data/FolderFile.ts index c228f25e..4d1844fd 100644 --- a/frontend/src/app/_data/FolderFile.ts +++ b/frontend/src/app/_data/FolderFile.ts @@ -1,4 +1,5 @@ export class FolderFile { + public _id: string = ""; constructor( public name: string, public dateCreated: Date, diff --git a/frontend/src/app/_data/Model.ts b/frontend/src/app/_data/Model.ts index 526a8290..cc25c91b 100644 --- a/frontend/src/app/_data/Model.ts +++ b/frontend/src/app/_data/Model.ts @@ -2,7 +2,6 @@ import { NgIf } from "@angular/common"; import { FolderFile } from "./FolderFile"; export default class Model extends FolderFile { - _id: string = ''; constructor( name: string = 'Novi model', public description: string = '', @@ -13,7 +12,7 @@ export default class Model extends FolderFile { // Neural net training settings public type: ProblemType = ProblemType.Regression, public optimizer: Optimizer = Optimizer.Adam, - public lossFunction: LossFunction = LossFunction.MeanSquaredError, + public lossFunction: LossFunction = LossFunctionRegression[0], public inputNeurons: number = 1, public hiddenLayers: number = 1, public batchSize: BatchSize = BatchSize.O3, @@ -132,21 +131,10 @@ export enum LossFunction { MeanSquaredLogarithmicError = 'mean_squared_logarithmic_error', HuberLoss = 'Huber' } -export enum LossFunctionRegression { - MeanAbsoluteError = 'mean_absolute_error', - MeanSquaredError = 'mean_squared_error', - MeanSquaredLogarithmicError = 'mean_squared_logarithmic_error', -} -export enum LossFunctionBinaryClassification { - BinaryCrossEntropy = 'binary_crossentropy', - SquaredHingeLoss = 'squared_hinge_loss', - HingeLoss = 'hinge_loss', -} -export enum LossFunctionMultiClassification { - CategoricalCrossEntropy = 'categorical_crossentropy', - SparseCategoricalCrossEntropy = 'sparse_categorical_crossentropy', - KLDivergence = 'kullback_leibler_divergence', -} +export const LossFunctionRegression = [LossFunction.MeanAbsoluteError, LossFunction.MeanSquaredError, LossFunction.MeanSquaredLogarithmicError] +export const LossFunctionBinaryClassification = [LossFunction.BinaryCrossEntropy, LossFunction.SquaredHingeLoss, LossFunction.HingeLoss] + +export const LossFunctionMultiClassification = [LossFunction.CategoricalCrossEntropy, LossFunction.SparseCategoricalCrossEntropy, LossFunction.KLDivergence] export enum Optimizer { Adam = 'Adam', diff --git a/frontend/src/app/_data/Predictor.ts b/frontend/src/app/_data/Predictor.ts index 8aa2b6cb..55d610ed 100644 --- a/frontend/src/app/_data/Predictor.ts +++ b/frontend/src/app/_data/Predictor.ts @@ -1,13 +1,28 @@ -export default class Predictor { - _id: string = ''; +import { FolderFile } from "./FolderFile"; + +export default class Predictor extends FolderFile { constructor( - public name: string = 'Novi prediktor', + name: string = 'Novi prediktor', public description: string = '', public inputs: string[] = [], public output: string = '', public isPublic: boolean = false, public accessibleByLink: boolean = false, - public dateCreated: Date = new Date(), - public uploaderId: string = '' + dateCreated: Date = new Date(), + lastUpdated: Date = new Date(), + public uploaderId: string = '', + //public finalMetrics: Metric[] = [] + public experimentId: string = "", + public modelId: string = "", + ) { + super(name, dateCreated, lastUpdated); + } +} + +export class Metric { + constructor( + public name: string = '', + public jsonValue: string = '' ) { } + }
\ No newline at end of file diff --git a/frontend/src/app/_data/User.ts b/frontend/src/app/_data/User.ts index be42ed0a..527bac77 100644 --- a/frontend/src/app/_data/User.ts +++ b/frontend/src/app/_data/User.ts @@ -6,6 +6,8 @@ export default class User { public password: string = '', public firstName: string = '', public lastName: string = '', - public photoId: string = '1' //difoltna profilna slika + public photoId: string = '1', //difoltna profilna slika + public isPermament:boolean=false, + public dateCreated:Date=new Date() ) { } }
\ No newline at end of file diff --git a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts index d6f4b6ec..bf5e3fd6 100644 --- a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts +++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts @@ -16,16 +16,31 @@ Chart.register(BoxPlotController, BoxAndWiskers, LinearScale, CategoryScale); }) export class BoxPlotComponent implements AfterViewInit { - @Input()width?: number; - @Input()height?: number; - + @Input() width?: number; + @Input() height?: number; + @Input() mean?: number; + @Input() median?: number; + @Input() min?: number; + @Input() max?: number; + @Input() q1?: number; + @Input() q3?: number; + + updateChart(min: number, max: number, q1: number, q3: number, median: number) { + if (this.myChart) { + this.boxplotData.datasets[0].data = [[min, q1, median, q3, max]] + this.myChart.update(); + } + }; + @ViewChild('boxplot') chartRef!: ElementRef; - constructor() { } + constructor() { + //this.updateChart(); + } boxplotData = { // define label tree - labels: ['January'/*, 'February', 'March', 'April', 'May', 'June', 'July'*/], - datasets: [{ + //labels: ['January'/*, 'February', 'March', 'April', 'May', 'June', 'July'*/], + datasets: [{ label: 'Dataset 1', backgroundColor: '#0063AB', borderColor: '#dfd7d7', @@ -35,68 +50,43 @@ export class BoxPlotComponent implements AfterViewInit { padding: 10, itemRadius: 0, data: [ - randomValues(100, 0, 100), - /*randomValues(100, 0, 20), - randomValues(100, 20, 70), - randomValues(100, 60, 100), - randomValues(40, 50, 100), - randomValues(100, 60, 120), - randomValues(100, 80, 100)*/ - ]}/*, { - label: 'Dataset 2', - backgroundColor: 'rgba(0,0,255,0.5)', - borderColor: 'blue', - borderWidth: 1, - outlierColor: '#999999', - padding: 10, - itemRadius: 0, - data: [ - randomValues(100, 60, 100), - randomValues(100, 0, 100), - randomValues(100, 0, 20), - randomValues(100, 20, 70), - randomValues(40, 60, 120), - randomValues(100, 20, 100), - randomValues(100, 80, 100) - ] - }*/] - }; + randomValues(100, 0, 100), + ] + }] + }; ngAfterViewInit(): void { - const myChart = new Chart(this.chartRef.nativeElement, { - type: "boxplot", - data: this.boxplotData, - options: { - /*title: { - display: true, - text: 'Predicted world population (millions) in 2050' - }*/ - plugins:{ - legend: { - display: false - }, - }, - scales : { - x: { - ticks: { - color: '#dfd7d7' - }, - grid: { - color: "rgba(0, 99, 171, 0.5)" - } - }, - y : { - min: -50, - max: 200, - ticks: { - color: '#dfd7d7' - }, - grid: { - color: "rgba(0, 99, 171, 0.5)" - } - } + this.myChart = new Chart(this.chartRef.nativeElement, { + type: "boxplot", + data: this.boxplotData, + options: { + plugins: { + legend: { + display: false + }, + }, + scales: { + x: { + ticks: { + color: '#dfd7d7' + }, + grid: { + color: "rgba(0, 99, 171, 0.5)" + } + }, + y: { + min: this.min, + max: this.max, + ticks: { + color: '#dfd7d7' + }, + grid: { + color: "rgba(0, 99, 171, 0.5)" + } } - } - }); -} + } + } + }); + } + myChart?: Chart; } diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css index e69de29b..a190693a 100644 --- a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css +++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css @@ -0,0 +1,9 @@ +canvas{ + + width:100% !important; + height:90% !important; + border: 1px solid var(--ns-primary); + background-color: var(--ns-bg-dark-100); + border-radius: 5px; + margin: 10px; + }
\ No newline at end of file diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html index 7f18256a..5bb7aae6 100644 --- a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html +++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html @@ -1,3 +1,4 @@ - <canvas id="myChart" style="width: 100%; height: 530px;"> - </canvas>
\ No newline at end of file + <canvas id="myChart" > + </canvas> +
\ No newline at end of file diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts index 655db9ec..e873618c 100644 --- a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts +++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts @@ -13,22 +13,18 @@ export class LineChartComponent implements AfterViewInit { dataMAE: number[] = []; dataMSE: number[] = []; dataLOSS: number[] = []; - + dataValAcc:number[]=[]; + dataValMAE:number[]=[]; + dataValMSE:number[]=[]; + dataValLoss:number[]=[]; dataEpoch: number[] = []; constructor() { - /*let i = 0; - setInterval(() => { - this.dataAcc.push(0.5); - this.dataEpoch.push(i); - i++; - this.update(); - }, 200);*/ } myChart!: Chart; - update(myEpochs: number[], myAcc: number[], myLoss: number[], myMae: number[], myMse: number[]) { + update(myEpochs: number[], myAcc: number[], myLoss: number[], myMae: number[], myMse: number[], myValAcc:number[],myValLoss:number[],myValMae:number[],myValMse:number[]) { this.dataAcc.length = 0; this.dataAcc.push(...myAcc); @@ -42,6 +38,18 @@ export class LineChartComponent implements AfterViewInit { this.dataLOSS.push(...myLoss); this.dataMSE.length = 0; + this.dataMSE.push(...myValAcc); + + this.dataMSE.length = 0; + this.dataMSE.push(...myValLoss); + + this.dataMSE.length = 0; + this.dataMSE.push(...myValMae); + + this.dataMSE.length = 0; + this.dataMSE.push(...myValMse); + + this.dataMSE.length = 0; this.dataMSE.push(...myMse); this.myChart.update(); @@ -54,8 +62,15 @@ export class LineChartComponent implements AfterViewInit { data: { labels: this.dataEpoch, datasets: [{ + label: 'Accuracy', data: this.dataAcc, + borderWidth: 1, + + }, + { + label: 'VAl_Accuracy', + data: this.dataMSE, borderWidth: 1 }, { @@ -64,18 +79,47 @@ export class LineChartComponent implements AfterViewInit { borderWidth: 1 }, { + label: 'Val_Loss', + data: this.dataMSE, + borderWidth: 1 + }, + { label: 'MAE', data: this.dataMAE, borderWidth: 1 }, { + label: 'Val_MAE', + data: this.dataMSE, + borderWidth: 1 + }, + { label: 'MSE', data: this.dataMSE, borderWidth: 1 + }, + { + label: 'Val_MSE', + data: this.dataMSE, + borderWidth: 1 } ] }, options: { + responsive: true, + maintainAspectRatio: true, + + plugins: { + legend: { + labels: { + // This more specific font property overrides the global property + color:'white', + font: { + size: 10 + } + } + } + }, scales: { x: { ticks: { @@ -83,21 +127,24 @@ export class LineChartComponent implements AfterViewInit { }, grid: { color: "rgba(0, 99, 171, 0.5)" - } + }, }, y: { beginAtZero: true, ticks: { color: 'white' + }, grid: { color: "rgba(0, 99, 171, 0.5)" } } + }, + animation: { + duration: 0 } - } } ); diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts index f141f522..c2bd3262 100644 --- a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts +++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts @@ -10,21 +10,36 @@ export class PieChartComponent implements AfterViewInit { @Input()width?: number; @Input()height?: number; + @Input()uniqueValues?: string[] = []; + @Input()uniqueValuesPercent?: number[] = []; + + updatePieChart(uniqueValues: string[], uniqueValuesPercent: number[]){ + console.log(this.uniqueValues, this.uniqueValuesPercent); + const newPieChartData = { + datasets: [{ + label: "Population (millions)", + backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"], + data: [2478,5267,734,784,433], + }] + + } + }; @ViewChild('piechart') chartRef!: ElementRef; constructor() { } + pieChartData = { + datasets: [{ + label: "Population (millions)", + backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"], + data: [2478,5267,734,784,433] + }] +} + ngAfterViewInit(): void { const myChart = new Chart(this.chartRef.nativeElement, { type: 'pie', - data: { - labels: ["Africa", "Asia", "Europe", "Latin America", "North America"], - datasets: [{ - label: "Population (millions)", - backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"], - data: [2478,5267,734,784,433], - }] - }, + data: this.pieChartData, options: { /*title: { display: true, @@ -36,11 +51,9 @@ export class PieChartComponent implements AfterViewInit { }, }, layout: { - padding: 15} + padding: 15 + } } -}); - - } + });} - -} +}
\ No newline at end of file diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css index 005cb692..3e91b926 100644 --- a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css +++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css @@ -1,4 +1,8 @@ -#divScatterChart{ - - display: block; -}
\ No newline at end of file +canvas{ + width:95% !important; + height:95% !important; + border: 1px solid var(--ns-primary); + background-color: var(--ns-bg-dark-100); + border-radius: 5px; + margin: 10px; + }
\ No newline at end of file diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html index ef41775a..eedc6ade 100644 --- a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html +++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html @@ -1,4 +1,2 @@ -<div id="divScatterChart" style="width: 100%;height: 100%;"> - <canvas id="ScatterCharts" style="width: 100%;height: 280px;"> </canvas> -</div>
\ No newline at end of file + <canvas id="ScatterCharts"> </canvas> diff --git a/frontend/src/app/_elements/column-table/column-table.component.html b/frontend/src/app/_elements/column-table/column-table.component.html index efc093d2..d07d50b2 100644 --- a/frontend/src/app/_elements/column-table/column-table.component.html +++ b/frontend/src/app/_elements/column-table/column-table.component.html @@ -113,8 +113,8 @@ <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> + <app-box-plot *ngIf="this.experiment.columnTypes[i] == ColumnType.numerical" [width]="150" [height]="150" [mean]="colInfo.mean" [median]="colInfo.median" [min]="colInfo.min" [max]="colInfo.max" [q1]="colInfo.q1" [q3]="colInfo.q3"></app-box-plot> + <app-pie-chart *ngIf="this.experiment.columnTypes[i] == ColumnType.categorical" [width]="150" [height]="150" [uniqueValues]="colInfo.uniqueValues" [uniqueValuesPercent]="colInfo.uniqueValuesPercent"></app-pie-chart> </td> </tr> <tr> @@ -142,7 +142,10 @@ </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-select matNativeControl [(value)]="experiment.encodings[i].encoding" [disabled]="!columnsChecked[i] || experiment.columnTypes[i] == ColumnType.numerical || colInfo.columnName == experiment.outputColumn" (selectionChange)="columnTableChangeDetected()"> + <mat-option [value]="Encoding.Label" *ngIf="experiment.columnTypes[i] == ColumnType.numerical || colInfo.columnName == experiment.outputColumn" [selected]="experiment.columnTypes[i] == ColumnType.numerical || colInfo.columnName == experiment.outputColumn"> + Nema enkodiranja + </mat-option> <mat-option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" [value]="option"> {{ optionName }} </mat-option> @@ -208,8 +211,9 @@ <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-select [(value)]="experiment.outputColumn" (selectionChange)="changeProblemType()"> <mat-option *ngFor="let inputColumn of experiment.inputColumns" [value]="inputColumn">{{inputColumn}}</mat-option> + <mat-option *ngIf="experiment.inputColumns.length == 0" value="-">-</mat-option> </mat-select> </mat-form-field> </div> diff --git a/frontend/src/app/_elements/column-table/column-table.component.ts b/frontend/src/app/_elements/column-table/column-table.component.ts index c200e674..217eda30 100644 --- a/frontend/src/app/_elements/column-table/column-table.component.ts +++ b/frontend/src/app/_elements/column-table/column-table.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChildren } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, 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'; @@ -12,6 +12,8 @@ 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'; +import { PieChartComponent } from '../_charts/pie-chart/pie-chart.component'; +import { BoxPlotComponent } from '../_charts/box-plot/box-plot.component'; @Component({ selector: 'app-column-table', @@ -20,10 +22,13 @@ import Shared from 'src/app/Shared'; }) export class ColumnTableComponent implements AfterViewInit { + @ViewChildren(BoxPlotComponent) boxplotComp!: QueryList<BoxPlotComponent>; + @ViewChildren(PieChartComponent) piechartComp!: QueryList<PieChartComponent>; @Input() dataset?: Dataset; @Input() experiment!: Experiment; @Output() okPressed: EventEmitter<string> = new EventEmitter(); @Output() columnTableChanged = new EventEmitter(); + @Output() experimentChanged = new EventEmitter(); Object = Object; Encoding = Encoding; @@ -41,9 +46,35 @@ export class ColumnTableComponent implements AfterViewInit { //ovo mi nece trebati jer primam dataset iz druge komponente } + updateCharts() { + //min: number, max: number, q1: number, q3: number, median: number + let i = 0; + this.boxplotComp.changes.subscribe(() => { + const bps = this.boxplotComp.toArray(); + this.dataset?.columnInfo.forEach(colInfo => { + if (this.experiment.columnTypes[i] == ColumnType.numerical) { + bps[i].updateChart(colInfo!.min, colInfo.max, colInfo.q1, colInfo.q3, colInfo.median); + i++; + } + }); + }); + } + + updatePieChart() { + //min: number, max: number, q1: number, q3: number, median: number + let i = 0; + const pieChart = this.piechartComp.toArray(); + this.dataset?.columnInfo.forEach(colInfo => { + if (this.experiment.columnTypes[i] == ColumnType.categorical) { + pieChart[i].updatePieChart(colInfo!.uniqueValues, colInfo.uniqueValuesPercent); + i++; + } + }); + } + loadDataset(dataset: Dataset) { + console.log("LOADED DATASET"); this.dataset = dataset; - this.setColumnTypeInitial(); this.dataset.columnInfo.forEach(column => { @@ -62,13 +93,17 @@ export class ColumnTableComponent implements AfterViewInit { 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.tableData = this.csvParseService.csvToArray(response, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter); } }); this.loaded = true; + + this.updateCharts(); + this.updatePieChart(); } ngAfterViewInit(): void { + console.log(this.dataset?.columnInfo); } @@ -89,7 +124,10 @@ export class ColumnTableComponent implements AfterViewInit { } } resetOutputColumn() { - this.experiment.outputColumn = this.experiment.inputColumns[0]; + if (this.experiment.inputColumns.length > 0) + this.experiment.outputColumn = this.experiment.inputColumns[0]; + else + this.experiment.outputColumn = '-'; } setDeleteRowsForMissingValTreatment() { @@ -112,7 +150,7 @@ export class ColumnTableComponent implements AfterViewInit { columnTypeChanged(columnName: string) { if (this.experiment.outputColumn == columnName) - this.changeOutputColumn(columnName); + this.changeProblemType(); else this.columnTableChangeDetected(); } @@ -124,6 +162,8 @@ export class ColumnTableComponent implements AfterViewInit { if (this.experiment.inputColumns.filter(x => x == columnName)[0] == undefined) { this.experiment.inputColumns.push(columnName); } + if (this.experiment.inputColumns.length == 1) + this.experiment.outputColumn = this.experiment.inputColumns[0]; } else { this.experiment.inputColumns = this.experiment.inputColumns.filter(x => x != columnName); @@ -131,17 +171,21 @@ export class ColumnTableComponent implements AfterViewInit { //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]; + if (columnName == this.experiment.outputColumn) { + if (this.experiment.inputColumns.length > 0) + this.experiment.outputColumn = this.experiment.inputColumns[0]; + else + this.experiment.outputColumn = '-'; + } } this.columnTableChangeDetected(); } } - changeOutputColumn(columnName: string) { + changeProblemType() { 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) { + if (i == -1 || this.experiment.columnTypes[i] == ColumnType.numerical) { this.experiment.type = ProblemType.Regression; } else { @@ -157,20 +201,30 @@ export class ColumnTableComponent implements AfterViewInit { resetColumnEncodings(encodingType: Encoding) { if (this.experiment != undefined && this.dataset != undefined) { this.experiment.encodings = []; - for (let i = 0; i < this.dataset?.columnInfo.length; i++) { + 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(); } } + resetColumnEncodingsGlobalSetting(encodingType: Encoding) { + if (this.experiment != undefined && this.dataset != undefined) { + for (let i = 0; i < this.dataset.columnInfo.length; i++) { + if (this.experiment.columnTypes[i] == ColumnType.categorical && this.dataset.columnInfo[i].columnName != this.experiment.outputColumn) //promeni + this.experiment.encodings[i].encoding = encodingType; + } + this.columnTableChangeDetected(); + } + } openEncodingDialog() { const dialogRef = this.dialog.open(EncodingDialogComponent, { - width: '300px' + width: '400px', + data: { experiment: this.experiment, dataset: this.dataset } }); dialogRef.afterClosed().subscribe(selectedEncoding => { if (selectedEncoding != undefined) - this.resetColumnEncodings(selectedEncoding); + this.resetColumnEncodingsGlobalSetting(selectedEncoding); }); } @@ -217,21 +271,24 @@ export class ColumnTableComponent implements AfterViewInit { openSaveExperimentDialog() { const dialogRef = this.dialog.open(SaveExperimentDialogComponent, { - width: '400px' + width: '400px', + data: { experiment: this.experiment } }); - 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(); - }); + dialogRef.afterClosed().subscribe(experiment => { + if (experiment) { + Object.assign(this.experiment, experiment); + this.experiment._columnsSelected = true; + this.experimentChanged.emit(); + console.log(this.experiment); + } }); } openUpdateExperimentDialog() { this.experimentService.updateExperiment(this.experiment).subscribe((response) => { - this.experiment = response; + Object.assign(this.experiment, response); + this.experiment._columnsSelected = true; + this.experimentChanged.emit(); Shared.openDialog("Izmena eksperimenta", "Uspešno ste izmenili podatke o eksperimentu."); }); } @@ -286,10 +343,20 @@ export class ColumnTableComponent implements AfterViewInit { return '0'; } saveExperiment() { - this.openSaveExperimentDialog(); + if (this.experiment.inputColumns.length == 0) + Shared.openDialog("Upozorenje", "Kako bi eksperiment bio uspešno izveden, neophodno je da izaberete barem dve kolone koje ćete koristiti."); + else if (this.experiment.inputColumns.length == 1) + Shared.openDialog("Upozorenje", "Kako bi eksperiment bio uspešno izveden, neophodno je da izaberete barem dve kolone koje ćete koristiti (mora postojati bar jedna ulazna i jedna izlazna kolona)."); + else + this.openSaveExperimentDialog(); } updateExperiment() { - this.openUpdateExperimentDialog(); + if (this.experiment.inputColumns.length == 0) + Shared.openDialog("Upozorenje", "Kako bi eksperiment bio uspešno izveden, neophodno je da izaberete barem dve kolone koje ćete koristiti."); + else if (this.experiment.inputColumns.length == 1) + Shared.openDialog("Upozorenje", "Kako bi eksperiment bio uspešno izveden, neophodno je da izaberete barem dve kolone koje ćete koristiti (mora postojati bar jedna ulazna i jedna izlazna kolona)."); + else + this.openUpdateExperimentDialog(); } @@ -312,10 +379,8 @@ export class ColumnTableComponent implements AfterViewInit { 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; } } @@ -344,3 +409,5 @@ export class Tab { public value: Table ) { } } + + diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts deleted file mode 100644 index 73dbf2d2..00000000 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Component, OnInit, ViewChild, ViewChildren } from '@angular/core'; -import { AddNewDatasetComponent } from '../add-new-dataset/add-new-dataset.component'; -import { ModelsService } from 'src/app/_services/models.service'; -import shared from 'src/app/Shared'; -import Dataset from 'src/app/_data/Dataset'; -import { DatatableComponent, TableData } from 'src/app/_elements/datatable/datatable.component'; -import { DatasetsService } from 'src/app/_services/datasets.service'; -import { CsvParseService } from 'src/app/_services/csv-parse.service'; -import { Output, EventEmitter } from '@angular/core'; -import { SignalRService } from 'src/app/_services/signal-r.service'; - -@Component({ - selector: 'app-dataset-load', - templateUrl: './dataset-load.component.html', - styleUrls: ['./dataset-load.component.css'] -}) -export class DatasetLoadComponent implements OnInit { - - @Output() selectedDatasetChangeEvent = new EventEmitter<Dataset>(); - - @ViewChild(AddNewDatasetComponent) addNewDatasetComponent!: AddNewDatasetComponent; - @ViewChild(AddNewDatasetComponent) datatable!: DatatableComponent; - - datasetLoaded: boolean = false; - selectedDatasetLoaded: boolean = false; - - showMyDatasets: boolean = true; - myDatasets?: Dataset[]; - existingDatasetSelected: boolean = false; - selectedDataset?: Dataset; - - tableData: TableData = new TableData(); - - term: string = ""; - - constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService) { - this.datasets.getMyDatasets().subscribe((datasets) => { - this.myDatasets = datasets; - }); - } - - viewMyDatasetsForm() { - this.showMyDatasets = true; - if (this.selectedDataset != undefined) - this.resetSelectedDataset(); - //this.resetCbsAndRbs(); //TREBA DA SE DESI - } - viewNewDatasetForm() { - this.showMyDatasets = false; - if (this.selectedDataset != undefined) - this.resetSelectedDataset(); - //this.resetCbsAndRbs(); //TREBA DA SE DESI - } - - refreshMyDatasets() { - this.datasets.getMyDatasets().subscribe((datasets) => { - this.myDatasets = datasets; - this.showMyDatasets = true; - }); - } - - selectThisDataset(dataset: Dataset) { - this.selectedDataset = dataset; - this.selectedDatasetLoaded = false; - this.existingDatasetSelected = true; - this.tableData.hasHeader = this.selectedDataset.hasHeader; - - this.tableData.hasInput = true; - this.tableData.loaded = false; - - this.datasets.getDatasetFile(dataset.fileId).subscribe((file: string | undefined) => { - if (file) { - this.tableData.loaded = true; - this.tableData.numRows = this.selectedDataset!.rowCount; - this.tableData.numCols = this.selectedDataset!.columnInfo.length; - this.tableData.data = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter == "") ? "," : dataset.delimiter); - //this.resetCbsAndRbs(); //TREBA DA SE DESI - //this.refreshThreeNullValueRadioOptions(); //TREBA DA SE DESI - this.selectedDatasetLoaded = true; - - this.selectedDatasetChangeEvent.emit(this.selectedDataset); - } - }); - } - - resetSelectedDataset(): boolean { - this.selectedDatasetChangeEvent.emit(this.selectedDataset); - return true; - } - - ngOnInit(): void { - if (this.signalRService.hubConnection) { - this.signalRService.hubConnection.on("NotifyDataset", _ => { - this.refreshMyDatasets(); - }); - } else { - console.warn("Dataset-Load: No connection!"); - } - } -} diff --git a/frontend/src/app/_elements/folder/folder.component.css b/frontend/src/app/_elements/folder/folder.component.css index 62324d62..ada2dba0 100644 --- a/frontend/src/app/_elements/folder/folder.component.css +++ b/frontend/src/app/_elements/folder/folder.component.css @@ -210,4 +210,13 @@ .form-hidden { display: none; +} + +.predictor { + text-decoration: underline; +} + +.highlight-exp { + /*font-size: 16px;*/ + font-weight: 700; }
\ No newline at end of file diff --git a/frontend/src/app/_elements/folder/folder.component.html b/frontend/src/app/_elements/folder/folder.component.html index bff066be..8896e7e5 100644 --- a/frontend/src/app/_elements/folder/folder.component.html +++ b/frontend/src/app/_elements/folder/folder.component.html @@ -67,17 +67,38 @@ <app-form-dataset [ngClass]="{'form-hidden': type != FolderType.Dataset}" [forExperiment]="forExperiment"></app-form-dataset> </div> <div [ngClass]="{'form-hidden' : !listView}" class="list-view"> - <div *ngFor="let file of filteredFiles; let i = index" class="list-item force-link" (click)="selectFile(file)"> - <div class="mx-2"> - {{file.name}} + <div *ngFor="let file of filteredFiles; let i = index"> + <div class="list-item force-link" (click)="selectFile(file)"> + <div class="mx-2" [ngClass]="{'highlight-exp' : selectedTab == TabType.MyExperiments}"> + {{file.name}} + </div> + <div class="mx-2 hover-hide"> + {{file.lastUpdated | date}} + </div> + <div class="mx-2 hover-show" *ngIf="selectedTab !== TabType.PublicDatasets && selectedTab !== TabType.PublicModels"> + <button class="btn-clear file-button" (click)="deleteFile(file, $event)"> + <mat-icon>delete</mat-icon> + </button> + </div> </div> - <div class="mx-2 hover-hide"> - {{file.lastUpdated | date}} - </div> - <div class="mx-2 hover-show" *ngIf="selectedTab !== TabType.PublicDatasets && selectedTab !== TabType.PublicModels"> - <button class="btn-clear file-button" (click)="deleteFile(file, $event)"> - <mat-icon>delete</mat-icon> - </button> + <div *ngIf="type == FolderType.Experiment" class="list-view"> + <div *ngFor="let predictor of predictorsForExp[file._id];" class="list-item"> + + <div class="mx-3"> + <div class="f-row"> + <mat-icon>subdirectory_arrow_right</mat-icon> + <div class="mx-1">{{predictor.name}}</div> + </div> + </div> + <div class="mx-2 hover-hide"> + {{predictor.lastUpdated | date}} + </div> + <div class="mx-2 hover-show"> + <button class="btn-clear file-button" (click)="deleteFile(predictor, $event)"> + <mat-icon>delete</mat-icon> + </button> + </div> + </div> </div> </div> diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts index fabb524c..665659a8 100644 --- a/frontend/src/app/_elements/folder/folder.component.ts +++ b/frontend/src/app/_elements/folder/folder.component.ts @@ -11,6 +11,8 @@ import { ExperimentsService } from 'src/app/_services/experiments.service'; import { PredictorsService } from 'src/app/_services/predictors.service'; import { SignalRService } from 'src/app/_services/signal-r.service'; import { FormModelComponent } from '../form-model/form-model.component'; +import { Router } from '@angular/router'; +import Predictor from 'src/app/_data/Predictor'; @Component({ selector: 'app-folder', @@ -30,7 +32,7 @@ export class FolderComponent implements AfterViewInit { @Input() type: FolderType = FolderType.Dataset; @Input() forExperiment!: Experiment; @Input() startingTab!: TabType; - + @Input() archive: boolean = false; newFileSelected: boolean = true; selectedFileIndex: number = -1; @@ -44,7 +46,7 @@ export class FolderComponent implements AfterViewInit { searchTerm: string = ''; - constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService, private signalRService: SignalRService) { + constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService, private signalRService: SignalRService, private router: Router) { this.tabsToShow.forEach(tab => this.folders[tab] = []); } @@ -91,20 +93,23 @@ export class FolderComponent implements AfterViewInit { this.newFileSelected = true; this.listView = false; this.displayFile(); - if(this.type == FolderType.Dataset) + if (this.type == FolderType.Dataset) this.formDataset.clear(); } selectFile(file?: FolderFile) { this.selectedFile = file; this.fileToDisplay = file; + if (this.type == FolderType.Experiment && file) { + this.router.navigate(['/experiment'/*, file._id*/]) + } this.newFileSelected = false; this.listView = false; this.selectedFileChanged.emit(this.selectedFile); this.selectTab(TabType.File); this.displayFile(); - if(this.type == FolderType.Dataset) + if (this.type == FolderType.Dataset) this.formDataset.loadExisting(); } @@ -122,7 +127,7 @@ export class FolderComponent implements AfterViewInit { _initialized: boolean = false; - refreshFiles(selectedDatasetId: string | null) { + refreshFiles(selectedDatasetId: string | null = null, selectedModelId: string | null = null) { this.files = [] this.filteredFiles.length = 0; this.folders[TabType.NewFile] = []; @@ -131,42 +136,85 @@ export class FolderComponent implements AfterViewInit { this.folders[tab] = []; }); - this.datasetsService.getMyDatasets().subscribe((datasets) => { - this.folders[TabType.MyDatasets] = datasets; - if (selectedDatasetId) { - this.selectFile(datasets.filter(x => x._id == selectedDatasetId)[0]); + if (this.archive) { + this.refreshDatasets(selectedDatasetId); + this.refreshModels(selectedModelId); + this.refreshExperiments(); + } else { + switch (this.type) { + case FolderType.Dataset: + this.refreshDatasets(selectedDatasetId); + break; + + case FolderType.Model: + this.refreshModels(selectedModelId); + break; + + case FolderType.Experiment: + this.refreshExperiments(); + break; + + default: + console.error("Bad folder type."); + break; } - }); - - this.experimentsService.getMyExperiments().subscribe((experiments) => { - this.folders[TabType.MyExperiments] = experiments; - }); + } - this.datasetsService.getPublicDatasets().subscribe((datasets) => { - this.folders[TabType.PublicDatasets] = datasets; - }); + if (!this._initialized) { + this.files = this.folders[this.startingTab]; + this.filteredFiles = []; + this.selectTab(this.startingTab); + this._initialized = true; + } + } + refreshModels(selectedModelId: string | null) { this.modelsService.getMyModels().subscribe((models) => { this.folders[TabType.MyModels] = models; + if (selectedModelId) { + this.selectFile(models.filter(x => x._id == selectedModelId)[0]); + } + this.searchTermsChanged(); }); - /*this.modelsService.getMyModels().subscribe((models) => { this.folders[TabType.PublicModels] = models; + this.searchTermsChanged(); });*/ this.folders[TabType.PublicModels] = []; + } + refreshDatasets(selectedDatasetId: string | null) { + this.datasetsService.getMyDatasets().subscribe((datasets) => { + this.folders[TabType.MyDatasets] = datasets; + if (selectedDatasetId) { + this.selectFile(datasets.filter(x => x._id == selectedDatasetId)[0]); + } + this.searchTermsChanged(); + }); + this.datasetsService.getPublicDatasets().subscribe((datasets) => { + this.folders[TabType.PublicDatasets] = datasets; + this.searchTermsChanged(); + }); + } + + refreshExperiments() { this.experimentsService.getMyExperiments().subscribe((experiments) => { this.folders[TabType.MyExperiments] = experiments; + this.predictorsService.getMyPredictors().subscribe((predictors) => { + this.predictorsForExp = {}; + experiments.forEach(exp => { + this.predictorsForExp[exp._id] = predictors.filter(pred => pred.experimentId == exp._id); + /* TODO IZMENI OVO DA SE SETUJE NA BACKU AUTOMATSKI */ + this.predictorsForExp[exp._id].forEach(pred => { + const model = this.folders[TabType.MyModels].find(model => model._id == pred.modelId); + pred.name = model?.name!; + pred.lastUpdated = model?.lastUpdated!; + }) + /* ------------------------------------------------ */ + this.searchTermsChanged(); + }) + }); }); - - if (!this._initialized) { - this.files = this.folders[this.startingTab]; - this.filteredFiles = []; - this.selectTab(this.startingTab); - this._initialized = true; - } - - this.searchTermsChanged(); } saveNewFile() { @@ -175,7 +223,7 @@ export class FolderComponent implements AfterViewInit { this.formDataset!.uploadDataset((dataset: Dataset) => { this.newFile = undefined; Shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira."); - this.refreshFiles(null); + this.refreshFiles(); }, () => { Shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset."); @@ -185,7 +233,7 @@ export class FolderComponent implements AfterViewInit { this.modelsService.addModel(this.formModel.newModel).subscribe(model => { this.newFile = undefined; Shared.openDialog("Obaveštenje", "Uspešno ste dodali novu konfiguraciju neuronske mreže u kolekciju."); - this.refreshFiles(null); // todo select model + this.refreshFiles(null, model._id); // todo select model }, (err) => { Shared.openDialog("Neuspeo pokušaj!", "Konfiguracija neuronske mreže sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeću konfiguraciju."); }); @@ -193,19 +241,7 @@ export class FolderComponent implements AfterViewInit { } } - - /*calcZIndex(i: number) { - let zIndex = (this.files.length - i - 1) - if (this.selectedFileIndex == i) - zIndex = this.files.length + 2; - if (this.hoveringOverFileIndex == i) - zIndex = this.files.length + 3; - return zIndex; - } - - newFileZIndex() { - return (this.files.length + 1); - }*/ + predictorsForExp: { [expId: string]: Predictor[] } = {} clearSearchTerm() { this.searchTerm = ''; @@ -232,10 +268,6 @@ export class FolderComponent implements AfterViewInit { listView: boolean = true; - toggleListView() { - this.listView = !this.listView; - } - deleteFile(file: FolderFile, event: Event) { event.stopPropagation(); //console.log('delete'); @@ -349,16 +381,22 @@ export class FolderComponent implements AfterViewInit { } hoverOverTab(tab: TabType) { - this.listView = this.getListView(tab); - this.privacy = this.getPrivacy(tab); + // this.listView = this.getListView(tab); + // this.privacy = this.getPrivacy(tab); this.hoverTab = tab; - if (tab == TabType.None) { - this.listView = this.getListView(this.selectedTab); - this.files = this.folders[this.selectedTab]; - } else { - this.files = this.folders[tab]; + // if (tab == TabType.None) { + // this.listView = this.getListView(this.selectedTab); + // this.files = this.folders[this.selectedTab]; + // } else { + // this.files = this.folders[tab]; + // } + // this.searchTermsChanged(); + } + + updateExperiment() { + if (this.formModel) { + this.formModel.updateGraph(); } - this.searchTermsChanged(); } } diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.css b/frontend/src/app/_elements/form-dataset/form-dataset.component.css index 953daa0c..079711d0 100644 --- a/frontend/src/app/_elements/form-dataset/form-dataset.component.css +++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.css @@ -5,25 +5,27 @@ } .topBar { + display: table; width: 100%; - margin: 1rem; - align-items: flex-start; + height: 100px; + margin-left: 1.5%; + margin-top: 1.5% ; +} +.kolona{ + float: left; + margin-left: 20px; } - .topBar label{ font-size: 30px; } .topBar mat-form-field{ width: 250px; + margin: 0; } -.toptop{ - margin-left: 1.5%; - width: 50%; -} - -.fileButton{ - margin-top: 10px; +.fileButton button{ + height: 51px; + width: 250px; } .file-container { diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.html b/frontend/src/app/_elements/form-dataset/form-dataset.component.html index 281f9c05..b96276bd 100644 --- a/frontend/src/app/_elements/form-dataset/form-dataset.component.html +++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.html @@ -1,18 +1,16 @@ <div class="folderBox" *ngIf="dataset"> - <div class="row" style="margin-right: 0;"> + <div class="topBar"> - <div class="row toptop"> - <div class="col-sm mb-3"> + <div class="kolona mb-3"> <div class="fileButton"> <button type="button" mat-raised-button (click)="fileInput.click()">Dodaj izvor podataka</button> </div> </div> - <div class="col-sm"> + <div class="kolona"> <div role="group"> - <div class="row"> <mat-form-field class="example-full-width" appearance="fill"> <mat-label>Naziv</mat-label> <input type="text" matInput value="{{dataset?.name}}" [(ngModel)]="dataset.name"> @@ -22,30 +20,24 @@ Naziv je <strong>obavezan</strong> </mat-error> </mat-form-field> - </div> </div> </div> - <div class="col-sm"> + <div class="kolona"> <mat-form-field appearance="fill"> <mat-label>Delimiter</mat-label> - <mat-select id="delimiterOptions" [(ngModel)]="dataset.delimiter" (change)="update()" value=","> + <mat-select id="delimiterOptions" [(ngModel)]="dataset.delimiter" (selectionChange)="update()" value=","> <mat-option *ngFor="let option of delimiterOptions" [value]="option"> {{ option }} </mat-option> </mat-select> </mat-form-field> </div> - <div class="col-sm"> - - </div> - </div> - <div class="row" *ngIf="firstInput"> - <label class=" mt-5">{{filename}}</label> - - </div> </div> - </div> + + <div class="row" *ngIf="firstInput"> + <label class=" mt-5">{{filename}}</label> + </div> <div class="row" style="margin-right: 0;"> <div class="file-container" [ngClass]="{'dottedClass': !tableData.hasInput}"> diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.ts b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts index 1eed2cdc..94ef9905 100644 --- a/frontend/src/app/_elements/form-dataset/form-dataset.component.ts +++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts @@ -101,7 +101,7 @@ export class FormDatasetComponent { this.tableData.loaded = true; this.tableData.numRows = this.dataset.rowCount; this.tableData.numCols = this.dataset.columnInfo.length; - this.tableData.data = this.csv.csvToArray(file, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter); + this.tableData.data = this.csv.csvToArray(file, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter); } }); diff --git a/frontend/src/app/_elements/form-model/form-model.component.css b/frontend/src/app/_elements/form-model/form-model.component.css index 11b6ef5e..95ace1ef 100644 --- a/frontend/src/app/_elements/form-model/form-model.component.css +++ b/frontend/src/app/_elements/form-model/form-model.component.css @@ -101,4 +101,4 @@ mat-slider { padding-bottom: 15px; font-size: 20px !important; font-weight: 600; -}
\ No newline at end of file +} diff --git a/frontend/src/app/_elements/form-model/form-model.component.html b/frontend/src/app/_elements/form-model/form-model.component.html index 96a5e1b6..09e44a99 100644 --- a/frontend/src/app/_elements/form-model/form-model.component.html +++ b/frontend/src/app/_elements/form-model/form-model.component.html @@ -11,7 +11,12 @@ <div class="ns-col"> <mat-form-field appearance="fill" class="mat-fix"> <mat-label>Tip problema</mat-label> - <mat-select [(ngModel)]="newModel.type"> + <mat-select [(ngModel)]="newModel.type" (selectionChange)="filterLossFunction()" *ngIf="this.hideProblemType" disabled="true"> + <mat-option *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" [value]="option"> + {{ optionName }} + </mat-option> + </mat-select> + <mat-select [(ngModel)]="newModel.type" (selectionChange)="filterLossFunction()" *ngIf="!this.hideProblemType" disabled="false"> <mat-option *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" [value]="option"> {{ optionName }} </mat-option> @@ -36,7 +41,7 @@ <mat-form-field appearance="fill" class="mat-fix"> <mat-label>Funkcija troška</mat-label> <mat-select [(ngModel)]="newModel.lossFunction"> - <mat-option *ngFor="let option of Object.keys(LossFunction); let optionName of Object.values(LossFunction)" [value]="option"> + <mat-option *ngFor="let option of Object.keys(lossFunction); let optionName of Object.values(lossFunction)" [value]="option"> {{ optionName }} </mat-option> </mat-select> @@ -109,7 +114,7 @@ <div class="col-sm-9"> <!-- {{forExperiment._columnsSelected}} --> - <app-graph [model]="newModel" *ngIf="forExperiment._columnsSelected" [inputColumns]="forExperiment.inputColumns"></app-graph> + <app-graph [model]="newModel" *ngIf="forExperiment._columnsSelected" [inputColumns]="getInputColumns()"></app-graph> <app-graph [model]="newModel" *ngIf="!forExperiment._columnsSelected" [inputColumns]="['Nisu odabrane ulazne kolone']"></app-graph> </div> </div> @@ -121,14 +126,15 @@ <div class="ns-col" id="layers-control"> <div>Broj Skrivenih Slojeva</div> - <button class="btn-clear btn-icon bubble" (click)="addLayer()"> - <mat-icon>add</mat-icon> - </button> - <div>{{newModel.hiddenLayers}}</div> <button class="btn-clear btn-icon bubble" (click)="removeLayer()"> - <mat-icon>remove</mat-icon> - </button> + <mat-icon>remove</mat-icon> + </button> + <div>{{newModel.hiddenLayers}}</div> + + <button class="btn-clear btn-icon bubble" (click)="addLayer()"> + <mat-icon>add</mat-icon> + </button> </div> <div class="break-1"></div> <div class="ns-col"> @@ -196,13 +202,13 @@ <div class="d-flex flex-row align-items-center justify-content-center tm"> <div class="col-6" style="font-size: 13px;">Broj čvorova</div> - <button class="btn-clear btn-icon bubble" (click)="addNeuron(i)"> - <mat-icon>add</mat-icon> - </button> - <div class="col-2 text-center">{{newModel.layers[i].neurons}}</div> <button class="btn-clear btn-icon bubble" (click)="removeNeuron(i)"> - <mat-icon>remove</mat-icon> - </button> + <mat-icon>remove</mat-icon> + </button> + <div class="col-2 text-center">{{newModel.layers[i].neurons}}</div> + <button class="btn-clear btn-icon bubble" (click)="addNeuron(i)"> + <mat-icon>add</mat-icon> + </button> </div> <mat-form-field appearance="fill" class="mat-fix"> diff --git a/frontend/src/app/_elements/form-model/form-model.component.ts b/frontend/src/app/_elements/form-model/form-model.component.ts index 71b374b0..e01c2339 100644 --- a/frontend/src/app/_elements/form-model/form-model.component.ts +++ b/frontend/src/app/_elements/form-model/form-model.component.ts @@ -15,8 +15,13 @@ export class FormModelComponent implements AfterViewInit { @ViewChild(GraphComponent) graph!: GraphComponent; @Input() forExperiment!: Experiment; @Output() selectedModelChangeEvent = new EventEmitter<Model>(); + @Input() hideProblemType:boolean; + @Input() forProblemType:ProblemType; testSetDistribution: number = 70; - constructor() { } + constructor() { + this.hideProblemType=false; + this.forProblemType=ProblemType.BinaryClassification; + } ngAfterViewInit(): void { } @@ -80,13 +85,6 @@ export class FormModelComponent implements AfterViewInit { } } - /* - setNeurons() - { - for(let i=0;i<this.newModel.hiddenLayers;i++){ - this.newModel.hiddenLayerNeurons[i]=1; - } - }*/ numSequence(n: number): Array<number> { return Array(n); } @@ -111,9 +109,7 @@ export class FormModelComponent implements AfterViewInit { changeAllActivation() { for (let i = 0; i < this.newModel.layers.length; i++) { this.newModel.layers[i].activationFunction = this.selectedActivation; - } - } changeAllRegularisation() { for (let i = 0; i < this.newModel.layers.length; i++) { @@ -131,8 +127,25 @@ export class FormModelComponent implements AfterViewInit { this.updateGraph(); } } - updateTestSet(event: MatSliderChange) { this.testSetDistribution = event.value!; } + filterLossFunction() { + if(this.newModel.type==ProblemType.Regression){ + this.lossFunction = LossFunctionRegression; + this.newModel.lossFunction=LossFunction.MeanSquaredError; + } + else if(this.newModel.type==ProblemType.BinaryClassification){ + this.lossFunction= LossFunctionBinaryClassification; + this.newModel.lossFunction=LossFunction.BinaryCrossEntropy; + } + else if(this.newModel.type==ProblemType.MultiClassification){ + this.lossFunction = LossFunctionMultiClassification; + this.newModel.lossFunction=LossFunction.SparseCategoricalCrossEntropy; + } + +} +getInputColumns() { + return this.forExperiment.inputColumns.filter(x => x != this.forExperiment.outputColumn); +} } diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.css b/frontend/src/app/_elements/metric-view/metric-view.component.css index f91c1ccc..d27418c3 100644 --- a/frontend/src/app/_elements/metric-view/metric-view.component.css +++ b/frontend/src/app/_elements/metric-view/metric-view.component.css @@ -1,10 +1,23 @@ -#container{ +.row{ + margin: 0 !important; + padding: 0 !important; +} +.container{ width: 100%; height: 90%; border-radius: 5px; - background-color:var(--ns-primary-25); - border:1px solid var(--ns-accent); + border:1px solid var(--ns-primary); + background-color: rgba(0, 65, 101, 0.7); + margin-top: 20px; + margin: 0 !important; + padding: 0 !important; + width: 100%; + height: 100%; +} +app-line-chart{ + width: 100%; + height: 100%; + border-radius: 5px; + border:1px solid var(--ns-primary); + background-color: rgba(0, 65, 101, 0.7); } -#line{ - background-color:#dfd7d7f0 ; -}
\ No newline at end of file diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.html b/frontend/src/app/_elements/metric-view/metric-view.component.html index d72bc92b..2ab0a425 100644 --- a/frontend/src/app/_elements/metric-view/metric-view.component.html +++ b/frontend/src/app/_elements/metric-view/metric-view.component.html @@ -1,8 +1,21 @@ -<div id="container" class="d-flex justify-content-center flex-row w-100"> - <div id="line" style="width: 100%;height: 100%;background-color:var(--ns-bg-dark-100);"> - <app-line-chart></app-line-chart> + <!--<div class="container" style="margin:auto;"> + <div class="row"> + <div class="col-xl"> + <div class="demo-content"><app-line-chart-acc [dataAcc]="myAcc" [dataEpoch]=""[dataValAcc]=""></app-line-chart-acc></div> + </div> + <div class="col-xl"> + <div class="demo-content"><app-line-chart-loss></app-line-chart-loss></div> + </div> + </div> + <div class="row"> + <div class="col-xl"> + <div class="demo-content"><app-line-chart-mae></app-line-chart-mae></div> </div> - <div style="background-color: var(--ns-bg-dark-100);width: 50%;height: 50%;"> - <app-scatterchart></app-scatterchart> + <div class="col-xl"> + <div class="demo-content"><app-line-chart-mse></app-line-chart-mse></div> </div> -</div>
\ No newline at end of file +</div> +</div>--> +<app-line-chart></app-line-chart> + + diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.ts b/frontend/src/app/_elements/metric-view/metric-view.component.ts index 6fd2f320..fbca2edf 100644 --- a/frontend/src/app/_elements/metric-view/metric-view.component.ts +++ b/frontend/src/app/_elements/metric-view/metric-view.component.ts @@ -1,6 +1,5 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { LineChartComponent } from '../_charts/line-chart/line-chart.component'; - @Component({ selector: 'app-metric-view', templateUrl: './metric-view.component.html', @@ -9,6 +8,7 @@ import { LineChartComponent } from '../_charts/line-chart/line-chart.component'; export class MetricViewComponent implements OnInit { @ViewChild(LineChartComponent) linechartComponent!: LineChartComponent; + constructor() { } ngOnInit(): void { @@ -21,6 +21,10 @@ export class MetricViewComponent implements OnInit { const myMae: number[] = []; const myMse: number[] = []; const myLoss: number[] = []; + const myValLoss: number[] = []; + const myValAcc: number[] = []; + const myValMAE: number[] = []; + const myValMSE: number[] = []; const myEpochs: number[] = []; this.history = history; @@ -41,9 +45,21 @@ export class MetricViewComponent implements OnInit { else if (key === 'mse') { myMse.push(parseFloat(value)); } + else if (key === 'val_acc') { + myValAcc.push(parseFloat(value)); + } + else if (key === 'val_loss') { + myValLoss.push(parseFloat(value)); + } + else if (key === 'val_mae') { + myValMAE.push(parseFloat(value)); + } + else if (key === 'val_mse') { + myValMSE.push(parseFloat(value)); + } } }); - this.linechartComponent.update(myEpochs, myAcc, myLoss, myMae, myMse); + this.linechartComponent.update(myEpochs, myAcc, myLoss, myMae, myMse, myValAcc,myValLoss,myValMAE,myValMSE); } }
\ No newline at end of file diff --git a/frontend/src/app/_elements/model-load/model-load.component.html b/frontend/src/app/_elements/model-load/model-load.component.html deleted file mode 100644 index dcb35c21..00000000 --- a/frontend/src/app/_elements/model-load/model-load.component.html +++ /dev/null @@ -1,215 +0,0 @@ -<div> - <div class="d-flex flex-row justify-content-center align-items-center mt-3 mb-5"> - <button type="button" id="btnMyModel" class="btn" (click)="viewMyModelsForm()" - [ngClass]="{'btnType1': showMyModels, 'btnType2': !showMyModels}"> - Izaberite model iz kolekcije - </button> - <h3 class="mt-3 mx-3">ili</h3> - <button type="button" id="btnNewModel" class="btn" (click)="viewNewModelForm()" - [ngClass]="{'btnType1': !showMyModels, 'btnType2': showMyModels}"> - Dodajte novi model - </button> - </div> - - <div *ngIf="showMyModels" class="px-5 my-3"> - <input *ngIf="showMyModels" type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term"> - </div> - <div *ngIf="showMyModels" class="px-5"> - <div class="overflow-auto" style="max-height: 500px;"> - <ul class="list-group"> - <li class="list-group-item p-3" *ngFor="let model of myModels|filter:term|filter:(forExperiment ? forExperiment.type : '')" - [ngClass]="{'selectedModelClass': this.selectedModel == model}"> - <app-item-model name="usersModel" [model]="model" (click)="selectThisModel(model);"> - </app-item-model> - </li> - </ul> - <div class="px-5 mt-5"> - <!--prikaz izabranog modela--> - </div> - </div> - </div> - - - <div *ngIf="!showMyModels"> - <div class="form-group row mt-3 mb-2 d-flex justify-content-center"> - - <div class="col-3"> - <label for="name" class="col-form-label">Naziv modela:</label> - <input type="text" class="form-control" name="name" placeholder="Naziv..." [(ngModel)]="newModel.name"> - </div> - <div class="col-5"> - <label for="desc" class="col-sm-2 col-form-label">Opis:</label> - <div> - <textarea class="form-control" name="desc" rows="3" [(ngModel)]="newModel.description"></textarea> - </div> - </div> - - </div> - <h2 class="mt-5 mb-4 mx-5">Parametri treniranja modela:</h2> - <div> - - <div class="row p-2"> - <div class="col-1"></div> - <div class="col-3"> - <label for="type" class="col-form-label">Tip problema: </label> - </div> - <div class="col-2"> - <select id=typeOptions class="form-select" name="type" [(ngModel)]="newModel.type" - (change)="filterOptions()"> - <option - *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <div class="col-1"></div> - <div class="col-3"> - <label for="hiddenLayers" class="col-form-label">Broj skrivenih slojeva: </label> - </div> - <div class="col-1"> - <input type="number" min="1" class="form-control" name="hiddenLayers" - [(ngModel)]="newModel.hiddenLayers" - (change)="newModel.hiddenLayerActivationFunctions = [].constructor(newModel.hiddenLayers).fill(newModel.hiddenLayerActivationFunctions[0])" - (ngModelChange)="updateGraph()"> - </div> - </div> - - <div class="row p-2"> - <div class="col-1"> - </div> - <div class="col-3"> - <label for="optimizer" class="col-form-label">Optimizacija: </label> - </div> - <div class="col-2"> - <select id=optimizerOptions class="form-select" name="optimizer" [(ngModel)]="newModel.optimizer"> - <option - *ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <div class="col-1"> - </div> - <div class="col-3"> - <label for="hiddenLayerNeurons" class="col-form-label">Broj neurona skrivenih slojeva: </label> - </div> - <div class="col-1"> - <input type="number" min="1" class="form-control" name="hiddenLayerNeurons" - [(ngModel)]="newModel.hiddenLayerNeurons" (ngModelChange)="updateGraph()"> - </div> - </div> - - <div class="row p-2"> - <div class="col-1"></div> - <div class="col-3"> - <label for="lossFunction" class="col-form-label">Funkcija troška: </label> - </div> - <div class="col-2"> - <select id=lossFunctionOptions class="form-select" name="lossFunction" - [(ngModel)]="newModel.lossFunction" aria-checked="true"> - <option - *ngFor="let option of Object.keys(lossFunction); let optionName of Object.values(lossFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <div class="col-1"></div> - <div class="col-3"> - <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label> - </div> - <div class="col-1"> - - <input type="number" min="0" step="1" max="7" class="form-control" name="batchSizePower" [(ngModel)]="batchSizePower" (click)="updateBatchSize()" > - {{newModel.batchSize}} - - </div> - - <div class="row p-2"> - <div class="col-1"></div> - <div class="col-3 m-1"> - <label for="epochs" class="col-form-label">Broj epoha: </label> - </div> - <div class="col-1"> - <input type="number" min="1" max="1000" class="form-control" name="epochs" - [(ngModel)]="newModel.epochs"> - </div> - </div> - </div> - - <div class="m-5"> - <app-graph [model]="newModel" [inputCols]="1"></app-graph> - </div> - - <h3 class="mx-5 mt-4">Aktivacione funkcije:</h3> - - <div class="row p-2" style="align-self: center;"> - <div class="col-1"></div> - <div class="col-3"> - <label for="hiddenLayerActivationFunction" class="col-form-label" - style="text-align: center;">Funkcija aktivacije<br>skrivenih slojeva:</label> - </div> - <div class="col-2 mt-2"> - <div *ngFor="let item of [].constructor(newModel.hiddenLayers); let i = index"> - <div class="input-group mb-2"> - <div class="input-group-prepend"> - <span class="input-group-text">#{{i+1}}</span> - </div> - <select [id]="'hiddenLayerActivationFunctionOption_'+i" class="form-select" - [(ngModel)]="newModel.hiddenLayerActivationFunctions[i]" > - <option - *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - </div> - </div> - <div class="col-1"></div> - <div class="col-2"> - <label for="outputLayerActivationFunction" class="col-form-label" - style="text-align: center;">Funkcija aktivacije<br>izlaznog sloja:</label> - </div> - <div class="col-2 mt-2"> - <select id=outputLayerActivationFunctionOptions class="form-select" - name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction"> - <option - *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <div class="col"> - </div> - </div> - </div> - - <div class="form-check form-check-inline overflow-auto m-4" style="width: max-content;"> - <h3>Izaberite metrike:</h3> - <div id="divMetricsinput" class="mt-2 mx-5"> - - <div *ngFor="let option of Object.keys(metrics); let optionName of Object.values(metrics) " - class="form-check form-check-inline"> - - <input name="cbmetrics" class="form-check-input" type="checkbox" value="{{option}}" - id="metrics_{{option}}" style="float: left;" checked> - <label class="form-check-label" for="metrics_{{option}}" for="inlineCheckbox2"> - {{optionName}} - </label> - </div> - </div> - </div> - - <div class="form-group row mt-3 mb-3"> - <div class="col"></div> - <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" - (click)="uploadModel();">Sačuvaj - model</button> - <div class="col"></div> - </div> - </div> -</div>
\ No newline at end of file diff --git a/frontend/src/app/_elements/model-load/model-load.component.ts b/frontend/src/app/_elements/model-load/model-load.component.ts deleted file mode 100644 index dbca3d17..00000000 --- a/frontend/src/app/_elements/model-load/model-load.component.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from '@angular/core'; -import Shared from 'src/app/Shared'; -import Experiment from 'src/app/_data/Experiment'; -import Model, { ActivationFunction, LossFunction, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType } from 'src/app/_data/Model'; -import { ModelsService } from 'src/app/_services/models.service'; -import { GraphComponent } from '../graph/graph.component'; - - -@Component({ - selector: 'app-model-load', - templateUrl: './model-load.component.html', - styleUrls: ['./model-load.component.css'] -}) -export class ModelLoadComponent implements OnInit { - - @ViewChild(GraphComponent) graph!: GraphComponent; - @Input() forExperiment?:Experiment; - @Output() selectedModelChangeEvent = new EventEmitter<Model>(); - - newModel: Model = new Model(); - myModels?: Model[]; - selectedModel?: Model; - - ProblemType = ProblemType; - ActivationFunction = ActivationFunction; - metrics: any = Metrics; - LossFunction = LossFunction; - Optimizer = Optimizer; - Object = Object; - document = document; - shared = Shared; - - term: string = ""; - selectedProblemType: string = ''; - selectedMetrics = []; - lossFunction: any = LossFunction; - - showMyModels: boolean = true; - - constructor(private modelsService: ModelsService) { - this.modelsService.getMyModels().subscribe((models) => { - this.myModels = models; - }); - } - - ngOnInit(): void { - } - batchSizePower:number=1; - updateBatchSize() - { - this.newModel.batchSize=2**this.batchSizePower; - } - - updateGraph() { - this.graph.update(); - } - - getMetrics() { - this.newModel.metrics = []; - let cb = document.getElementsByName("cbmetrics"); - - for (let i = 0; i < cb.length; i++) { - let chb = <HTMLInputElement>cb[i]; - if (chb.checked == true) - this.newModel.metrics.push(chb.value); - } - } - - uploadModel() { - this.getMetrics(); - - this.newModel.uploaderId = Shared.userId; - - this.modelsService.addModel(this.newModel).subscribe((response) => { - Shared.openDialog('Model dodat', 'Model je uspešno dodat u bazu.'); - // treba da se selektuje nov model u listi modela - //this.selectedModel = - }, (error) => { - Shared.openDialog('Greška', 'Model sa unetim nazivom već postoji u Vašoj kolekciji. Promenite naziv modela i nastavite sa kreiranim datasetom.'); - }); - } - - filterOptions() { - switch (this.newModel.type) { - case 'regresioni': - this.lossFunction = LossFunctionRegression; - this.metrics = MetricsRegression; - break; - case 'binarni-klasifikacioni': - this.lossFunction = LossFunctionBinaryClassification; - this.metrics = MetricsBinaryClassification; - break; - case 'multi-klasifikacioni': - this.lossFunction = LossFunctionMultiClassification; - this.metrics = MetricsMultiClassification; - break; - default: - break; - } - } - - viewMyModelsForm() { - this.showMyModels = true; - } - viewNewModelForm() { - this.showMyModels = false; - } - - selectThisModel(model: Model) { - this.selectedModel = model; - this.selectedModelChangeEvent.emit(this.selectedModel); - } - -} diff --git a/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.css b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.css index e69de29b..e99a1e1e 100644 --- a/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.css +++ b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.css @@ -0,0 +1,8 @@ +#btnYes { + background-color: var(--offwhite); + color: var(--ns-bg-dark-100); +} + +#btnNo { + color: gray; +}
\ No newline at end of file diff --git a/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.html b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.html index 7ba286cb..08c1f26b 100644 --- a/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.html +++ b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.html @@ -1,16 +1,17 @@ -<h1 mat-dialog-title>Enkodiranje svih kolona</h1> -<div mat-dialog-content> - <p>Odaberite tip enkodinga za sve kolone zajedno:</p> - <mat-form-field> - <mat-select matNativeControl [(value)]="selectedEncodingType"> - <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> - <p>Da li ste sigurni u izbor?</p> +<h2 mat-dialog-title class="text-center">Enkodiranje svih kolona</h2> +<div mat-dialog-content class="mt-5 mb-4"> + <p>Sve izabrane kolone biće enkodirane metodom:</p> + <form (keydown)="withEnterKey($event)"> + <mat-form-field> + <mat-select matNativeControl [(value)]="selectedEncodingType" cdkFocusInitial> + <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> + </form> </div> -<div mat-dialog-actions> - <button mat-button [mat-dialog-close]="selectedEncodingType" cdkFocusInitial>Da</button> - <button mat-button (click)="onNoClick()">Odustani</button> +<div mat-dialog-actions class="justify-content-center"> + <button id="btnYes" mat-stroked-button color="basic" (click)="onYesClick()">Potvrdi</button> + <button id="btnNo" mat-stroked-button (click)="onNoClick()">Odustani</button> </div>
\ No newline at end of file diff --git a/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.ts b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.ts index 3b7560bf..9c45d7b6 100644 --- a/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.ts +++ b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.ts @@ -1,7 +1,10 @@ import { Component, OnInit } from '@angular/core'; -import { MatDialogRef } from '@angular/material/dialog'; -import { Encoding } from 'src/app/_data/Experiment'; - +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { ColumnType, Encoding } from 'src/app/_data/Experiment'; +import Experiment from 'src/app/_data/Experiment'; +import { ExperimentsService } from 'src/app/_services/experiments.service'; +import { Inject} from '@angular/core'; +import Dataset from 'src/app/_data/Dataset'; @Component({ selector: 'app-encoding-dialog', @@ -13,6 +16,7 @@ export class EncodingDialogComponent implements OnInit { selectedEncodingType?: Encoding; Encoding = Encoding; Object = Object; + categoricalColumnExists: boolean = true; constructor(public dialogRef: MatDialogRef<EncodingDialogComponent>) { @@ -20,9 +24,19 @@ export class EncodingDialogComponent implements OnInit { } ngOnInit(): void { + } onNoClick() { this.dialogRef.close(); } -} + + withEnterKey(keyboardEvent: KeyboardEvent) { + if (keyboardEvent.code == "Enter" || keyboardEvent.code == "NumpadEnter") + this.onYesClick(); + } + + onYesClick() { + this.dialogRef.close(this.selectedEncodingType); + } +} diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.html b/frontend/src/app/_modals/login-modal/login-modal.component.html index cea6bf39..79e11db8 100644 --- a/frontend/src/app/_modals/login-modal/login-modal.component.html +++ b/frontend/src/app/_modals/login-modal/login-modal.component.html @@ -6,12 +6,12 @@ <mat-icon>close</mat-icon> </button> <h1 class="login-heading mt-5 mb-5">Prijava</h1> - <form> + <form (keydown)="doLoginWithEnterKey($event)"> <!-- Korisnicko ime --> <div class="mb-3"> <mat-form-field appearance="fill"> <mat-label>Korisničko ime</mat-label> - <input type="text" matInput [(ngModel)]="username" name="username" id="username"> + <input type="text" matInput [(ngModel)]="username" name="username" id="username" #usernameInput autofocus="true"> <mat-icon matSuffix></mat-icon> </mat-form-field> </div> diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.ts b/frontend/src/app/_modals/login-modal/login-modal.component.ts index ccd78509..33c17c31 100644 --- a/frontend/src/app/_modals/login-modal/login-modal.component.ts +++ b/frontend/src/app/_modals/login-modal/login-modal.component.ts @@ -5,15 +5,17 @@ import { AuthService } from 'src/app/_services/auth.service'; import { UserInfoService } from 'src/app/_services/user-info.service'; import shared from '../../Shared'; import {AfterViewInit, ElementRef} from '@angular/core'; +import { MatSelect } from '@angular/material/select'; @Component({ selector: 'app-login-modal', templateUrl: './login-modal.component.html', styleUrls: ['./login-modal.component.css'] }) -export class LoginModalComponent implements OnInit { +export class LoginModalComponent implements AfterViewInit { @ViewChild('closeButton') closeButton?: ElementRef; + @ViewChild('usernameInput') usernameInput!: ElementRef; @ViewChild('pass') passwordInput!: ElementRef; username: string = ''; @@ -30,7 +32,14 @@ export class LoginModalComponent implements OnInit { private userInfoService: UserInfoService ) { } - ngOnInit(): void { + ngAfterViewInit(): void { + //console.log(this.usernameInput); + this.usernameInput.nativeElement.focus(); + } + + doLoginWithEnterKey(keyboardEvent: KeyboardEvent) { + if (keyboardEvent.code == "Enter" || keyboardEvent.code == "NumpadEnter") + this.doLogin(); } doLogin() { diff --git a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.css b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.css index e69de29b..e99a1e1e 100644 --- a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.css +++ b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.css @@ -0,0 +1,8 @@ +#btnYes { + background-color: var(--offwhite); + color: var(--ns-bg-dark-100); +} + +#btnNo { + color: gray; +}
\ No newline at end of file diff --git a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.html b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.html index 81aec5f8..7ab92d02 100644 --- a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.html +++ b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.html @@ -1,13 +1,13 @@ -<h1 mat-dialog-title>Popunjavanje nedostajućih vrednosti</h1> -<div mat-dialog-content> - <p>Želim da:</p> - <mat-radio-group [(ngModel)]="selectedMissingValuesOption"> - <mat-radio-button [value]="NullValueOptions.DeleteColumns" checked>obrišem sve kolone koje sadrže nedostajuće vrednosti</mat-radio-button> - <mat-radio-button [value]="NullValueOptions.DeleteRows">obrišem sve redove koji sadrže nedostajuće vrednosti</mat-radio-button> - </mat-radio-group> - <p>Da li ste sigurni u izbor?</p> +<h2 mat-dialog-title class="text-center">Nedostajuće vrednosti</h2> +<div mat-dialog-content class="mt-5 mb-4"> + <form (keydown)="withEnterKey($event)"> + <mat-radio-group [(ngModel)]="selectedMissingValuesOption" [ngModelOptions]="{standalone: true}"> + <mat-radio-button [value]="NullValueOptions.DeleteColumns" checked>Obriši sve kolone koje sadrže nedostajuće vrednosti</mat-radio-button> + <mat-radio-button [value]="NullValueOptions.DeleteRows">Obriši sve redove koji sadrže nedostajuće vrednosti</mat-radio-button> + </mat-radio-group> + </form> </div> -<div mat-dialog-actions> - <button mat-button [mat-dialog-close]="selectedMissingValuesOption" cdkFocusInitial>Da</button> - <button mat-button (click)="onNoClick()">Odustani</button> +<div mat-dialog-actions class="justify-content-center"> + <button id="btnYes" mat-stroked-button color="basic" (click)="onYesClick()">Potvrdi</button> + <button id="btnNo" mat-stroked-button (click)="onNoClick()">Odustani</button> </div>
\ No newline at end of file diff --git a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.ts b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.ts index 908edd9e..822d4e4a 100644 --- a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.ts +++ b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.ts @@ -25,4 +25,12 @@ export class MissingvaluesDialogComponent implements OnInit { this.dialogRef.close(); } + withEnterKey(keyboardEvent: KeyboardEvent) { + if (keyboardEvent.code == "Enter" || keyboardEvent.code == "NumpadEnter") + this.onYesClick(); + } + onYesClick() { + this.dialogRef.close(this.selectedMissingValuesOption); + } + } diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.html b/frontend/src/app/_modals/register-modal/register-modal.component.html index d76af4d6..0c791a61 100644 --- a/frontend/src/app/_modals/register-modal/register-modal.component.html +++ b/frontend/src/app/_modals/register-modal/register-modal.component.html @@ -6,7 +6,7 @@ <mat-icon>close</mat-icon> </button> <h1 class="mt-5 mb-4">Registracija</h1> - <form class="mx-4"> + <form class="mx-4" (keydown)="doRegisterWithEnterKey($event)"> <!--Ime--> <div> <mat-form-field appearance="fill"> diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.ts b/frontend/src/app/_modals/register-modal/register-modal.component.ts index 9da5484d..a5c6ddc6 100644 --- a/frontend/src/app/_modals/register-modal/register-modal.component.ts +++ b/frontend/src/app/_modals/register-modal/register-modal.component.ts @@ -49,6 +49,11 @@ export class RegisterModalComponent implements OnInit { ngOnInit(): void { } + doRegisterWithEnterKey(keyboardEvent: KeyboardEvent) { + if (keyboardEvent.code == "Enter" || keyboardEvent.code == "NumpadEnter") + this.doRegister(); + } + doRegister() { this.validation(); } @@ -150,7 +155,9 @@ export class RegisterModalComponent implements OnInit { username: this.username, password: this.pass1, email: this.email, - photoId: "1" + photoId: "1", + isPermament:true, + dateCreated:new Date() } this.authService.register(user) diff --git a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.css b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.css index e69de29b..8225af36 100644 --- a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.css +++ b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.css @@ -0,0 +1,13 @@ +#btnYes { + background-color: var(--offwhite); + color: var(--ns-bg-dark-100); +} + +#btnNo { + color: gray; +} + +.wrongInput { + color: var(--ns-warn); + font-size: 11px; +}
\ No newline at end of file diff --git a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.html b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.html index bac73e0a..2b3678ce 100644 --- a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.html +++ b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.html @@ -1,13 +1,15 @@ -<h1 mat-dialog-title>Čuvanje eksperimenta</h1> -<div mat-dialog-content> - <span>Unesi naziv eksperimenta:</span> - <mat-form-field> - <input type="text" matInput [(ngModel)]="selectedName"> - </mat-form-field> - <br><br> - <p>Sačuvaj eksperiment:</p> +<h1 mat-dialog-title class="text-center">Sačuvaj eksperiment</h1> +<div mat-dialog-content class="mt-5 mb-4 mx-1"> + <form (keydown)="saveWithEnterKey($event)"> + Naziv eksperimenta:<br> + <mat-form-field [style.width.px]=250> + <input type="text" matInput [(ngModel)]="selectedName" cdkFocusInitial [ngModelOptions]="{standalone: true}"> + </mat-form-field> + <p *ngIf="wrongAlreadyExists" class="wrongInput">Izaberi drugi naziv za eskperiment.<br>Eskperiment sa unetim nazivom već postoji u kolekciji.</p> + <p *ngIf="wrongEmptyName" class="wrongInput">Unesite naziv eksperimenta.</p> + </form> </div> -<div mat-dialog-actions> - <button mat-button [mat-dialog-close]="selectedName" cdkFocusInitial>Da</button> - <button mat-button (click)="onNoClick()">Odustani</button> +<div mat-dialog-actions class="justify-content-center"> + <button id="btnYes" mat-stroked-button color="basic" (click)="onYesClick()">Sačuvaj</button> + <button id="btnNo" mat-stroked-button (click)="onNoClick()">Odustani</button> </div>
\ No newline at end of file diff --git a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.ts b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.ts index ca01f57e..3c19e732 100644 --- a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.ts +++ b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.ts @@ -1,5 +1,12 @@ import { Component, OnInit } from '@angular/core'; -import { MatDialogRef } from '@angular/material/dialog'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import Experiment from 'src/app/_data/Experiment'; +import { ExperimentsService } from 'src/app/_services/experiments.service'; +import { Inject} from '@angular/core'; + +interface DialogData { + experiment: Experiment; +} @Component({ selector: 'app-save-experiment-dialog', @@ -9,8 +16,13 @@ import { MatDialogRef } from '@angular/material/dialog'; export class SaveExperimentDialogComponent implements OnInit { selectedName: string = ''; + wrongAlreadyExists: boolean = false; + wrongEmptyName: boolean = false; - constructor(public dialogRef: MatDialogRef<SaveExperimentDialogComponent>) { } + constructor(public dialogRef: MatDialogRef<SaveExperimentDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: DialogData, private experimentService: ExperimentsService) { + this.wrongAlreadyExists = false; + this.wrongEmptyName = false; + } ngOnInit(): void { } @@ -18,4 +30,27 @@ export class SaveExperimentDialogComponent implements OnInit { onNoClick() { this.dialogRef.close(); } + + saveWithEnterKey(keyboardEvent: KeyboardEvent) { + if (keyboardEvent.code == "Enter" || keyboardEvent.code == "NumpadEnter") + this.onYesClick(); + } + onYesClick() { + if (this.selectedName == "") { + this.wrongEmptyName = true; + return; + } + this.wrongEmptyName = false; + + this.data.experiment.name = this.selectedName; + this.experimentService.addExperiment(this.data.experiment).subscribe((response) => { + this.wrongAlreadyExists = false; + this.data.experiment = response; + this.dialogRef.close(this.data.experiment); + }, (error) => { + if (error.error == "Experiment with this name exists") { + this.wrongAlreadyExists = true; + } + }); + } } diff --git a/frontend/src/app/_pages/archive/archive.component.html b/frontend/src/app/_pages/archive/archive.component.html index fc3c4763..f9cce56b 100644 --- a/frontend/src/app/_pages/archive/archive.component.html +++ b/frontend/src/app/_pages/archive/archive.component.html @@ -1,5 +1,5 @@ <div class="d-flex flex-column align-items-center my-5"> - <app-folder></app-folder> + <app-folder [archive]="true" [startingTab]="TabType.MyExperiments"></app-folder> <!--<div class="my-5" style="height: fit-content;"> <app-playlist [datasets]="publicDatasets"></app-playlist> diff --git a/frontend/src/app/_pages/archive/archive.component.ts b/frontend/src/app/_pages/archive/archive.component.ts index 47f96218..bb4bd9e9 100644 --- a/frontend/src/app/_pages/archive/archive.component.ts +++ b/frontend/src/app/_pages/archive/archive.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import Dataset from 'src/app/_data/Dataset'; +import { TabType } from 'src/app/_elements/folder/folder.component'; import { DatasetsService } from 'src/app/_services/datasets.service'; @Component({ @@ -10,6 +11,7 @@ import { DatasetsService } from 'src/app/_services/datasets.service'; export class ArchiveComponent implements OnInit { publicDatasets: Dataset[] = []; + TabType = TabType; constructor(private datasetsService: DatasetsService) { } diff --git a/frontend/src/app/_pages/experiment/experiment.component.css b/frontend/src/app/_pages/experiment/experiment.component.css index 36c35484..4c063f87 100644 --- a/frontend/src/app/_pages/experiment/experiment.component.css +++ b/frontend/src/app/_pages/experiment/experiment.component.css @@ -52,4 +52,16 @@ mat-stepper { width: 98%; height: 98%; overflow-y: auto; -}
\ No newline at end of file +} +.ekspName{ + font-weight: bold; + font-size: large; +} + + +.addedElement{ + color:var(--ns-accent); +} +.text-overflow { + overflow-wrap: break-word; +} diff --git a/frontend/src/app/_pages/experiment/experiment.component.html b/frontend/src/app/_pages/experiment/experiment.component.html index 2b32db81..a2ede838 100644 --- a/frontend/src/app/_pages/experiment/experiment.component.html +++ b/frontend/src/app/_pages/experiment/experiment.component.html @@ -1,28 +1,48 @@ <div class="container-fluid p-0 text-offwhite holder" style="height: calc(100vh - 64px); text-align: center;"> <div class="d-flex flex-colum align-items-center sidenav"> + <div> + <div class="ekspName " style="width: 250px;"> + <div class="text-overflow" *ngIf="experiment.name=='exp1'" style="width: 250px;"> + Novi Eksperiment + </div> + <div class="text-overflow" *ngIf="experiment.name!='exp1'" style="width: 250px;"> + {{experiment.name}} + </div> + + </div> <mat-stepper orientation="vertical" (selectionChange)="changePage($event)"> <mat-step> <!--editable="false"--> - <ng-template matStepLabel><span class="label">Izvor podataka</span></ng-template> + <ng-template matStepLabel> + <span class="label" *ngIf="dataset==undefined">Izvor podataka</span> + <span class="label addedElement" *ngIf="dataset!=undefined">{{dataset.name}}</span> + </ng-template> <ng-template matStepContent> - <p>Izaberite vas izvor podataka</p> + <p class="text-left">Izaberite vas izvor podataka</p> </ng-template> </mat-step> <mat-step> - <ng-template matStepLabel> <span class="label">Odabir kolona</span></ng-template> + <ng-template matStepLabel> + <span class="label addedElement" *ngIf="experiment.name!='exp1'">Predvideti:{{experiment.outputColumn}}</span> + <span class="label" *ngIf="experiment.name=='exp1'">Odabir kolona</span> + </ng-template> <ng-template matStepContent> - <p>Pripremite podatke i izaberite izlazne kolone</p> + <p class="text-left">Pripremite podatke i izaberite izlazne kolone</p> </ng-template> </mat-step> <mat-step> - <ng-template matStepLabel><span class="label">Treniranje</span></ng-template> - <p>Odaberite parametre i trenirajte model</p> + <ng-template matStepLabel> + <span class="label addedElement" *ngIf="modelToTrain!=undefined">{{modelToTrain.name}}</span> + <span class="label" *ngIf="modelToTrain==undefined">Treniranje</span> + </ng-template> + <p class="text-left">Odaberite parametre i trenirajte model</p> </mat-step> <mat-step> <ng-template matStepLabel><span class="label">Pregled rezultata treniranja</span></ng-template> - <p>Pregledajte tok treniranja i grafički prikaz rezultata</p> + <p class="text-left">Pregledajte tok treniranja i grafički prikaz rezultata</p> </mat-step> </mat-stepper> + </div> </div> <div #stepsContainer class="steps-container"> <div #steps id="step_1" class="step-content"> @@ -32,7 +52,7 @@ </div> <div #steps id="step_2" class="step-content"> <div class="step-content-inside"> - <app-column-table (okPressed)="goToPage(2); experiment._columnsSelected = true;" (columnTableChanged)="columnTableChangedEvent()" [experiment]="experiment" [dataset]="dataset"></app-column-table> + <app-column-table (okPressed)="goToPage(2); experiment._columnsSelected = true;" (columnTableChanged)="columnTableChangedEvent()" (experimentChanged)="experimentChangedEvent()" [experiment]="experiment" [dataset]="dataset"></app-column-table> </div> </div> <div #steps id="step_3" class="step-content"> @@ -46,4 +66,4 @@ </div> </div> </div> -</div>
\ No newline at end of file +</div> diff --git a/frontend/src/app/_pages/experiment/experiment.component.ts b/frontend/src/app/_pages/experiment/experiment.component.ts index 1dc18a78..abf4b697 100644 --- a/frontend/src/app/_pages/experiment/experiment.component.ts +++ b/frontend/src/app/_pages/experiment/experiment.component.ts @@ -147,12 +147,17 @@ export class ExperimentComponent implements AfterViewInit { //console.log("promenio se column-table"); } + experimentChangedEvent() { + this.folderModel.updateExperiment(); + } + setDataset(dataset: FolderFile) { const d = <Dataset>dataset; this.experiment.datasetId = d._id; this.dataset = d; this.columnTable.loadDataset(this.dataset); + } modelToTrain?: Model; diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts deleted file mode 100644 index 66b3755e..00000000 --- a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { DatasetsService } from 'src/app/_services/datasets.service'; -import Dataset from 'src/app/_data/Dataset'; -import {Router} from '@angular/router' -import { JwtHelperService } from '@auth0/angular-jwt'; -import { CookieService } from 'ngx-cookie-service'; -import shared from 'src/app/Shared'; - -@Component({ - selector: 'app-filter-datasets', - templateUrl: './filter-datasets.component.html', - styleUrls: ['./filter-datasets.component.css'] -}) -export class FilterDatasetsComponent implements OnInit { - - shared = shared; - publicDatasets?: Dataset[]; - term: string = ""; - constructor(private datasets: DatasetsService,private router:Router, private cookie: CookieService) { - this.datasets.getPublicDatasets().subscribe((datasets) => { - this.publicDatasets = datasets; - }); - } - - ngOnInit(): void { - - } - addDataset(dataset: Dataset):void{ - //this.router.navigateByUrl('/predict?id='+id); - const helper = new JwtHelperService(); - const decodedToken = helper.decodeToken(this.cookie.get("token")); - const newDataset={...dataset}; - newDataset._id = ""; - newDataset.isPublic = false; - newDataset.lastUpdated = new Date(); - newDataset.uploaderId = decodedToken.uploaderId; - let name=prompt("Unesite naziv dataset-a",newDataset.name); - newDataset.name=name as string; - if(name!=null && name!="") - this.datasets.addDataset(newDataset).subscribe((response:string)=>{ - shared.openDialog("Obaveštenje", "Uspešno ste dodali dataset sa nazivom " + newDataset.name); - },(error)=>{ - shared.openDialog("Obaveštenje", "U svojoj kolekciji već imate dataset sa ovim imenom. Molimo Vas da unesete drugo ime."); - }); - - }; - -} diff --git a/frontend/src/app/_pages/profile/profile.component.css b/frontend/src/app/_pages/profile/profile.component.css index 428870da..bbd4c9ba 100644 --- a/frontend/src/app/_pages/profile/profile.component.css +++ b/frontend/src/app/_pages/profile/profile.component.css @@ -1,21 +1,25 @@ -.card{ +.card { background-color: transparent; - color:var(--offwhite) + color: var(--offwhite) } -.card-header{ +.card-header { background-color: var(--ns-primary-50); - color:var(--offwhite) + color: var(--offwhite) } -.card-body{ + +.card-body { background-color: var(--ns-bg-dark-50); } -mat-form-field{ +mat-form-field { width: 100%; } -.danger-Text{ - color:var(--ns-warn) +.danger-Text { + color: var(--ns-warn) } +.selectedPicture { + background-color: var(--ns-accent); +}
\ No newline at end of file diff --git a/frontend/src/app/_pages/profile/profile.component.html b/frontend/src/app/_pages/profile/profile.component.html index 37df4f14..8d655513 100644 --- a/frontend/src/app/_pages/profile/profile.component.html +++ b/frontend/src/app/_pages/profile/profile.component.html @@ -12,7 +12,7 @@ <img class="img-account-profile rounded-circle mb-2" src="{{this.photoPath}}" alt="profilePicture"> <!-- User's info --> <span>@ {{this.user.username}}</span> - <span class="mt-3" style="font-weight: bold;">{{this.user.firstName}} {{this.user.lastName}}</span> + <span class="mt-3" style="font-weight: bold;">{{this.user.firstName}} {{this.user.lastName}}</span> </div> </div> </div> @@ -31,7 +31,7 @@ <mat-form-field appearance="fill"> <mat-label>Važeća lozinka</mat-label> <input matInput id="inputPassword" name="inputPassword" type="password" placeholder="" [(ngModel)]="this.oldPass"> - </mat-form-field> + </mat-form-field> <small *ngIf="wrongOldPassBool" class="form-text danger-Text">Pogrešan format.</small> </div> <!-- Form Group (new password)--> @@ -39,12 +39,12 @@ <mat-form-field appearance="fill"> <mat-label>Nova lozinka</mat-label> <input matInput id="inputNewPassword" name="inputNewPassword" type="password" placeholder="" [(ngModel)]="this.newPass1"> - </mat-form-field> + </mat-form-field> <small *ngIf="wrongNewPassBool" class="form-text danger-Text">Lozinke se ne podudaraju.</small> <small *ngIf="wrongNewPass1Bool" class="form-text danger-Text">Pogrešan format.</small> </div> </div> - + <!-- Form Row--> <div class="row gx-3 mb-3"> <div class="col-md-6"> @@ -118,24 +118,23 @@ <div> <label class="small mt-2 mb-3">Kliknite na sliku kako biste je odabrali za profilnu:</label> - + <div class="container"> <div class="card-group"> <!--bootstrap card with 3 horizontal images--> <div class="row overflow-auto" style="max-height: 200px;"> - <div class="card col-md-3" *ngFor="let picture of this.pictures" (click)="this.photoId = picture.photoId.toString()" - [ngClass]="{'selectedPicture': this.photoId == picture.photoId.toString()}"> + <div class="card col-md-3" *ngFor="let picture of this.pictures" (click)="this.photoId = picture.photoId.toString()" [ngClass]="{'selectedPicture': this.photoId == picture.photoId.toString()}"> <img src="{{picture.path}}"> </div> </div> </div> </div> </div> - + <div class="row mt-5"> <div class="col text-center"> <!-- Save changes button--> - <button mat-raised-button color="primary" (click)="saveInfoChanges()" >Sačuvaj izmene</button> + <button mat-raised-button color="primary" (click)="saveInfoChanges()">Sačuvaj izmene</button> </div> </div> </form> @@ -147,7 +146,7 @@ <div class="row"> <div class="col-xl-4"> - + </div> </div> diff --git a/frontend/src/app/_pages/profile/profile.component.ts b/frontend/src/app/_pages/profile/profile.component.ts index ae9fb12c..fdcd347c 100644 --- a/frontend/src/app/_pages/profile/profile.component.ts +++ b/frontend/src/app/_pages/profile/profile.component.ts @@ -28,6 +28,8 @@ export class ProfileComponent implements OnInit { newPass2: string = ''; photoId: string = ''; photoPath: string = ''; + isPermament:boolean=false; + dateCreated:Date=new Date(); wrongPassBool: boolean = false; wrongNewPassBool: boolean = false; @@ -60,6 +62,9 @@ export class ProfileComponent implements OnInit { this.firstName = this.user.firstName; this.lastName = this.user.lastName; this.photoId = this.user.photoId; + this.dateCreated=this.user.dateCreated; + this.isPermament=this.user.isPermament; + for (let i = 0; i < this.pictures.length; i++) { if (this.pictures[i].photoId.toString() === this.photoId) { @@ -81,7 +86,9 @@ export class ProfileComponent implements OnInit { password: this.user.password, firstName: this.firstName, lastName: this.lastName, - photoId: this.photoId + photoId: this.photoId, + isPermament:this.isPermament, + dateCreated:this.dateCreated } this.userInfoService.changeUserInfo(editedUser).subscribe((response: any) =>{ diff --git a/frontend/src/app/_pages/settings/settings.component.css b/frontend/src/app/_pages/settings/settings.component.css deleted file mode 100644 index e69de29b..00000000 --- a/frontend/src/app/_pages/settings/settings.component.css +++ /dev/null diff --git a/frontend/src/app/_pages/settings/settings.component.html b/frontend/src/app/_pages/settings/settings.component.html deleted file mode 100644 index 4ab2a415..00000000 --- a/frontend/src/app/_pages/settings/settings.component.html +++ /dev/null @@ -1 +0,0 @@ -<p>settings works!</p> diff --git a/frontend/src/app/_pages/settings/settings.component.spec.ts b/frontend/src/app/_pages/settings/settings.component.spec.ts deleted file mode 100644 index a3a508b0..00000000 --- a/frontend/src/app/_pages/settings/settings.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { SettingsComponent } from './settings.component'; - -describe('SettingsComponent', () => { - let component: SettingsComponent; - let fixture: ComponentFixture<SettingsComponent>; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ SettingsComponent ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(SettingsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/_pages/settings/settings.component.ts b/frontend/src/app/_pages/settings/settings.component.ts deleted file mode 100644 index 19862fb0..00000000 --- a/frontend/src/app/_pages/settings/settings.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-settings', - templateUrl: './settings.component.html', - styleUrls: ['./settings.component.css'] -}) -export class SettingsComponent implements OnInit { - - constructor() { } - - ngOnInit(): void { - } - -} diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index d15793e7..fb0b0223 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -7,7 +7,7 @@ </app-reactive-background> <app-navbar></app-navbar> <a class="bg-controls" style="z-index: 1000;" routerLink="playground"> - <mat-icon color="accent">settings_suggest</mat-icon> + <mat-icon color="primary">settings_suggest</mat-icon> </a> <router-outlet></router-outlet> <app-notifications></app-notifications>
\ No newline at end of file diff --git a/frontend/src/app/experiment/experiment.component.html b/frontend/src/app/experiment/experiment.component.html deleted file mode 100644 index 62236cce..00000000 --- a/frontend/src/app/experiment/experiment.component.html +++ /dev/null @@ -1,255 +0,0 @@ -<div id="header"> - <h1>Napravite svoju veštačku neuronsku mrežu</h1> -</div> -<div id="wrapper"> - <div id="container" class="container p-5" style="background-color: white; min-height: 100%;"> - - <div class="d-flex flex-row justify-content-center align-items-center my-3"> - <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="0">Izvor podataka</a> - <mat-icon>arrow_forward</mat-icon> - <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="1">Preprocesiranje</a> - <mat-icon>arrow_forward</mat-icon> - <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="2">Dodaj eksperiment</a> - </div> - - <div id="carouselExampleControls" class="carousel slide px-5 mt-5" data-bs-wrap="false" data-bs-ride="carousel" data-bs-interval="false"> - <div class="carousel-inner"> - <div class="carousel-item active mt-2"> - <h2 class="mb-5">1. Izvor podataka</h2> - <app-dataset-load (selectedDatasetChangeEvent)="updateDataset($event)"></app-dataset-load> - </div> - - <div class="carousel-item mt-2"> - <h2 class="mb-4">2. Preprocesiranje</h2> - - <div class="px-5"> - <h3>Biranje ulaznih i izlaznih kolona:</h3> - <div *ngIf="selectedDataset"> - <div class="row"> - <div class="col d-flex justify-content-center"> - <h3>Izaberite ulazne kolone:</h3> - <div id="divInputs" class="form-check mt-2"> - <br> - <div *ngFor="let item of selectedDataset.columnInfo; let i = index"> - <input class="form-check-input" type="checkbox" value="{{item.columnName}}" id="cb_{{item.columnName}}" name="cbsNew" [checked]="experiment.outputColumn != item.columnName" [disabled]="experiment.outputColumn == item.columnName" (change)="checkedColumnsChanged(item, 0); resetColumnEncodings();"> - <label class="form-check-label" for="cb_{{item.columnName}}"> - {{item.columnName}} - </label> - </div> - </div> - </div> - <div class="col d-flex justify-content-left"> - <h3>Izaberite izlaznu kolonu:</h3> - <div id="divOutputs" class="form-check mt-2"> - <br> - <div *ngFor="let item of selectedDataset.columnInfo; let i = index"> - <input class="form-check-input" type="radio" value="{{item.columnName}}" id="rb_{{item.columnName}}" name="rbsNew" [(ngModel)]="this.experiment.outputColumn" (change)="experiment.outputColumn = item.columnName; checkedColumnsChanged(item, 1); resetColumnEncodings();" - checked> - <label class="form-check-label" for="rb_{{item.columnName}}"> - {{item.columnName}} - </label> - </div> - </div> - </div> - </div> - </div> - - - <div class="mt-5 mb-3 mx-3" *ngIf="countSelectedNullCols() == 0"> - <h3 class="border p-2 text-center"><i>Izabrane kolone nemaju nedostajuće vrednosti koje možete popuniti.</i></h3> - </div> - - <div *ngIf="countSelectedNullCols() != 0"> - <h3 class="mt-5">Popunjavanje nedostajućih vrednosti:</h3> - <div class="form-check" *ngIf="selectedDataset"> - <input type="radio" [(ngModel)]="experiment.nullValues " [value]="NullValueOptions.DeleteRows " class="form-check-input" value="deleteRows" name="fillMissing " id="delRows" checked data-bs-toggle="collapse" data-bs-target="#fillMissingCustom.show"> - <label for="delRows" class="form-check-label ">Obriši sve - redove sa nedostajućim vrednostima ({{selectedDataset.nullRows}} od {{selectedDataset.rowCount}})</label><br> - <input type="radio" [(ngModel)]="experiment.nullValues " [value]="NullValueOptions.DeleteColumns " class="form-check-input" value="deleteCols" name="fillMissing " id="delCols" data-bs-toggle="collapse" data-bs-target="#fillMissingCustom.show"> - <label for="delCols" class="form-check-label ">Obriši sve - kolone sa nedostajućim vrednostima ({{countSelectedNullCols()}} od {{selectedDataset.columnInfo.length}})</label><br> - <input type="radio" [(ngModel)]="experiment.nullValues " [value]="NullValueOptions.Replace " class="form-check-input" name="fillMissing" id="replace" data-bs-toggle="collapse" data-bs-target="#fillMissingCustom:not(.show)"> - <label for="replace" class="form-check-label">Izabraću - vrednosti koje će da zamene nedostajuće vrednosti za svaku kolonu...</label><br><br> - <div class="collapse" id="fillMissingCustom"> - <div> - <label for="columnReplacers" class="form-label">Unesite zamenu za svaku kolonu:</label> - <div class="my-3 " *ngIf="getSelectedColumnsArrayWithoutNullVals().length> 0"> - <label class="text-center form-control mx-3 text-secondary"> - Kolone <span style="font-style: italic;" *ngFor="let colname of getSelectedColumnsArrayWithoutNullVals(); let i = index"> - <span *ngIf="i != getSelectedColumnsArrayWithoutNullVals().length - 1">{{colname}}, </span> - <span *ngIf="i == getSelectedColumnsArrayWithoutNullVals().length - 1">{{colname}} </span> - </span> - nemaju nedostajućih vrednosti za popunjavanje. - </label> - </div> - <div id="columnReplacers"> - <div *ngFor="let column of selectedColumnsInfoArray; let i = index" class="my-3"> - <div *ngIf="column.numNulls > 0"> - <span class="w-20 mx-3"> - {{column.columnName}} - <span class="small" style="color:gray;">({{column.numNulls}} null)</span> - </span> - - <div class="d-flex flex-row justify-content-end"> - <div class="flex-grow-3 mx-3 me-auto"> - <div class="input-group"> - <div class="input-group-prepend"> - <label [for]="'fillCol_'+column.columnName" class="form-control"> - Zameni - <input type="radio" [id]="'fillCol_'+column.columnName" - [name]="'delOp_'+column.columnName"> - </label> - </div> - <input type="text" class="form-control" [id]="'fillText_'+column.columnName" (keyup)="checkFillColRadio(column.columnName)" placeholder="Unesi vrednost..."> - - <div class="input-group-append"> - <select [id]="'replaceOptions'+i" class="form-control btn-primary" *ngIf="column.isNumber" (change)="replace($event, column); checkFillColRadio(column.columnName);"> - <option - *ngFor="let option of Object.keys(ReplaceWith); let optionName of Object.values(ReplaceWith)" - [value]="option"> - {{ optionName }} - </option> - </select> - <select [id]="'replaceOptions'+i" class="form-control btn-outline-primary" *ngIf="!column.isNumber && column.numNulls > 0" (change)="replace($event, column); checkFillColRadio(column.columnName);"> - <option *ngFor="let option of column.uniqueValues" [value]="option"> - {{ option }} - </option> - </select> - </div> - </div> - </div> - - <div class="flex-shrink-1 mx-3"> - <div class="input-group"> - <label class="form-control" [for]="'delCol_'+column.columnName">Izbriši - kolonu - <input type="radio" [id]="'delCol_'+column.columnName" - [name]="'delOp_'+column.columnName" - (change)="emptyFillTextInput(column.columnName)"></label> - </div> - </div> - - <div class="flex-shrink-1 mx-3"> - <div class="input-group"> - <label class="form-control" [for]="'delRows_'+column.columnName">Izbriši - redove - <input type="radio" [id]="'delRows_'+column.columnName" - [name]="'delOp_'+column.columnName" checked - (change)="emptyFillTextInput(column.columnName)"></label> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - - <div id="randomOptions" class="mt-5"> - <div class="p-2 m-2"> - <label for="type" class="form-check-label">Želite li da redosled podataka bude nasumičan?</label> - <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="experiment.randomOrder" type="checkbox" value="" checked> - </div> - <div class="border m-3"> - <div class="row p-2 m-2"> - <div class="col-4"> - <label for="splitYesNo" class="form-check-label"> - <h3>Podela test skupa: - <input id="splitYesNo" class="form-check-input" type="checkbox" - [checked]="experiment.randomTestSet" - (change)="experiment.randomTestSet = !experiment.randomTestSet"> - </h3> - </label> - </div> - <div class="col-8"> - trening - <mat-slider style="width: 85%;" min="10" max="90" step="10" value="10" name="randomTestSetDistribution" thumbLabel [disabled]="!experiment.randomTestSet" [(ngModel)]="tempTestSetDistribution"> - </mat-slider> - test - </div> - </div> - - <div class="row p-2 m-2"> - <label for="percentage" class="form-label">Procenat podataka koji se uzima za trening - skup:</label> - <input id="percentage" type="number" class="form-control mx-3" style=" max-width: 15%" min="10" max="90" step="10" value="90" [(ngModel)]="tempTestSetDistribution" [disabled]="!experiment.randomTestSet"> - </div> - </div> - </div> - - <div id="encodingsForColumns" class="px-3 my-5"> - <label for="encoding" class="col-form-label mb-2">Enkoding za izabrane kolone:</label> - - <div class="row d-flex flex-row justify-content-between align-items-center"> - <div class="col-5" *ngFor="let item of [].constructor(selectedColumnsInfoArray.length); let i = index"> - <div class="input-group mb-2"> - <div class="input-group-prepend"> - <span class="input-group-text">{{selectedColumnsInfoArray[i].columnName}}</span> - </div> - <select [id]="'encodingOption_'+i" class="form-select" [(ngModel)]="experiment.encodings[i].encoding"> - <option - *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - </div> - </div> - </div> - - </div> - - - </div> - - <div class="carousel-item mt-2"> - <h2 class="mb-4">3. Dodaj eskperiment</h2> - - <div class="row"> - <div class="col"></div> - <div class="col-8"> - <label for="name" class="col-form-label">Naziv eksperimenta:</label> - <input type="text" class="form-control mb-3" name="name" placeholder="Naziv..." [(ngModel)]="experiment.name"> - - <label for="desc" class="col-sm-2 col-form-label">Opis:</label> - <div> - <textarea class="form-control" name="desc" rows="3" [(ngModel)]="experiment.description"></textarea> - </div> - <label for="desc" class="col-sm-2 col-form-label mt-3">Tip problema:</label> - <div class="col-4"> - <select id="typeOptions" class="form-select" name="type" [(ngModel)]="experiment.type"> - <option - *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <div class="form-group row mt-5 mb-3"> - <div class="col"></div> - <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="saveExperiment();">Sačuvaj - eksperiment</button> - <div class="col"></div> - </div> - </div> - <div class="col"></div> - </div> - </div> - - </div> - - <div class="m-3 d-flex flex-row justify-content-between align-items-center" style=" margin-left: auto;"> - <button mat-fab color="primary" data-bs-target="#carouselExampleControls" data-bs-slide="prev"> - <mat-icon>arrow_backward</mat-icon> - </button> - <button mat-fab color="primary" data-bs-target="#carouselExampleControls" data-bs-slide="next"> - <mat-icon>arrow_forward</mat-icon> - </button> - </div> - </div> - </div> -</div>
\ No newline at end of file diff --git a/frontend/src/app/experiment/experiment.component.ts b/frontend/src/app/experiment/experiment.component.ts deleted file mode 100644 index 2d0f6ec5..00000000 --- a/frontend/src/app/experiment/experiment.component.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import Experiment, { NullValReplacer, NullValueOptions, ReplaceWith, Encoding } from '../_data/Experiment'; -import Model,{ProblemType} from '../_data/Model'; -import Dataset, { ColumnInfo } from '../_data/Dataset'; -import { ModelsService } from '../_services/models.service'; -import Shared from '../Shared'; -import { ExperimentsService } from '../_services/experiments.service'; -import { ColumnEncoding } from '../_data/Experiment'; - -@Component({ - selector: 'app-experiment', - templateUrl: './experiment.component.html', - styleUrls: ['./experiment.component.css'] -}) -export class ExperimentComponent implements OnInit { - - experiment: Experiment = new Experiment(); - selectedModel?: Model; - selectedDataset?: Dataset; - trainingResult: any; // any za sad, promeni kasnije - - NullValueOptions = NullValueOptions; - ReplaceWith = ReplaceWith; - Encoding = Encoding; - ColumnEncoding = ColumnEncoding; - Object = Object; - ProblemType=ProblemType; - selectedColumnsInfoArray: ColumnInfo[] = []; - selectedNotNullColumnsArray: string[] = []; - - tempTestSetDistribution = 90; - - constructor(private modelsService: ModelsService, private experimentsService: ExperimentsService) { - } - - ngOnInit(): void { - } - - updateDataset(dataset: Dataset) { - this.selectedDataset = dataset; - this.selectedColumnsInfoArray = this.selectedDataset.columnInfo; - this.selectedNotNullColumnsArray = []; - this.experiment.outputColumn = this.selectedDataset.columnInfo[this.selectedDataset.columnInfo.length - 1].columnName; - - this.resetColumnEncodings(); - console.log(this.experiment.encodings); - } - - resetColumnEncodings() { - this.experiment.encodings = []; - for (let i = 0; i < this.selectedColumnsInfoArray.length; i++) { - this.experiment.encodings.push(new ColumnEncoding(this.selectedColumnsInfoArray[i].columnName, Encoding.Label)); - } - } - - getInputById(id: string): HTMLInputElement { - return document.getElementById(id) as HTMLInputElement; - } - - arrayColumn = (arr: any[][], n: number) => [...this.dropEmptyString(new Set(arr.map(x => x[n])))]; - - dropEmptyString(set: Set<any>): Set<string> { - if (set.has("")) - set.delete(""); - if (set.has(null)) - set.delete(null); - if (set.has(undefined)) - set.delete(undefined); - return set; - } - - emptyFillTextInput(colName: string) { - (<HTMLInputElement>document.getElementById("fillText_" + colName)).value = ""; - } - - checkFillColRadio(colName: string) { - (<HTMLInputElement>document.getElementById("fillCol_" + colName)).checked = true; - } - - checkedColumnsChanged(checkedColumnInfo: ColumnInfo, buttonType: number) { //0-input,1-output - let col = this.selectedColumnsInfoArray.find(x => x.columnName == checkedColumnInfo.columnName); - if (buttonType == 0) { //inputCol - if (col == undefined) - this.selectedColumnsInfoArray.push(checkedColumnInfo); - else - this.selectedColumnsInfoArray = this.selectedColumnsInfoArray.filter(x => x.columnName != checkedColumnInfo.columnName); - } - else { //outputCol - if (col == undefined) //ako je vec cekiran neki output, samo dodaj sad ovaj, a taj output postaje input i ostaje u nizu - this.selectedColumnsInfoArray.push(checkedColumnInfo); - } - //console.log(this.selectedColumnsInfoArray); - } - - replace(event: Event, column: ColumnInfo) { - let option = (<HTMLInputElement>event.target).value; - - const input = (<HTMLInputElement>document.getElementById("fillText_" + column.columnName)); - if (column.isNumber) { - switch (option) { - case ReplaceWith.Max: - input.value = "" + column.max; - break; - case ReplaceWith.Min: - input.value = "" + column.min; - break; - case ReplaceWith.Mean: - input.value = "" + column.mean; - break; - case ReplaceWith.Median: - input.value = "" + column.median; - break; - case ReplaceWith.None: - break; - } - } else { - input.value = option; - } - } - - getSelectedColumnsArrayWithoutNullVals(): string[] { - let colNames: string[] = []; - - for (let i = 0; i < this.selectedColumnsInfoArray.length; i++) { - let oneColInfo = this.selectedColumnsInfoArray[i]; - if (oneColInfo.numNulls == 0) - colNames.push(oneColInfo.columnName); - } - return colNames; - } - - getNullValuesReplacersArray(): NullValReplacer[] { - let array: NullValReplacer[] = []; - - if (this.experiment.nullValues == NullValueOptions.Replace) { - - for (let i = 0; i < this.selectedColumnsInfoArray.length; i++) { - let oneColInfo = this.selectedColumnsInfoArray[i]; - - if (oneColInfo.numNulls > 0) { //ako kolona nema null vrednosti, ne dodajemo je u niz - if ((<HTMLInputElement>document.getElementById("delCol_" + oneColInfo.columnName)).checked) { - array.push({ - column: oneColInfo.columnName, - option: NullValueOptions.DeleteColumns, - value: "" - }); - } - else if ((<HTMLInputElement>document.getElementById("delRows_" + oneColInfo.columnName)).checked) { - array.push({ - column: oneColInfo.columnName, - option: NullValueOptions.DeleteRows, - value: "" - }); - } - else if (((<HTMLInputElement>document.getElementById("fillCol_" + oneColInfo.columnName)).checked)) { - array.push({ - column: oneColInfo.columnName, - option: NullValueOptions.Replace, - value: (<HTMLInputElement>document.getElementById("fillText_" + oneColInfo.columnName)).value - }); - } - } - } - } - return array; - } - - saveExperiment() { - if (this.selectedDataset == undefined) { - Shared.openDialog("Greška", "Izvor podataka nije izabran!"); - return; - } - if (this.experiment.outputColumn == '') { - Shared.openDialog("Greška", "Molimo Vas da izaberete izlaznu kolonu."); - return; - } - if (this.selectedColumnsInfoArray.length <= 1) { //jer izlazna je izabrana - Shared.openDialog("Greška", "Molimo Vas da izaberete ulazne kolone."); - return; - } - - this.experiment._id = ''; - this.experiment.uploaderId = ''; - this.experiment.datasetId = this.selectedDataset._id; - - let pom = this.selectedColumnsInfoArray.filter(x => x.columnName != this.experiment.outputColumn); - for (let i = 0; i < pom.length; i++) - this.experiment.inputColumns.push(pom[i].columnName); - - this.selectedColumnsInfoArray = this.selectedColumnsInfoArray.filter(x => x.numNulls > 0); //obavezno - this.experiment.nullValuesReplacers = this.getNullValuesReplacersArray(); - - this.experiment.randomTestSetDistribution = 1 - Math.round(this.tempTestSetDistribution / 100 * 10) / 10; - - console.log("Eksperiment:", this.experiment); - - this.experimentsService.addExperiment(this.experiment).subscribe((response) => { - this.experiment = response; - - this.selectedColumnsInfoArray = []; - this.selectedNotNullColumnsArray = []; - this.experiment.encodings = []; - - Shared.openDialog("Obaveštenje", "Eksperiment je uspešno kreiran."); - }, (error) => { - if (error.error == "Experiment with this name exists") { - Shared.openDialog("Greška", "Eksperiment sa unetim nazivom već postoji u Vašoj kolekciji. Unesite neki drugi naziv."); - } - }); - } - - countSelectedNullCols(): number { - let counter: number = 0; - - for (let i = 0; i < this.selectedColumnsInfoArray.length; i++) { - let oneColInfo = this.selectedColumnsInfoArray[i]; - if (oneColInfo.numNulls > 0) - ++counter; - } - return counter; - } -} diff --git a/frontend/src/app/training/training.component.html b/frontend/src/app/training/training.component.html deleted file mode 100644 index 672e75fb..00000000 --- a/frontend/src/app/training/training.component.html +++ /dev/null @@ -1,46 +0,0 @@ -<div id="header"> - <h1>Trenirajte veštačku neuronsku mrežu</h1> -</div> -<div id="wrapper" class="mb-4"> - <div id="container" class="container p-5 row" style="background-color: white; min-height: 100%;"> - <div class="col"></div> - - <div class="col-10"> - - <h2>1. Izaberite eksperiment iz kolekcije</h2> - <div class="px-5 mt-5 mb-3"> - <input type="text" class="form-control" placeholder="Pretraga" - [(ngModel)]="term"> - </div> - <div class="overflow-auto px-5" style="max-height: 500px;"> - <ul class="list-group"> - <li class="list-group-item p-3" *ngFor="let experiment of myExperiments|filter:term" - [ngClass]="{'selectedExperimentClass': this.selectedExperiment == experiment}"> - <app-item-experiment [experiment]="experiment" - (click)="selectThisExperiment(experiment);"></app-item-experiment> - </li> - </ul> - </div> - - <h2 class="mt-5 mb-2">2. Izaberite model</h2> - <app-model-load (selectedModelChangeEvent)="selectModel($event)" [forExperiment]="selectedExperiment"></app-model-load> - - <h2 class="my-5">3. Treniranje modela</h2> - - <div class="d-flex flex-row justify-content-center align-items-center my-3"> - <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="trainModel();">Treniraj - model</button> - </div> - - <h2 class="mt-5">Rezultati treniranja</h2> - <div class="m-3" *ngIf="trainingResult"> - <h2 class="my-2">Rezultati treniranja:</h2> - <p> - {{trainingResult}} - </p> - </div> - </div> - - <div class="col"></div> - </div> -</div>
\ No newline at end of file diff --git a/frontend/src/styles/helper.css b/frontend/src/styles/helper.css index 1c172058..971a2ed6 100644 --- a/frontend/src/styles/helper.css +++ b/frontend/src/styles/helper.css @@ -17,7 +17,8 @@ .bg-controls { position: fixed; bottom: 10px; - right: 10px; + left: 50%; + right: 50%; } .center-horizontal { |