diff options
Diffstat (limited to 'frontend/src/app/_pages')
11 files changed, 291 insertions, 16 deletions
diff --git a/frontend/src/app/_pages/experiment/experiment.component.css b/frontend/src/app/_pages/experiment/experiment.component.css index 59e004e9..37edd3c2 100644 --- a/frontend/src/app/_pages/experiment/experiment.component.css +++ b/frontend/src/app/_pages/experiment/experiment.component.css @@ -74,4 +74,34 @@ mat-stepper { .text-overflow-experiment-name { overflow-wrap: break-word; +} + +#compareButton { + position: absolute; + top: 10px; + right: 10px; +} + +.side-by-side { + display: flex; + flex-direction: row; + width: 100%; + height: 100%; +} + + +/* one item */ + +.side-by-side>*:first-child:nth-last-child(1) { + /* -or- li:only-child { */ + width: 100%; +} + + +/* two items */ + +.side-by-side>*:first-child:nth-last-child(2), +.side-by-side>*:first-child:nth-last-child(2)~li { + width: 50%; + margin: 5px; }
\ No newline at end of file diff --git a/frontend/src/app/_pages/experiment/experiment.component.html b/frontend/src/app/_pages/experiment/experiment.component.html index 17a6539d..3176f58c 100644 --- a/frontend/src/app/_pages/experiment/experiment.component.html +++ b/frontend/src/app/_pages/experiment/experiment.component.html @@ -25,10 +25,10 @@ <ng-template matStepLabel> <span class="label addedElement text-overflow" *ngIf="experiment._id!=''">Predvideti:{{experiment.outputColumn}}</span> <span *ngIf="!this.step1" class="align-middle"><mat-icon>lock</mat-icon></span> - <span class="label text-overflow" *ngIf="experiment._id==''">Odabir kolona </span> + <span class="label text-overflow" *ngIf="experiment._id==''">Priprema podataka </span> </ng-template> <ng-template matStepContent> - <p class="text-left text-overflow">Pripremite podatke i izaberite izlazne kolone</p> + <p class="text-left text-overflow">Pripremite podatke i odaberite ulazne i izlaznu kolonu</p> </ng-template> </mat-step> <mat-step [completed]="this.step3"> @@ -57,18 +57,35 @@ </div> <div #steps id="step_2" class="step-content" *ngIf="step1"> <div class="step-content-inside"> - <app-column-table (okPressed)="goToPage(2); experiment._columnsSelected = true;" (columnTableChanged)="columnTableChangedEvent()" (experimentChanged)="experimentChangedEvent()" [experiment]="experiment" [dataset]="dataset"></app-column-table> + <app-column-table (columnTableChanged)="columnTableChangedEvent()" (experimentChanged)="experimentChangedEvent()" [experiment]="experiment" [dataset]="dataset"></app-column-table> </div> </div> <div #steps id="step_3" class="step-content" *ngIf="step2"> <div class="step-content-inside"> - <app-folder #folderModel [type]="FolderType.Model" [forExperiment]="experiment" [startingTab]="TabType.NewFile" [tabsToShow]="[TabType.MyModels]" (okPressed)="goToPage(3); trainModel();" (selectedFileChanged)="setModel($event)"></app-folder> + <div id="compareButton"> + <button mat-raised-button color="accent" *ngIf="!comparing" (click)="toggleCompare()"><mat-icon>compare</mat-icon> Dodaj konfiguraciju za upoređivanje</button> + <button mat-raised-button *ngIf="comparing" (click)="toggleCompare()"><mat-icon>not_interested</mat-icon> Prekini upoređivanje</button> + </div> + <div class="side-by-side"> + <app-folder #folderModel [type]="FolderType.Model" [forExperiment]="experiment" [startingTab]="TabType.NewFile" [tabsToShow]="[TabType.MyModels]" (okPressed)="trainModel();" (selectedFileChanged)="setModel($event);"></app-folder> + <app-folder #folderModelCompare [type]="FolderType.Model" [forExperiment]="experiment" [startingTab]="TabType.MyModels" [tabsToShow]="[TabType.MyModels]" (okPressed)=" trainModelCmp();" (selectedFileChanged)="setModelCmp($event);" style="width: 50%;" + *ngIf="comparing"></app-folder> + </div> </div> </div> <div #steps id="step_4" class="step-content" *ngIf="step3"> <div class="step-content-inside"> - <app-metric-view #metricView></app-metric-view> + <div class="side-by-side"> + <app-line-chart #linechart [experiment]="experiment" [predictor]="predictor!"></app-line-chart> + <app-line-chart #linechartCompare [experiment]="experiment" style="width: 50%;" *ngIf="comparing"></app-line-chart> + </div> </div> </div> + <!-- + <div #steps id="step_5" class="step-content" *ngIf="step4"> + <div class="step-content-inside"> + <app-form-predictor></app-form-predictor> + </div> + </div>--> </div> </div>
\ No newline at end of file diff --git a/frontend/src/app/_pages/experiment/experiment.component.ts b/frontend/src/app/_pages/experiment/experiment.component.ts index ec4275fa..e4d444b2 100644 --- a/frontend/src/app/_pages/experiment/experiment.component.ts +++ b/frontend/src/app/_pages/experiment/experiment.component.ts @@ -15,6 +15,8 @@ import { MetricViewComponent } from 'src/app/_elements/metric-view/metric-view.c import { ActivatedRoute, Router } from '@angular/router'; import { DatasetsService } from 'src/app/_services/datasets.service'; import { PredictorsService } from 'src/app/_services/predictors.service'; +import { LineChartComponent } from 'src/app/_elements/_charts/line-chart/line-chart.component'; +import Predictor from 'src/app/_data/Predictor'; @Component({ selector: 'app-experiment', @@ -30,16 +32,32 @@ export class ExperimentComponent implements AfterViewInit { event: number = 0; experiment: Experiment; dataset?: Dataset; + predictor?: Predictor; + @ViewChild("folderDataset") folderDataset!: FolderComponent; @ViewChild(ColumnTableComponent) columnTable!: ColumnTableComponent; @ViewChild("folderModel") folderModel!: FolderComponent; - @ViewChild("metricView") metricView!: MetricViewComponent; + @ViewChild("folderModelCompare") folderModelCmp!: FolderComponent; + @ViewChild("linechart") linechartComponent!: LineChartComponent; + @ViewChild("linechartCompare") linechartComponentCmp!: LineChartComponent; step1: boolean = false; step2: boolean = false; step3: boolean = false; step4: boolean = false; + comparing: boolean = false; + + toggleCompare() { + this.comparing = !this.comparing; + setTimeout(() => { + if (this.folderModel.formModel) + this.folderModel.formModel.graph.resize(); + if (this.folderModel.formNewModel) + this.folderModel.formNewModel.graph.resize(); + }); + } + constructor(private experimentsService: ExperimentsService, private modelsService: ModelsService, private datasetsService: DatasetsService, private predictorsService: PredictorsService, private signalRService: SignalRService, private route: ActivatedRoute) { this.experiment = new Experiment("exp1"); } @@ -57,7 +75,22 @@ export class ExperimentComponent implements AfterViewInit { Shared.openDialog('Greška', 'Morate odabrati konfiguraciju neuronske mreže'); } else { this.modelsService.trainModel(this.modelToTrain._id, this.experiment._id).subscribe(() => { console.log("pocelo treniranje") }); - this.step4 = true; + this.step3 = true; + setTimeout(() => { + this.goToPage(3); + }); + } + } + + trainModelCmp() { + if (!this.modelToTrainCmp) { + Shared.openDialog('Greška', 'Morate odabrati konfiguraciju neuronske mreže'); + } else { + this.modelsService.trainModel(this.modelToTrainCmp._id, this.experiment._id).subscribe(() => { console.log("pocelo treniranje") }); + this.step3 = true; + setTimeout(() => { + this.goToPage(3); + }); } } @@ -83,16 +116,48 @@ export class ExperimentComponent implements AfterViewInit { if (this.signalRService.hubConnection) { this.signalRService.hubConnection.on("NotifyEpoch", (mName: string, mId: string, stat: string, totalEpochs: number, currentEpoch: number) => { - if (currentEpoch == 0) { - this.history = []; - } + if (this.modelToTrain?._id == mId) { + if (currentEpoch == 0) { + this.linechartComponent.setName(mName); + this.history = []; + } + stat = stat.replace(/'/g, '"'); this.history.push(JSON.parse(stat)); - this.metricView.update(this.history,this.modelToTrain.epochs); + + this.linechartComponent.updateAll(this.history, this.modelToTrain.epochs); + } + + if (this.modelToTrainCmp?._id == mId) { + if (currentEpoch == 0) { + this.linechartComponentCmp.setName(mName); + this.historyCmp = []; + } + + stat = stat.replace(/'/g, '"'); + + this.historyCmp.push(JSON.parse(stat)); + this.linechartComponentCmp.updateAll(this.historyCmp, this.modelToTrainCmp.epochs); } }); + this.signalRService.hubConnection.on("NotifyPredictor", (pId: string, mId: string) => { + console.log("Predictor trained: ", pId, "for model:", mId); + + if (this.modelToTrain && mId == this.modelToTrain._id) { + this.predictorsService.getPredictor(pId).subscribe((predictor) => { + this.linechartComponent.predictor = predictor; + }); + } + + if (this.modelToTrainCmp && mId == this.modelToTrainCmp._id) { + this.predictorsService.getPredictor(pId).subscribe((predictor) => { + this.linechartComponentCmp.predictor = predictor; + }); + } + }) + } this.route.queryParams.subscribe(params => { @@ -102,6 +167,7 @@ export class ExperimentComponent implements AfterViewInit { if (predictorId != null) { this.predictorsService.getPredictor(predictorId!).subscribe((response) => { let predictor = response; + this.predictor = predictor; this.experimentsService.getExperimentById(predictor.experimentId).subscribe((response) => { this.experiment = response; this.datasetsService.getDatasetById(this.experiment.datasetId).subscribe((response: Dataset) => { @@ -111,11 +177,13 @@ export class ExperimentComponent implements AfterViewInit { this.modelsService.getModelById(predictor.modelId).subscribe((response) => { let model = response; - this.folderModel.formModel.newModel = model; + this.folderModel.selectFile(model); + //this.folderModel.formModel.newModel = model; this.step3 = true; - let numOfEpochsArray = Array.from({length: model.epochs}, (_, i) => i + 1); + let numOfEpochsArray = Array.from({ length: model.epochs }, (_, i) => i + 1); setTimeout(() => { - this.metricView.linechartComponent.update(numOfEpochsArray, predictor.metricsAcc, predictor.metricsLoss, predictor.metricsMae, predictor.metricsMse, predictor.metricsValAcc, predictor.metricsValLoss, predictor.metricsValMae, predictor.metricsValMse); + this.linechartComponent.update(numOfEpochsArray, predictor.metricsAcc, predictor.metricsLoss, predictor.metricsMae, predictor.metricsMse, predictor.metricsValAcc, predictor.metricsValLoss, predictor.metricsValMae, predictor.metricsValMse); + this.goToPage(3); }) }); }); @@ -129,6 +197,7 @@ export class ExperimentComponent implements AfterViewInit { this.dataset = response; this.folderDataset.forExperiment = this.experiment; this.folderDataset.selectFile(this.dataset); + this.goToPage(1); }); }); } @@ -137,6 +206,7 @@ export class ExperimentComponent implements AfterViewInit { } history: any[] = []; + historyCmp: any[] = []; updatePageIfScrolled() { if (this.scrolling) return; @@ -197,14 +267,18 @@ export class ExperimentComponent implements AfterViewInit { this.step2 = true; setTimeout(() => { this.folderModel.updateExperiment(); + this.folderModel.selectFile(undefined); + this.folderModel.selectTab(TabType.NewFile); + this.goToPage(2); }); } setDataset(dataset: FolderFile | null) { - if (dataset == null) { + if (dataset == null ||dataset==undefined) { this.columnTable.loaded = false; this.dataset = undefined; this.experiment.datasetId = ''; + this.step1=false; return; } const d = <Dataset>dataset; @@ -215,13 +289,21 @@ export class ExperimentComponent implements AfterViewInit { setTimeout(() => { this.columnTable.loadDataset(d); }); + // REFRESH GRAFIKA (4. KORAKA) URADITI } modelToTrain?: Model; + modelToTrainCmp?: Model; setModel(model: FolderFile) { const m = <Model>model; this.modelToTrain = m; - this.step3 = true; + //this.step3 = true; + } + + setModelCmp(model: FolderFile) { + const m = <Model>model; + this.modelToTrainCmp = m; + //this.step3 = true; } } diff --git a/frontend/src/app/_pages/page-dataset/page-dataset.component.css b/frontend/src/app/_pages/page-dataset/page-dataset.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/page-dataset/page-dataset.component.css diff --git a/frontend/src/app/_pages/page-dataset/page-dataset.component.html b/frontend/src/app/_pages/page-dataset/page-dataset.component.html new file mode 100644 index 00000000..2357a656 --- /dev/null +++ b/frontend/src/app/_pages/page-dataset/page-dataset.component.html @@ -0,0 +1,4 @@ +<div class="force-centered" style="color: var(--offwhite);"> + <app-form-dataset [disableAll]="true"></app-form-dataset> + <button mat-raised-button class="m-3 p-2" (click)="import()">Uvezi</button> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_pages/page-dataset/page-dataset.component.spec.ts b/frontend/src/app/_pages/page-dataset/page-dataset.component.spec.ts new file mode 100644 index 00000000..2c961cd5 --- /dev/null +++ b/frontend/src/app/_pages/page-dataset/page-dataset.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PageDatasetComponent } from './page-dataset.component'; + +describe('PageDatasetComponent', () => { + let component: PageDatasetComponent; + let fixture: ComponentFixture<PageDatasetComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PageDatasetComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PageDatasetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/page-dataset/page-dataset.component.ts b/frontend/src/app/_pages/page-dataset/page-dataset.component.ts new file mode 100644 index 00000000..b6d0c2a4 --- /dev/null +++ b/frontend/src/app/_pages/page-dataset/page-dataset.component.ts @@ -0,0 +1,45 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import Shared from 'src/app/Shared'; +import Dataset from 'src/app/_data/Dataset'; +import { FormDatasetComponent } from 'src/app/_elements/form-dataset/form-dataset.component'; +import { DatasetsService } from 'src/app/_services/datasets.service'; + +@Component({ + selector: 'app-page-dataset', + templateUrl: './page-dataset.component.html', + styleUrls: ['./page-dataset.component.css'] +}) +export class PageDatasetComponent implements OnInit { + + @ViewChild(FormDatasetComponent) formDataset!: FormDatasetComponent; + + constructor(private route: ActivatedRoute, private router: Router, private datasetsService: DatasetsService) { } + + ngOnInit(): void { + this.route.queryParams.subscribe(params => { + let id = this.route.snapshot.paramMap.get("id"); + if (id) { + this.datasetsService.getDatasetById(id).subscribe((dataset) => { + this.formDataset.dataset = dataset; + this.formDataset.loadExisting(); + }); + } else { + this.router.navigate(['']); + } + }); + } + + import() { + this.formDataset.dataset._id = ""; + this.formDataset.dataset.isPreProcess = true; + this.formDataset.dataset.isPublic = false; + this.datasetsService.stealDataset(this.formDataset.dataset).subscribe((response) => { + Shared.openDialog("Obaveštenje", "Uspešno ste dodali javni izvor podataka u vašu kolekciju."); + }, (error: any) => { + if (error.error == "Dataset with this name already exists") { + Shared.openDialog("Obaveštenje", "Izvor podataka sa ovim imenom postoji u vašoj kolekciji."); + } + }); + } +} diff --git a/frontend/src/app/_pages/page-model/page-model.component.css b/frontend/src/app/_pages/page-model/page-model.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_pages/page-model/page-model.component.css diff --git a/frontend/src/app/_pages/page-model/page-model.component.html b/frontend/src/app/_pages/page-model/page-model.component.html new file mode 100644 index 00000000..0ac8aaae --- /dev/null +++ b/frontend/src/app/_pages/page-model/page-model.component.html @@ -0,0 +1,6 @@ +<div class="force-centered" style="color: var(--offwhite);"> + <div> + {{JSON.stringify(model)}} + </div> + <button mat-raised-button class="m-3 p-2" (click)="import()">Uvezi</button> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_pages/page-model/page-model.component.spec.ts b/frontend/src/app/_pages/page-model/page-model.component.spec.ts new file mode 100644 index 00000000..e235de5c --- /dev/null +++ b/frontend/src/app/_pages/page-model/page-model.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PageModelComponent } from './page-model.component'; + +describe('PageModelComponent', () => { + let component: PageModelComponent; + let fixture: ComponentFixture<PageModelComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ PageModelComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PageModelComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_pages/page-model/page-model.component.ts b/frontend/src/app/_pages/page-model/page-model.component.ts new file mode 100644 index 00000000..0ccd0f9a --- /dev/null +++ b/frontend/src/app/_pages/page-model/page-model.component.ts @@ -0,0 +1,41 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import Shared from 'src/app/Shared'; +import Experiment from 'src/app/_data/Experiment'; +import Model from 'src/app/_data/Model'; +import { FormModelComponent } from 'src/app/_elements/form-model/form-model.component'; +import { ModelsService } from 'src/app/_services/models.service'; + +@Component({ + selector: 'app-page-model', + templateUrl: './page-model.component.html', + styleUrls: ['./page-model.component.css'] +}) +export class PageModelComponent implements OnInit { + + constructor(private route: ActivatedRoute, private router: Router, private modelsService: ModelsService) { } + model!:Model; + ngOnInit(): void { + this.route.queryParams.subscribe(params => { + let id = this.route.snapshot.paramMap.get("id"); + if (id) { + this.modelsService.getModelById(id).subscribe((model) => { + this.model = model; + }); + } else { + this.router.navigate(['']); + } + }); + } + + import() { + this.model._id = ""; + this.model.isPublic = false; + this.modelsService.stealModel(this.model).subscribe((response) => { + Shared.openDialog("Obaveštenje", "Uspešno ste dodali javnu konfiguraciju neuronske mreže u vašu kolekciju."); + }, (error: any) => { + Shared.openDialog("Obaveštenje", "Konfiguracija neuronske mreže sa ovim imenom postoji u vašoj kolekciji."); + }); + } + JSON=JSON; +} |