diff options
Diffstat (limited to 'frontend/src/app')
23 files changed, 341 insertions, 254 deletions
diff --git a/frontend/src/app/_data/Dataset.ts b/frontend/src/app/_data/Dataset.ts index 732d1c56..766040a3 100644 --- a/frontend/src/app/_data/Dataset.ts +++ b/frontend/src/app/_data/Dataset.ts @@ -10,7 +10,7 @@ export default class Dataset { public accessibleByLink: boolean = false, public dateCreated: Date = new Date(), public lastUpdated: Date = new Date(), - public username: string = '', + public uploaderId: string = '', public delimiter: string = '', public hasHeader: boolean = true, diff --git a/frontend/src/app/_data/Experiment.ts b/frontend/src/app/_data/Experiment.ts index 453f6ca0..95ef6e1e 100644 --- a/frontend/src/app/_data/Experiment.ts +++ b/frontend/src/app/_data/Experiment.ts @@ -1,3 +1,4 @@ +import { ProblemType } from "./Model"; export default class Experiment { _id: string = ''; uploaderId: string = ''; @@ -18,8 +19,8 @@ export default class Experiment { public randomTestSet: boolean = true, public randomTestSetDistribution: number = 0.1, //0.1-0.9 (10% - 90%) JESTE OVDE ZAKUCANO 10, AL POSLATO JE KAO 0.1 BACK-U - //TODO - za svaku kolonu se bira enkoding - public encoding: Encoding = Encoding.Label + public encodings: ColumnEncoding[] = [], + public type: ProblemType = ProblemType.Regression ) { } } @@ -65,4 +66,12 @@ export enum Encoding { WOE = 'woe', Quantile = 'quantile' */ +} + +export class ColumnEncoding { + constructor ( + public columnName: string, + public encoding: Encoding + ) + {} }
\ No newline at end of file diff --git a/frontend/src/app/_data/Model.ts b/frontend/src/app/_data/Model.ts index 1af3fe30..b273f56a 100644 --- a/frontend/src/app/_data/Model.ts +++ b/frontend/src/app/_data/Model.ts @@ -19,7 +19,7 @@ export default class Model { public batchSize: number = 5, public hiddenLayerActivationFunctions: string[] = ['sigmoid'], public outputLayerActivationFunction: ActivationFunction = ActivationFunction.Sigmoid, - public username: string = '', + public uploaderId: string = '', public metrics: string[] = [], // TODO add to add-model form public epochs: number = 5 // TODO add to add-model form ) { } diff --git a/frontend/src/app/_data/Notification.ts b/frontend/src/app/_data/Notification.ts index c505d399..94a3be1d 100644 --- a/frontend/src/app/_data/Notification.ts +++ b/frontend/src/app/_data/Notification.ts @@ -1,5 +1,4 @@ export default class Notification { - _id: string = ''; constructor( public title: string = 'Treniranje u toku...', public id: string = '042', diff --git a/frontend/src/app/_data/Predictor.ts b/frontend/src/app/_data/Predictor.ts index 7e902eae..8aa2b6cb 100644 --- a/frontend/src/app/_data/Predictor.ts +++ b/frontend/src/app/_data/Predictor.ts @@ -7,6 +7,7 @@ export default class Predictor { public output: string = '', public isPublic: boolean = false, public accessibleByLink: boolean = false, - public dateCreated: Date = new Date() + public dateCreated: Date = new Date(), + public uploaderId: string = '' ) { } }
\ No newline at end of file diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts index 6ff108ce..3e1b5c73 100644 --- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts +++ b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts @@ -90,7 +90,7 @@ export class AddNewDatasetComponent { this.modelsService.uploadData(this.files[0]).subscribe((file) => { //console.log('ADD MODEL: STEP 2 - ADD DATASET WITH FILE ID ' + file._id); this.dataset.fileId = file._id; - this.dataset.username = shared.username; + this.dataset.uploaderId = shared.userId; this.datasetsService.addDataset(this.dataset).subscribe((dataset) => { this.newDatasetAdded.emit("added"); diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts index 62cca456..73dbf2d2 100644 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts +++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts @@ -41,12 +41,14 @@ export class DatasetLoadComponent implements OnInit { viewMyDatasetsForm() { this.showMyDatasets = true; - this.resetSelectedDataset(); + if (this.selectedDataset != undefined) + this.resetSelectedDataset(); //this.resetCbsAndRbs(); //TREBA DA SE DESI } viewNewDatasetForm() { this.showMyDatasets = false; - this.resetSelectedDataset(); + if (this.selectedDataset != undefined) + this.resetSelectedDataset(); //this.resetCbsAndRbs(); //TREBA DA SE DESI } diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.html b/frontend/src/app/_elements/item-predictor/item-predictor.component.html index 7ae26fd3..3199dcc8 100644 --- a/frontend/src/app/_elements/item-predictor/item-predictor.component.html +++ b/frontend/src/app/_elements/item-predictor/item-predictor.component.html @@ -1,26 +1,35 @@ <div class="card" style="min-width: 12rem;"> - <div class="card-header"> - {{predictor.name}} + <div class="card-header d-flex mb-2 justify-content-" style="padding: 0;margin: 0;"> + + <div class=" p-2 float-left "><b style="color: gray;">Prediktor</b></div> + </div> - <div class="card-body"> + <div class="card-body overflow-hidden"> + <b style="color: gray;">Opis</b><hr style="width: 20%;"> <p class="card-text"> - {{predictor.description}} + {{predictor.description}} </p> - <div class="d-flex flex-column align-items-center"> - <table class="table table-bordered table-sm"> + + <b style="color: gray;">Ulazne kolone</b> + <div style="overflow: scroll; overflow-y: hidden;"> + + <table class="table table-bordered table-md" > <thead> - <th class="text-center" *ngFor="let column of predictor.inputs">{{column}}</th> + <th scope="col" *ngFor="let column of predictor.inputs" >{{column}}</th> </thead> </table> - <mat-icon>arrow_downward</mat-icon> - <p> - {{predictor.output}} - </p> + </div> + <b style="color: gray;">Izlazna kolona: </b><b>{{predictor.output}}</b> + <hr> + <div> + <table> + <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{predictor.dateCreated |date}}</td></tr> + </table> </div> </div> <div class="card-footer text-center"> - <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" + <button class="btn btn-md col-4" style="background-color:#003459; color:white;" (click)="openPredictor();">Iskoristi</button> - <!--<a routerLink="/predict" mat-raised-button color="primary">Iskoristi</a>--> + </div> </div>
\ 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 index f40ea476..dcb35c21 100644 --- a/frontend/src/app/_elements/model-load/model-load.component.html +++ b/frontend/src/app/_elements/model-load/model-load.component.html @@ -17,7 +17,7 @@ <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" + <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> @@ -43,11 +43,7 @@ <textarea class="form-control" name="desc" rows="3" [(ngModel)]="newModel.description"></textarea> </div> </div> - <div class="col-2"> - <label for="dateCreated" class="col-form-label">Datum:</label> - <input type="text" class="form-control-plaintext" id="dateCreated" placeholder="--/--/--" - value="{{newModel.dateCreated | date: 'dd/MM/yyyy'}}" readonly> - </div> + </div> <h2 class="mt-5 mb-4 mx-5">Parametri treniranja modela:</h2> <div> @@ -58,7 +54,7 @@ <label for="type" class="col-form-label">Tip problema: </label> </div> <div class="col-2"> - <select id=typeOptions class="form-control" name="type" [(ngModel)]="newModel.type" + <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)" @@ -86,7 +82,7 @@ <label for="optimizer" class="col-form-label">Optimizacija: </label> </div> <div class="col-2"> - <select id=optimizerOptions class="form-control" name="optimizer" [(ngModel)]="newModel.optimizer"> + <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"> @@ -108,10 +104,10 @@ <div class="row p-2"> <div class="col-1"></div> <div class="col-3"> - <label for="lossFunction" class="col-form-label">Funkcija obrade gubitka: </label> + <label for="lossFunction" class="col-form-label">Funkcija troška: </label> </div> <div class="col-2"> - <select id=lossFunctionOptions class="form-control" name="lossFunction" + <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)" @@ -125,7 +121,21 @@ <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label> </div> <div class="col-1"> - <input type="number" min="1" class="form-control" name="batchSize" [(ngModel)]="newModel.batchSize"> + + <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> @@ -147,8 +157,8 @@ <div class="input-group-prepend"> <span class="input-group-text">#{{i+1}}</span> </div> - <select [id]="'hiddenLayerActivationFunctionOption_'+i" class="form-control" - [(ngModel)]="newModel.hiddenLayerActivationFunctions[i]"> + <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"> @@ -164,7 +174,7 @@ style="text-align: center;">Funkcija aktivacije<br>izlaznog sloja:</label> </div> <div class="col-2 mt-2"> - <select id=outputLayerActivationFunctionOptions class="form-control" + <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)" diff --git a/frontend/src/app/_elements/model-load/model-load.component.ts b/frontend/src/app/_elements/model-load/model-load.component.ts index 745dc12e..dbca3d17 100644 --- a/frontend/src/app/_elements/model-load/model-load.component.ts +++ b/frontend/src/app/_elements/model-load/model-load.component.ts @@ -1,5 +1,6 @@ -import { Component, OnInit, ViewChild, Output, EventEmitter } from '@angular/core'; +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'; @@ -13,6 +14,7 @@ import { GraphComponent } from '../graph/graph.component'; export class ModelLoadComponent implements OnInit { @ViewChild(GraphComponent) graph!: GraphComponent; + @Input() forExperiment?:Experiment; @Output() selectedModelChangeEvent = new EventEmitter<Model>(); newModel: Model = new Model(); @@ -43,6 +45,11 @@ export class ModelLoadComponent implements OnInit { ngOnInit(): void { } + batchSizePower:number=1; + updateBatchSize() + { + this.newModel.batchSize=2**this.batchSizePower; + } updateGraph() { this.graph.update(); @@ -62,7 +69,7 @@ export class ModelLoadComponent implements OnInit { uploadModel() { this.getMetrics(); - this.newModel.username = Shared.username; + this.newModel.uploaderId = Shared.userId; this.modelsService.addModel(this.newModel).subscribe((response) => { Shared.openDialog('Model dodat', 'Model je uspešno dodat u bazu.'); diff --git a/frontend/src/app/_elements/notifications/notifications.component.html b/frontend/src/app/_elements/notifications/notifications.component.html index ef897cfc..3b2f4eaa 100644 --- a/frontend/src/app/_elements/notifications/notifications.component.html +++ b/frontend/src/app/_elements/notifications/notifications.component.html @@ -11,14 +11,18 @@ </button> </h2> - <div id="collapseNotifs" class="collapse show"> + <div id="collapseNotifs" class="collapse show overflow-auto" style="max-height: 32rem;"> <div *ngFor="let notification of notifications" class="p-2 m-1 border rounded"> - <div class="d-flex flex-row"> - <p>{{notification.title}}</p> - </div> - <div *ngIf="notification.hasProgress" class="border-3 border-primary bg-dark m-1" style="height: 5px; margin-top: -10px !important;"> - <div class="bg-primary" style="height: 5px;" [style]="'width: '+(notification.progress*100)+'%;'"> + <div class="d-flex flex-row "> + <div> + <p>{{notification.title}}</p> + <div *ngIf="notification.hasProgress" class="border-3 border-primary bg-dark m-1" style="height: 5px; margin-top: -10px !important; min-width: 12rem;"> + <div class="bg-primary" style="height: 5px;" [style]="'width: '+(notification.progress*100)+'%;'"> + </div> + </div> </div> + <button type="button" class="btn-close ms-auto align-self-center" aria-label="Close" (click)="removeNotification(notification);"></button> </div> + </div> </div>
\ No newline at end of file diff --git a/frontend/src/app/_elements/notifications/notifications.component.ts b/frontend/src/app/_elements/notifications/notifications.component.ts index e199f70a..9b460240 100644 --- a/frontend/src/app/_elements/notifications/notifications.component.ts +++ b/frontend/src/app/_elements/notifications/notifications.component.ts @@ -21,13 +21,33 @@ export class NotificationsComponent implements OnInit { this.notifications.push(new Notification(`Obrađen izvor podataka: ${dName}`, dId, 1.0, false)); }); - this.signalRService.hubConnection.on("NotifyEpoch", (epoch: string, mName: string, mId: string, numEpochs) => { - //todo epoch - this.notifications.push(new Notification(`Treniranje modela: ${mName}`, mId, 0.5)); + this.signalRService.hubConnection.on("NotifyEpoch", (mName: string, mId: string, stat: string, totalEpochs: number, currentEpoch: number) => { + const existingNotification = this.notifications.find(x => x.id === mId) + const progress = ((currentEpoch + 1) / totalEpochs); + console.log("Ukupno epoha", totalEpochs, "Trenutna epoha:", currentEpoch); + if (!existingNotification) + this.notifications.push(new Notification(`Treniranje modela: ${mName}`, mId, progress, true)); + else { + existingNotification.progress = progress; + } + }); + + this.signalRService.hubConnection.on("NotifyModel", (mName: string, mId: string, stat: string, totalEpochs: number, currentEpoch: number) => { + const existingNotification = this.notifications.find(x => x.id === mId) + const progress = ((currentEpoch + 1) / totalEpochs); + if (!existingNotification) + this.notifications.push(new Notification(`Treniranje modela: ${mName}`, mId, progress, true)); + else { + existingNotification.progress = progress; + } }); } else { console.warn("Notifications: No connection!"); } } + removeNotification(notification: Notification) { + this.notifications.splice(this.notifications.indexOf(notification), 1); + } + } diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts index c83bf208..66b3755e 100644 --- a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts +++ b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts @@ -33,7 +33,7 @@ export class FilterDatasetsComponent implements OnInit { newDataset._id = ""; newDataset.isPublic = false; newDataset.lastUpdated = new Date(); - newDataset.username = decodedToken.name; + newDataset.uploaderId = decodedToken.uploaderId; let name=prompt("Unesite naziv dataset-a",newDataset.name); newDataset.name=name as string; if(name!=null && name!="") diff --git a/frontend/src/app/_pages/my-models/my-models.component.html b/frontend/src/app/_pages/my-models/my-models.component.html index b0e9c4ef..9b281239 100644 --- a/frontend/src/app/_pages/my-models/my-models.component.html +++ b/frontend/src/app/_pages/my-models/my-models.component.html @@ -15,7 +15,7 @@ <div class="panel-footer row"><!-- panel-footer --> <div class="col-xs-6 text-center"> <div> - <button type="button" class="btn btn-default btn-lg"style="min-width: 7rem;float: left;" (click)="deleteThisModel(model)" mat-raised-button color="primary">Koristi + <button type="button" class="btn btn-default btn-lg"style="min-width: 7rem;float: left;" (click)="useThisModel(model)" mat-raised-button color="primary">Koristi <span class="glyphicon glyphicon-search"></span> </button> <button (click)="deleteThisModel(model)" mat-raised-button color="warn" style="min-width: 7rem;float: right" ><mat-icon>delete</mat-icon></button> diff --git a/frontend/src/app/_pages/my-models/my-models.component.ts b/frontend/src/app/_pages/my-models/my-models.component.ts index 92d3fbaa..d379fa69 100644 --- a/frontend/src/app/_pages/my-models/my-models.component.ts +++ b/frontend/src/app/_pages/my-models/my-models.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; import shared from 'src/app/Shared'; import Model from 'src/app/_data/Model'; import { ModelsService } from 'src/app/_services/models.service'; @@ -12,7 +13,7 @@ export class MyModelsComponent implements OnInit { myModels: Model[] = []; //myModel: Model; - constructor(private modelsS : ModelsService) { + constructor(private modelsS : ModelsService, private router : Router) { @@ -44,6 +45,11 @@ deleteThisModel(model: Model): void{ } +useThisModel(model: Model): void{ + + this.router.navigate(['/training']) + +} getAllMyModels(): void{ this.modelsS.getMyModels().subscribe(m => { this.myModels = m; diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.html b/frontend/src/app/_pages/my-predictors/my-predictors.component.html index d38f93e4..31fa786c 100644 --- a/frontend/src/app/_pages/my-predictors/my-predictors.component.html +++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.html @@ -1,16 +1,23 @@ <div id="header"> <h1>Trenirani modeli</h1> </div> -<div id="container" style="background-color:rgba(255, 255, 255, 0.8);"> -<div class="row" *ngFor="let predictor of predictors"> - <div class="left"> - <app-item-predictor [predictor]="predictor"></app-item-predictor> - </div> - <div> - <button (click)="delete(predictor)" mat-raised-button color="warn" style="min-width: 15rem;float: right" ><mat-icon>delete</mat-icon></button> - </div> - +<div id="wrapper"> +<div id="container" class="container p-5" style="background-color:rgba(255, 255, 255, 0.8); min-height: 100%;"> + <div class="row mt-3 mb-2 d-flex justify-content-center"> + + <div class="col-sm-6" style="margin-bottom: 10px;"> + </div> + <div class="row"> + <div class="col-sm-4" style="margin-bottom: 10px;" *ngFor="let predictor of predictors"> + <app-item-predictor [predictor]="predictor"></app-item-predictor> + <div> + <button (click)="deleteThisPredictor(predictor)" mat-raised-button color="warn" style="min-width: 10rem;float: right" ><mat-icon>delete</mat-icon></button> + </div> + </div> + </div> +</div> </div> - </div> + + diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.ts b/frontend/src/app/_pages/my-predictors/my-predictors.component.ts index 17c496fd..4dc5300d 100644 --- a/frontend/src/app/_pages/my-predictors/my-predictors.component.ts +++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import Predictor from 'src/app/_data/Predictor'; import { PredictorsService } from 'src/app/_services/predictors.service'; - +import shared from 'src/app/Shared'; @Component({ selector: 'app-my-predictors', templateUrl: './my-predictors.component.html', @@ -12,28 +12,30 @@ export class MyPredictorsComponent implements OnInit { constructor(private predictorsS : PredictorsService) { } ngOnInit(): void { - this.getAllMyPredictors(); - + this.predictorsS.getMyPredictors().subscribe((response) => { + this.predictors = response; + }, (error) => { + if (error.error == "Predictor with...") { + shared.openDialog("Greska", "Greska"); + } + }); } - delete(predictor: Predictor){ - if(window.confirm("IZABRANI MODEL ĆE BITI IZBRISAN")) - { - this.predictorsS.deletePredictor(predictor).subscribe((response) => { - this.getAllMyPredictors(); - }, (error) =>{ - if (error.error == "Predictor with name = {name} deleted") { - alert("Greška pri brisanju modela!"); - } - }); - } - - + deleteThisPredictor(predictor: Predictor): void{ + shared.openYesNoDialog('Brisanje prediktora','Da li ste sigurni da želite da obrišete prediktor?',() => { + this.predictorsS.deletePredictor(predictor).subscribe((response) => { + this.getAllMyPredictors(); + }, (error) =>{ + if (error.error == "Predictor with name = {name} deleted") { + shared.openDialog("Obaveštenje", "Greška prilikom brisanja prediktora."); + } + }); + }); } getAllMyPredictors(): void{ - this.predictorsS.getMyPredictors().subscribe(m => { - this.predictors = m; + this.predictorsS.getMyPredictors().subscribe(p => { + this.predictors = p; }); } diff --git a/frontend/src/app/_services/datasets.service.ts b/frontend/src/app/_services/datasets.service.ts index c3281be6..b2272f0a 100644 --- a/frontend/src/app/_services/datasets.service.ts +++ b/frontend/src/app/_services/datasets.service.ts @@ -29,10 +29,10 @@ export class DatasetsService { } editDataset(dataset: Dataset): Observable<Dataset> { - return this.http.put<Dataset>(`${Configuration.settings.apiURL}/dataset/`, dataset, { headers: this.authService.authHeader() }); + return this.http.put<Dataset>(`${Configuration.settings.apiURL}/dataset/` + dataset._id, dataset, { headers: this.authService.authHeader() }); } deleteDataset(dataset: Dataset) { - return this.http.delete(`${Configuration.settings.apiURL}/dataset/` + dataset.name, { headers: this.authService.authHeader(), responseType: "text" }); + return this.http.delete(`${Configuration.settings.apiURL}/dataset/` + dataset._id, { headers: this.authService.authHeader(), responseType: "text" }); } } diff --git a/frontend/src/app/_services/predictors.service.ts b/frontend/src/app/_services/predictors.service.ts index 9e8383aa..a24ee708 100644 --- a/frontend/src/app/_services/predictors.service.ts +++ b/frontend/src/app/_services/predictors.service.ts @@ -24,7 +24,7 @@ export class PredictorsService { } deletePredictor(predictor: Predictor) { - return this.http.delete(`${Configuration.settings.apiURL}/predictor/` + predictor.name, { headers: this.authService.authHeader(), responseType: "text" }); + return this.http.delete(`${Configuration.settings.apiURL}/predictor/` + predictor._id, { headers: this.authService.authHeader(), responseType: "text" }); } getMyPredictors(): Observable<Predictor[]> { diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index e22f7a88..238668d9 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -20,6 +20,7 @@ const routes: Routes = [ /*{ path: 'add-model', component: AddModelComponent, data: { title: 'Dodaj model' } },*/ { path: 'experiment', component: ExperimentComponent, data: { title: 'Dodaj eksperiment' } }, { path: 'training', component: TrainingComponent, data: { title: 'Treniraj model' } }, + { path: 'training/:id', component: TrainingComponent, data: { title: 'Treniraj model' } }, { path: 'my-datasets', component: MyDatasetsComponent, canActivate: [AuthGuardService], data: { title: 'Moji izvori podataka' } }, { path: 'my-models', component: MyModelsComponent, canActivate: [AuthGuardService], data: { title: 'Moji modeli' } }, { path: 'my-predictors', component: MyPredictorsComponent, canActivate: [AuthGuardService], data: { title: 'Moji trenirani modeli' } }, @@ -28,6 +29,7 @@ const routes: Routes = [ { path: 'browse-datasets', component: FilterDatasetsComponent, data: { title: 'Javni izvori podataka' } }, { path: 'browse-predictors', component: BrowsePredictorsComponent, data: { title: 'Javni trenirani modeli' } }, { path: 'predict/:id', component: PredictComponent, data: { title: 'Predvidi vrednosti' } }, + ]; @NgModule({ diff --git a/frontend/src/app/experiment/experiment.component.html b/frontend/src/app/experiment/experiment.component.html index e46f5bd9..62236cce 100644 --- a/frontend/src/app/experiment/experiment.component.html +++ b/frontend/src/app/experiment/experiment.component.html @@ -13,150 +13,132 @@ </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-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="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" - (click)="checkedColumnsChanged(item, 0)"> - <label class="form-check-label" for="cb_{{item.columnName}}"> + <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> - <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" - (click)="checkedColumnsChanged(item, 1);"> - <label class="form-check-label" for="rb_{{item.columnName}}"> + <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> - - <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 + + + <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 + <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 + <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"> + <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> - - <label *ngIf="column.numNulls <= 0" - class="text-center form-control mx-3 text-secondary"> - Ova kolona nema - nedostajućih - vrednosti. - </label> - - <div *ngIf="column.numNulls > 0" 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"> + </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);"> + </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);"> + <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> - </div> - - <div class="flex-shrink-1 mx-3"> - <div class="input-group"> - <label class="form-control" [for]="'delCol_'+column.columnName">Izbriši + + <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 + </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> @@ -165,96 +147,109 @@ </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"> + + <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="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 + + <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"> + <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> - - <div id="encodingForColumns" class="row px-3 my-5"> - <div class="col-1"> - <label for="encoding" class="col-form-label">Enkoding: </label> - </div> - <div class="col-2"> - <select id=encodingOptions class="form-control" name="encoding" [(ngModel)]="experiment.encoding"> - <option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" - [value]="option"> - {{ optionName }} - </option> - </select> + + <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> + </div> - <div class="carousel-item mt-2"> - <h2 class="mb-4">3. Dodaj eskperiment</h2> + <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> - <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 + <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 class="col"></div> + </div> </div> + <div class="col"></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 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 index b5e1d1f4..2d0f6ec5 100644 --- a/frontend/src/app/experiment/experiment.component.ts +++ b/frontend/src/app/experiment/experiment.component.ts @@ -1,10 +1,11 @@ import { Component, OnInit } from '@angular/core'; import Experiment, { NullValReplacer, NullValueOptions, ReplaceWith, Encoding } from '../_data/Experiment'; -import Model from '../_data/Model'; +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', @@ -21,8 +22,9 @@ export class ExperimentComponent implements OnInit { NullValueOptions = NullValueOptions; ReplaceWith = ReplaceWith; Encoding = Encoding; + ColumnEncoding = ColumnEncoding; Object = Object; - + ProblemType=ProblemType; selectedColumnsInfoArray: ColumnInfo[] = []; selectedNotNullColumnsArray: string[] = []; @@ -38,6 +40,17 @@ export class ExperimentComponent implements OnInit { 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 { @@ -179,13 +192,14 @@ export class ExperimentComponent implements OnInit { this.experiment.randomTestSetDistribution = 1 - Math.round(this.tempTestSetDistribution / 100 * 10) / 10; - //console.log("Eksperiment:", this.experiment); + console.log("Eksperiment:", this.experiment); this.experimentsService.addExperiment(this.experiment).subscribe((response) => { this.experiment = response; - this.selectedColumnsInfoArray = []; + this.selectedColumnsInfoArray = []; this.selectedNotNullColumnsArray = []; + this.experiment.encodings = []; Shared.openDialog("Obaveštenje", "Eksperiment je uspešno kreiran."); }, (error) => { diff --git a/frontend/src/app/training/training.component.html b/frontend/src/app/training/training.component.html index 0ce4cc89..672e75fb 100644 --- a/frontend/src/app/training/training.component.html +++ b/frontend/src/app/training/training.component.html @@ -23,7 +23,7 @@ </div> <h2 class="mt-5 mb-2">2. Izaberite model</h2> - <app-model-load (selectedModelChangeEvent)="selectModel($event)"></app-model-load> + <app-model-load (selectedModelChangeEvent)="selectModel($event)" [forExperiment]="selectedExperiment"></app-model-load> <h2 class="my-5">3. Treniranje modela</h2> |