aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/app/_elements
diff options
context:
space:
mode:
authorDanijel Andjelkovic <adanijel99@gmail.com>2022-04-20 00:12:42 +0000
committerDanijel Andjelkovic <adanijel99@gmail.com>2022-04-20 00:12:42 +0000
commitb814ef17d31dca80a3f23b3fbe4ce56885192a4c (patch)
treed7a297db46d57267b5516a8c20ee906dd39571ed /frontend/src/app/_elements
parent9a480b28ac9b93dee082925b9cb4beef3244b135 (diff)
parente6d9e3fd2dcf83c90db8560e749544dfd9910d07 (diff)
Merge branch 'dev' into 'master'
Merge master See merge request igrannonica/neuronstellar!27
Diffstat (limited to 'frontend/src/app/_elements')
-rw-r--r--frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html50
-rw-r--r--frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts11
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.html30
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.ts37
-rw-r--r--frontend/src/app/_elements/item-predictor/item-predictor.component.html35
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.css0
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.html5
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.ts88
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.css0
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.html5
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.ts49
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.html76
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.ts48
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.html34
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.ts8
-rw-r--r--frontend/src/app/_elements/notifications/notifications.component.html16
-rw-r--r--frontend/src/app/_elements/notifications/notifications.component.ts26
19 files changed, 415 insertions, 153 deletions
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html
index bff8b022..e5d4cd23 100644
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html
+++ b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html
@@ -2,48 +2,54 @@
<div class="col-2">
</div>
<div class="col-3">
- <label for="name" class="col-form-label">Naziv dataseta:</label>
- <input type="text" class="form-control mb-1" name="name" placeholder="Naziv..." [(ngModel)]="dataset.name">
+ <label for="name" class="col-form-label">Naziv dataseta:</label>
+ <input type="text" class="form-control mb-1" name="name" placeholder="Naziv..." [(ngModel)]="dataset.name">
- <label for="desc" class="col-sm-2 col-form-label">Opis:</label>
- <div>
- <textarea class="form-control" name="desc" rows="3" [(ngModel)]="dataset.description"></textarea>
- </div>
+ <label for="desc" class="col-sm-2 col-form-label">Opis:</label>
+ <div>
+ <textarea class="form-control" name="desc" rows="3" [(ngModel)]="dataset.description"></textarea>
+ </div>
- <label for="checkboxIsPublic" class="form-check-label mt-3 mb-1">Želite li da dataset bude javan?
+ <label for="checkboxIsPublic" class="form-check-label mt-3 mb-1">Želite li da dataset bude javan?
<input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="dataset.isPublic" (change)="checkAccessible()" type="checkbox"
value="" id="checkboxIsPublic">
- </label>
-
- <label for="checkboxAccessibleByLink" class="form-check-label">Želite li da bude deljiv linkom? &nbsp;
+ </label>
+
+ <label for="checkboxAccessibleByLink" class="form-check-label">Želite li da bude deljiv linkom? &nbsp;
<input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="dataset.accessibleByLink" type="checkbox"
value="" id="checkboxAccessibleByLink">
</label>
</div>
<div class="col-1">
</div>
- <div class="col-4 mt-4">
+ <div class="col-3 mt-4">
- <input list="delimiterOptions" placeholder="Izaberite ili ukucajte delimiter za .csv fajl" class="form-control mt-2"
- [(ngModel)]="dataset.delimiter" (input)="update()">
- <datalist id="delimiterOptions">
+ <label for="type" class="col-form-label">Izaberite delimiter za .csv fajl</label>
+ <select id="delimiterOptions" class="form-select" name="type" [(ngModel)]="dataset.delimiter" (change)="update()">
+ <option
+ *ngFor="let option of delimiterOptions">
+ {{ option }}
+ </option>
+ </select>
+ <!--
+ <input list="delimiterOptions" placeholder="Izaberite ili ukucajte delimiter za .csv fajl" class="form-control mt-2" [(ngModel)]="dataset.delimiter" (input)="update()">
+ <datalist id="delimiterOptions">
<option *ngFor="let option of delimiterOptions">{{option}}</option>
</datalist>
-
- <label for="type" class="form-check-label my-5">Da li .csv ima header?
+-->
+ <label for="type" class="form-check-label my-5">Da li .csv ima header?
<input class="mx-3 form-check-input" type="checkbox" (input)="update()" [(ngModel)]="dataset.hasHeader" type="checkbox"
value="" id="checkboxHeader" checked>
</label>
- <br>
- <input id="fileInput" class="form-control" type="file" class="upload" (change)="changeListener($event)"
- accept=".csv">
+ <br>
+ <input id="fileInput" class="form-control btn-lg" type="file" class="upload" (change)="changeListener($event)" accept=".csv">
</div>
</div>
<div class="px-5 mt-5">
- <app-datatable [tableData]="tableData"></app-datatable>
+ <app-datatable [tableData]="tableData"></app-datatable>
</div>
<div class="d-flex flex-row align-items-center justify-content-center w-100 my-2">
- <button (click)="uploadDataset()" class="btn btn-lg col-4" style="background-color:#003459; color:white;">Dodaj izvor podataka</button>
-</div>
+ <button (click)="uploadDataset()" class="btn btn-lg col-4" style="background-color:#003459; color:white;">Dodaj izvor podataka</button>
+</div> \ 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..1f395105 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
@@ -13,10 +13,9 @@ import { CsvParseService } from 'src/app/_services/csv-parse.service';
})
export class AddNewDatasetComponent {
- @Output() newDatasetAdded = new EventEmitter<string>();
@ViewChild(DatatableComponent) datatable!: DatatableComponent;
- delimiterOptions: Array<string> = [",", ";", "\t", "razmak", "|"]; //podrazumevano ","
+ delimiterOptions: Array<string> = [",", ";", "|", "razmak", "novi red"]; //podrazumevano ","
csvRecords: any[] = [];
files: File[] = [];
@@ -29,6 +28,7 @@ export class AddNewDatasetComponent {
constructor(private modelsService: ModelsService, private datasetsService: DatasetsService, private csv: CsvParseService) {
this.dataset = new Dataset();
+ this.dataset.delimiter = ',';
}
//@ViewChild('fileImportInput', { static: false }) fileImportInput: any; cemu je ovo sluzilo?
@@ -36,8 +36,6 @@ export class AddNewDatasetComponent {
changeListener($event: any): void {
this.files = $event.srcElement.files;
if (this.files.length == 0 || this.files[0] == null) {
- //console.log("NEMA FAJLA");
- //this.loaded.emit("not loaded");
this.tableData.hasInput = false;
return;
}
@@ -56,7 +54,7 @@ export class AddNewDatasetComponent {
const fileReader = new FileReader();
fileReader.onload = (e) => {
if (typeof fileReader.result === 'string') {
- const result = this.csv.csvToArray(fileReader.result, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter)
+ const result = this.csv.csvToArray(fileReader.result, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter)
if (this.dataset.hasHeader)
this.csvRecords = result.splice(0, 11);
@@ -90,10 +88,9 @@ 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");
shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira.");
}, (error) => {
shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset.");
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.html b/frontend/src/app/_elements/dataset-load/dataset-load.component.html
index 56a3b3c9..f244a882 100644
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.html
+++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.html
@@ -1,40 +1,34 @@
<div>
- <!--Sklonjeno ucitavanje novog dataseta i sve opcije u vezi sa tim, premesteno u add-new-dataset-->
+ <!--Sklonjeno ucitavanje novog dataseta i sve opcije u vezi sa tim, premesteno u add-new-dataset-->
<div class="d-flex flex-row justify-content-center align-items-center mt-3 mb-5">
- <button type="button" id="btnMyDataset" class="btn" (click)="viewMyDatasetsForm()"
- [ngClass]="{'btnType1': showMyDatasets, 'btnType2': !showMyDatasets}">
+ <button type="button" id="btnMyDataset" class="btn" (click)="viewMyDatasetsForm()" [ngClass]="{'btnType1': showMyDatasets, 'btnType2': !showMyDatasets}">
Izaberite dataset iz kolekcije
</button>
<h3 class="mt-3 mx-3">ili</h3>
- <button type="button" id="btnNewDataset" class="btn" (click)="viewNewDatasetForm()"
- [ngClass]="{'btnType1': !showMyDatasets, 'btnType2': showMyDatasets}">
+ <button type="button" id="btnNewDataset" class="btn" (click)="viewNewDatasetForm()" [ngClass]="{'btnType1': !showMyDatasets, 'btnType2': showMyDatasets}">
Dodajte novi dataset
</button>
</div>
- <div class="px-5 my-2">
- <input *ngIf="showMyDatasets" type="text" class="form-control" placeholder="Pretraga"
- [(ngModel)]="term">
- </div>
- <div class="px-5" *ngIf="showMyDatasets">
+ <div class="px-5 my-2">
+ <input *ngIf="showMyDatasets" type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term">
+ </div>
+ <div class="px-5" *ngIf="showMyDatasets">
<div class="overflow-auto" style="max-height: 500px;">
<ul class="list-group">
- <li class="list-group-item p-3" *ngFor="let dataset of myDatasets|filter:term"
- [ngClass]="{'selectedDatasetClass': this.selectedDataset == dataset}">
- <app-item-dataset name="usersDataset" [dataset]="dataset"
- (click)="selectThisDataset(dataset);"></app-item-dataset>
+ <li class="list-group-item p-3" *ngFor="let dataset of myDatasets|filter:term" [ngClass]="{'selectedDatasetClass': this.selectedDataset == dataset}">
+ <app-item-dataset name="usersDataset" [dataset]="dataset" (click)="selectThisDataset(dataset);"></app-item-dataset>
</li>
</ul>
</div>
<div class="px-5 mt-5">
<app-datatable [tableData]="tableData"></app-datatable>
</div>
- </div>
+ </div>
- <app-add-new-dataset [style]="(showMyDatasets)?'display:none;visibility:hidden;':''" id="dataset"
- (newDatasetAdded)="refreshMyDatasets()">
- </app-add-new-dataset>
+ <app-add-new-dataset [style]="(showMyDatasets)?'display:none;visibility:hidden;':''" id="dataset">
+ </app-add-new-dataset>
</div> \ No newline at end of file
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..be1dc097 100644
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
+++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
@@ -8,6 +8,7 @@ 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';
+import { AuthService } from 'src/app/_services/auth.service';
@Component({
selector: 'app-dataset-load',
@@ -33,7 +34,15 @@ export class DatasetLoadComponent implements OnInit {
term: string = "";
- constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService) {
+ constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService, private authService: AuthService) {
+ this.fetchDatasets();
+
+ authService.loggedInEvent.subscribe(_ => {
+ this.fetchDatasets();
+ })
+ }
+
+ fetchDatasets() {
this.datasets.getMyDatasets().subscribe((datasets) => {
this.myDatasets = datasets;
});
@@ -41,22 +50,17 @@ 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
}
- refreshMyDatasets() {
- this.datasets.getMyDatasets().subscribe((datasets) => {
- this.myDatasets = datasets;
- this.showMyDatasets = true;
- });
- }
-
selectThisDataset(dataset: Dataset) {
this.selectedDataset = dataset;
this.selectedDatasetLoaded = false;
@@ -86,10 +90,19 @@ export class DatasetLoadComponent implements OnInit {
return true;
}
+ refreshMyDatasets(selectedDatasetId: string | null) {
+ this.datasets.getMyDatasets().subscribe((datasets) => {
+ this.myDatasets = datasets.reverse();
+ this.showMyDatasets = true;
+ this.selectedDataset = this.myDatasets.filter(x => x._id == selectedDatasetId)[0];
+ this.resetSelectedDataset();
+ });
+ }
+
ngOnInit(): void {
if (this.signalRService.hubConnection) {
- this.signalRService.hubConnection.on("NotifyDataset", _ => {
- this.refreshMyDatasets();
+ this.signalRService.hubConnection.on("NotifyDataset", (dName: string, dId: string) => {
+ this.refreshMyDatasets(dId);
});
} else {
console.warn("Dataset-Load: No connection!");
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/line-chart/line-chart.component.css b/frontend/src/app/_elements/line-chart/line-chart.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_elements/line-chart/line-chart.component.css
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.html b/frontend/src/app/_elements/line-chart/line-chart.component.html
new file mode 100644
index 00000000..c8f406f4
--- /dev/null
+++ b/frontend/src/app/_elements/line-chart/line-chart.component.html
@@ -0,0 +1,5 @@
+<div class="chart-wrapper">
+ <canvas id="myChart">
+
+ </canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.spec.ts b/frontend/src/app/_elements/line-chart/line-chart.component.spec.ts
new file mode 100644
index 00000000..0c5e7ef5
--- /dev/null
+++ b/frontend/src/app/_elements/line-chart/line-chart.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LineChartComponent } from './line-chart.component';
+
+describe('LineChartComponent', () => {
+ let component: LineChartComponent;
+ let fixture: ComponentFixture<LineChartComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ LineChartComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LineChartComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.ts b/frontend/src/app/_elements/line-chart/line-chart.component.ts
new file mode 100644
index 00000000..1a8579a0
--- /dev/null
+++ b/frontend/src/app/_elements/line-chart/line-chart.component.ts
@@ -0,0 +1,88 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { Chart } from 'chart.js';
+
+@Component({
+ selector: 'app-line-chart',
+ templateUrl: './line-chart.component.html',
+ styleUrls: ['./line-chart.component.css']
+})
+
+export class LineChartComponent implements OnInit {
+
+ dataAcc: number[] = [];
+ dataMAE: number[] = [];
+ dataMSE: number[] = [];
+ dataLOSS: 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[]) {
+ this.dataAcc.length = 0;
+ this.dataAcc.push(...myAcc);
+
+ this.dataEpoch.length = 0;
+ this.dataEpoch.push(...myEpochs);
+
+ this.dataMAE.length = 0;
+ this.dataMAE.push(...myMae);
+
+ this.dataLOSS.length = 0;
+ this.dataLOSS.push(...myLoss);
+
+ this.dataMSE.length = 0;
+ this.dataMSE.push(...myMse);
+
+ this.myChart.update();
+ }
+
+ ngOnInit(): void {
+ this.myChart = new Chart("myChart",
+ {
+ type: 'line',
+ data: {
+ labels: this.dataEpoch,
+ datasets: [{
+ label: 'Accuracy',
+ data: this.dataAcc,
+ borderWidth: 1
+ },
+ {
+ label: 'Loss',
+ data: this.dataLOSS,
+ borderWidth: 1
+ },
+ {
+ label: 'MAE',
+ data: this.dataMAE,
+ borderWidth: 1
+ },
+ {
+ label: 'MSE',
+ data: this.dataMSE,
+ borderWidth: 1
+ }
+ ]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ }
+ );
+ }
+}
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.css b/frontend/src/app/_elements/metric-view/metric-view.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.css
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.html b/frontend/src/app/_elements/metric-view/metric-view.component.html
new file mode 100644
index 00000000..e7a4c547
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.html
@@ -0,0 +1,5 @@
+<div>
+ <app-line-chart>
+
+ </app-line-chart>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.spec.ts b/frontend/src/app/_elements/metric-view/metric-view.component.spec.ts
new file mode 100644
index 00000000..c3ecc67f
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MetricViewComponent } from './metric-view.component';
+
+describe('MetricViewComponent', () => {
+ let component: MetricViewComponent;
+ let fixture: ComponentFixture<MetricViewComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ MetricViewComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MetricViewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.ts b/frontend/src/app/_elements/metric-view/metric-view.component.ts
new file mode 100644
index 00000000..9193a0e5
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.ts
@@ -0,0 +1,49 @@
+import { Component, Input, OnInit, ViewChild } from '@angular/core';
+import { SignalRService } from 'src/app/_services/signal-r.service';
+import { LineChartComponent } from '../line-chart/line-chart.component';
+@Component({
+ selector: 'app-metric-view',
+ templateUrl: './metric-view.component.html',
+ styleUrls: ['./metric-view.component.css']
+})
+export class MetricViewComponent implements OnInit {
+ @ViewChild(LineChartComponent) linechartComponent!: LineChartComponent;
+
+ @Input() history!: any[];
+
+ constructor(private signalRService: SignalRService) { }
+
+ ngOnInit(): void {
+ }
+
+ update(history: any[]) {
+ const myAcc: number[] = [];
+ const myMae: number[] = [];
+ const myMse: number[] = [];
+ const myLoss: number[] = [];
+
+ const myEpochs: number[] = [];
+ this.history = history;
+ this.history.forEach((metrics, epoch) => {
+ myEpochs.push(epoch + 1);
+ for (let key in metrics) {
+ let value = metrics[key];
+ console.log(key, ':::', value, epoch);
+ if (key === 'accuracy') {
+ myAcc.push(parseFloat(value));
+ }
+ else if (key === 'loss') {
+ myLoss.push(parseFloat(value));
+ }
+ else if (key === 'mae') {
+ myMae.push(parseFloat(value));
+ }
+ else if (key === 'mse') {
+ myMse.push(parseFloat(value));
+ }
+ }
+ });
+
+ this.linechartComponent.update(myEpochs, myAcc, myLoss, myMae, myMse);
+ }
+} \ 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..1f9852d1 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.html
+++ b/frontend/src/app/_elements/model-load/model-load.component.html
@@ -1,12 +1,10 @@
<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}">
+ <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}">
+ <button type="button" id="btnNewModel" class="btn" (click)="viewNewModelForm()" [ngClass]="{'btnType1': !showMyModels, 'btnType2': showMyModels}">
Dodajte novi model
</button>
</div>
@@ -17,8 +15,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"
- [ngClass]="{'selectedModelClass': this.selectedModel == model}">
+ <li class="list-group-item p-3" *ngFor="let model of myModels|filter:((forExperiment != undefined) ? forExperiment.type : '')" [ngClass]="{'selectedModelClass': this.selectedModel == model}">
<app-item-model name="usersModel" [model]="model" (click)="selectThisModel(model);">
</app-item-model>
</li>
@@ -43,11 +40,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> &nbsp;&nbsp;
- <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,8 +51,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"
- (change)="filterOptions()">
+ <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">
@@ -72,10 +64,7 @@
<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()">
+ <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>
@@ -86,7 +75,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">
@@ -100,19 +89,17 @@
<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()">
+ <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 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"
- [(ngModel)]="newModel.lossFunction" aria-checked="true">
+ <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">
@@ -122,10 +109,22 @@
</div>
<div class="col-1"></div>
<div class="col-3">
- <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label>
+ <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji:&nbsp;<b>{{newModel.batchSize}}</b><br>(izaberite stepen dvojke)</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()">
+
+ </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>
@@ -138,8 +137,7 @@
<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>
+ <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">
@@ -147,8 +145,7 @@
<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">
@@ -160,12 +157,10 @@
</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>
+ <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-control"
- name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction">
+ <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">
@@ -178,26 +173,23 @@
</div>
</div>
- <div class="form-check form-check-inline overflow-auto m-4" style="width: max-content;">
+ <!--<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">
+ <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>
+ <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>-->
+
<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
+ <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="uploadModel();">Sačuvaj
model</button>
<div class="col"></div>
</div>
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..fb4b3fd0 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.ts
+++ b/frontend/src/app/_elements/model-load/model-load.component.ts
@@ -1,7 +1,10 @@
-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 { AuthService } from 'src/app/_services/auth.service';
import { ModelsService } from 'src/app/_services/models.service';
+import { SignalRService } from 'src/app/_services/signal-r.service';
import { GraphComponent } from '../graph/graph.component';
@@ -13,6 +16,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();
@@ -29,21 +33,44 @@ export class ModelLoadComponent implements OnInit {
shared = Shared;
term: string = "";
- selectedProblemType: string = '';
selectedMetrics = [];
lossFunction: any = LossFunction;
showMyModels: boolean = true;
- constructor(private modelsService: ModelsService) {
+ batchSizePower: number = 2;
+
+ constructor(private modelsService: ModelsService, private authService: AuthService) {
+ //console.log("forExperiment = ", this.forExperiment);
+ this.fetchModels();
+
+ this.authService.loggedInEvent.subscribe(_ => {
+ this.fetchModels();
+ })
+ }
+
+ fetchModels(andSelectWithId: string | null = '') {
+ //if (this.forExperiment == undefined) {
this.modelsService.getMyModels().subscribe((models) => {
- this.myModels = models;
+ this.myModels = models.reverse();
+ this.selectThisModel(this.myModels.filter(x => x._id == andSelectWithId)[0]);
});
+ /*}
+ else {
+ this.modelsService.getMyModelsByType(ProblemType.Regression).subscribe((models) => {
+ this.myModels = models;
+ //console.log("modeli po tipu: ", this.myModels);
+ });
+ }*/
}
ngOnInit(): void {
}
+ updateBatchSize() {
+ this.newModel.batchSize = 2 ** this.batchSizePower;
+ }
+
updateGraph() {
this.graph.update();
}
@@ -62,12 +89,17 @@ 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.');
- // treba da se selektuje nov model u listi modela
- //this.selectedModel =
+ console.log(this.newModel);
+ //Shared.openDialog('Model dodat', 'Model je uspešno dodat u bazu.');
+
+ Shared.openYesNoDialog("Model dodat", "Model je uspešno dodat u bazu. Da li želite da nastavite treniranje sa dodatim modelom?", () => {
+ this.fetchModels(response._id);
+ this.showMyModels = true;
+ });
+ this.fetchModels();
}, (error) => {
Shared.openDialog('Greška', 'Model sa unetim nazivom već postoji u Vašoj kolekciji. Promenite naziv modela i nastavite sa kreiranim datasetom.');
});
diff --git a/frontend/src/app/_elements/navbar/navbar.component.html b/frontend/src/app/_elements/navbar/navbar.component.html
index 7d0c4cd8..1988b834 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.html
+++ b/frontend/src/app/_elements/navbar/navbar.component.html
@@ -6,31 +6,25 @@
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
- <li><a routerLink="" class="nav-link px-2"
- [class]="(currentUrl === '') ? 'text-secondary' : 'text-white'">Početna</a></li>
- <li><a routerLink="experiment" class="nav-link px-2"
- [class]="(currentUrl === '/experiment') ? 'text-secondary' : 'text-white'">Napravi eksperiment</a>
+ <li><a routerLink="" class="nav-link px-2" [class]="(currentUrl === '') ? 'text-secondary' : 'text-white'">Početna</a></li>
+ <li><a routerLink="experiment" class="nav-link px-2" [class]="(currentUrl === '/experiment') ? 'text-secondary' : 'text-white'">Napravi eksperiment</a>
</li>
- <li><a routerLink="training" class="nav-link px-2"
- [class]="(currentUrl === '/training') ? 'text-secondary' : 'text-white'">Treniraj model</a>
+ <li><a routerLink="training" class="nav-link px-2" [class]="(currentUrl === '/training') ? 'text-secondary' : 'text-white'">Treniraj model</a>
</li>
- <li><a routerLink="my-predictors" class="nav-link px-2"
- [class]="(currentUrl === '/my-predictors') ? 'text-secondary' : 'text-white' + (shared.loggedIn) ? '' : 'disabled'">Predvidi</a>
+ <li><a routerLink="my-predictors" class="nav-link px-2" [class]="(currentUrl === '/my-predictors') ? 'text-secondary' : 'text-white' + (shared.loggedIn) ? '' : 'disabled'">Predvidi</a>
</li>
</ul>
<div *ngIf="shared.loggedIn" class="dropdown text-end">
- <a href="#" class="d-block link-light text-decoration-none dropdown-toggle" id="dropdownUser1"
- data-bs-toggle="dropdown" aria-expanded="false">
- <img [src]="'/assets/profilePictures/'+ shared.photoId +'.png'" alt="mdo" width="32" height="32"
- class="rounded-circle">
+ <a href="#" class="d-block link-light text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
+ <img [src]="'/assets/profilePictures/'+ shared.photoId +'.png'" alt="mdo" width="32" height="32" class="rounded-circle">
</a>
- <ul class="dropdown-menu text-small" aria-labelledby="dropdownUser1"
- style="position: absolute; inset: 0px 0px auto auto; margin: 0px; transform: translate(0px, 34px);"
- data-popper-placement="bottom-end">
- <li><a class="dropdown-item" routerLink="add-model">Nov model...</a></li>
- <li><a class="dropdown-item" routerLink="settings">Podešavanja</a></li>
+ <ul class="dropdown-menu text-small" aria-labelledby="dropdownUser1" style="position: absolute; inset: 0px 0px auto auto; margin: 0px; transform: translate(0px, 34px);" data-popper-placement="bottom-end">
+ <li><a class="dropdown-item" routerLink="my-datasets">Moji izvori podataka</a></li>
+ <li><a class="dropdown-item" routerLink="my-models">Moji modeli</a></li>
+ <li><a class="dropdown-item" routerLink="my-predictors">Moji prediktori</a></li>
<li><a class="dropdown-item" routerLink="profile">Moj profil</a></li>
+ <li><a class="dropdown-item" routerLink="settings" disabled>Podešavanja</a></li>
<li>
<hr class="dropdown-divider">
</li>
@@ -38,12 +32,10 @@
</ul>
</div>
<div *ngIf="!shared.loggedIn" class="dropdown text-end">
- <button type="button" mat-raised-button color="primary" class="mx-2" data-bs-toggle="modal"
- data-bs-target="#modalForLogin">
+ <button type="button" mat-raised-button color="primary" class="mx-2" data-bs-toggle="modal" data-bs-target="#modalForLogin">
Prijavi se
</button>
- <button type="button" mat-raised-button color="primary" data-bs-toggle="modal"
- data-bs-target="#modalForRegister">
+ <button type="button" mat-raised-button color="primary" data-bs-toggle="modal" data-bs-target="#modalForRegister">
Registruj se
</button>
</div>
diff --git a/frontend/src/app/_elements/navbar/navbar.component.ts b/frontend/src/app/_elements/navbar/navbar.component.ts
index 368508ed..d5d1744f 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.ts
+++ b/frontend/src/app/_elements/navbar/navbar.component.ts
@@ -4,6 +4,7 @@ import { AuthService } from '../../_services/auth.service';
import shared from 'src/app/Shared';
import { UserInfoService } from 'src/app/_services/user-info.service';
import { MatDialog } from '@angular/material/dialog';
+import { SignalRService } from 'src/app/_services/signal-r.service';
@Component({
selector: 'app-navbar',
@@ -15,11 +16,15 @@ export class NavbarComponent implements OnInit {
currentUrl: string;
shared = shared;
- constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService, private matDialog: MatDialog) {
+ constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService, private matDialog: MatDialog, private signalRService: SignalRService) {
shared.dialog = matDialog;
this.currentUrl = this.location.path();
this.location.onUrlChange(() => {
this.currentUrl = this.location.path();
+ });
+
+ this.auth.loggedInEvent.subscribe(_ => {
+ this.signalRService.startConnection();
})
}
@@ -34,5 +39,6 @@ export class NavbarComponent implements OnInit {
logOut() {
this.auth.logOut();
+ this.signalRService.stopConnection();
}
}
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..f324662a 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);
+ }
+
}