aboutsummaryrefslogtreecommitdiff
path: root/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/app/Shared.ts12
-rw-r--r--frontend/src/app/_data/Dataset.ts37
-rw-r--r--frontend/src/app/_data/Experiment.ts27
-rw-r--r--frontend/src/app/_data/FolderFile.ts14
-rw-r--r--frontend/src/app/_data/Model.ts82
-rw-r--r--frontend/src/app/_elements/_charts/barchart/barchart.component.css (renamed from frontend/src/app/barchart/barchart.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/barchart/barchart.component.html (renamed from frontend/src/app/barchart/barchart.component.html)0
-rw-r--r--frontend/src/app/_elements/_charts/barchart/barchart.component.spec.ts (renamed from frontend/src/app/barchart/barchart.component.spec.ts)0
-rw-r--r--frontend/src/app/_elements/_charts/barchart/barchart.component.ts (renamed from frontend/src/app/barchart/barchart.component.ts)1
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.css (renamed from frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.html3
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.spec.ts (renamed from frontend/src/app/training/training.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts102
-rw-r--r--frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.css (renamed from frontend/src/app/_elements/carousel/carousel.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html1
-rw-r--r--frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.spec.ts (renamed from frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts37
-rw-r--r--frontend/src/app/_elements/_charts/heatmap/heatmap.component.css (renamed from frontend/src/app/_elements/item-experiment/item-experiment.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/heatmap/heatmap.component.html3
-rw-r--r--frontend/src/app/_elements/_charts/heatmap/heatmap.component.spec.ts (renamed from frontend/src/app/_pages/predict/predict.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/heatmap/heatmap.component.ts108
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.css (renamed from frontend/src/app/_elements/line-chart/line-chart.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.html3
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.spec.ts (renamed from frontend/src/app/_elements/line-chart/line-chart.component.spec.ts)0
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts (renamed from frontend/src/app/_elements/line-chart/line-chart.component.ts)25
-rw-r--r--frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.css (renamed from frontend/src/app/mixed-chart/mixed-chart.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.html (renamed from frontend/src/app/mixed-chart/mixed-chart.component.html)0
-rw-r--r--frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.spec.ts (renamed from frontend/src/app/mixed-chart/mixed-chart.component.spec.ts)0
-rw-r--r--frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.ts (renamed from frontend/src/app/mixed-chart/mixed-chart.component.ts)0
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.css (renamed from frontend/src/app/_elements/item-predictor/item-predictor.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html3
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.spec.ts (renamed from frontend/src/app/_pages/my-models/my-models.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts46
-rw-r--r--frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.css (renamed from frontend/src/app/point-linechart/point-linechart.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.html (renamed from frontend/src/app/point-linechart/point-linechart.component.html)0
-rw-r--r--frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.spec.ts (renamed from frontend/src/app/point-linechart/point-linechart.component.spec.ts)0
-rw-r--r--frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.ts (renamed from frontend/src/app/point-linechart/point-linechart.component.ts)0
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css4
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html4
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.spec.ts (renamed from frontend/src/app/scatterchart/scatterchart.component.spec.ts)0
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts (renamed from frontend/src/app/scatterchart/scatterchart.component.ts)21
-rw-r--r--frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html55
-rw-r--r--frontend/src/app/_elements/annvisual/annvisual.component.css4
-rw-r--r--frontend/src/app/_elements/annvisual/annvisual.component.html5
-rw-r--r--frontend/src/app/_elements/annvisual/annvisual.component.ts45
-rw-r--r--frontend/src/app/_elements/carousel/carousel.component.html17
-rw-r--r--frontend/src/app/_elements/carousel/carousel.component.ts18
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.css266
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.html249
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.spec.ts (renamed from frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.ts346
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.css18
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.html34
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.ts31
-rw-r--r--frontend/src/app/_elements/datatable/datatable.component.html44
-rw-r--r--frontend/src/app/_elements/datatable/datatable.component.ts1
-rw-r--r--frontend/src/app/_elements/folder/folder.component.css213
-rw-r--r--frontend/src/app/_elements/folder/folder.component.html111
-rw-r--r--frontend/src/app/_elements/folder/folder.component.spec.ts (renamed from frontend/src/app/_elements/carousel/carousel.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/folder/folder.component.ts379
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.css64
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.html65
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.spec.ts (renamed from frontend/src/app/_elements/dataset-load/dataset-load.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.ts (renamed from frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts)78
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.css104
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.html228
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.spec.ts (renamed from frontend/src/app/_elements/annvisual/annvisual.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.ts138
-rw-r--r--frontend/src/app/_elements/gradient-background/gradient-background.component.css (renamed from frontend/src/app/_pages/browse-datasets/browse-datasets.component.css)0
-rw-r--r--frontend/src/app/_elements/gradient-background/gradient-background.component.html1
-rw-r--r--frontend/src/app/_elements/gradient-background/gradient-background.component.spec.ts (renamed from frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/gradient-background/gradient-background.component.ts47
-rw-r--r--frontend/src/app/_elements/graph/graph.component.css20
-rw-r--r--frontend/src/app/_elements/graph/graph.component.html9
-rw-r--r--frontend/src/app/_elements/graph/graph.component.ts85
-rw-r--r--frontend/src/app/_elements/item-dataset/item-dataset.component.css23
-rw-r--r--frontend/src/app/_elements/item-dataset/item-dataset.component.html41
-rw-r--r--frontend/src/app/_elements/item-dataset/item-dataset.component.ts40
-rw-r--r--frontend/src/app/_elements/item-experiment/item-experiment.component.html10
-rw-r--r--frontend/src/app/_elements/item-experiment/item-experiment.component.ts15
-rw-r--r--frontend/src/app/_elements/item-model/item-model.component.css23
-rw-r--r--frontend/src/app/_elements/item-model/item-model.component.html58
-rw-r--r--frontend/src/app/_elements/item-model/item-model.component.ts33
-rw-r--r--frontend/src/app/_elements/item-predictor/item-predictor.component.html35
-rw-r--r--frontend/src/app/_elements/item-predictor/item-predictor.component.ts23
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.html5
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.css10
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.html11
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.ts12
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.css17
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.html68
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.ts47
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.css8
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.html22
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.ts13
-rw-r--r--frontend/src/app/_elements/notifications/notifications.component.ts1
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.css61
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.html19
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.ts49
-rw-r--r--frontend/src/app/_elements/reactive-background/reactive-background.component.html2
-rw-r--r--frontend/src/app/_elements/reactive-background/reactive-background.component.ts166
-rw-r--r--frontend/src/app/_modals/alert-dialog/alert-dialog.component.css3
-rw-r--r--frontend/src/app/_modals/alert-dialog/alert-dialog.component.html16
-rw-r--r--frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.css (renamed from frontend/src/app/_pages/filter-datasets/filter-datasets.component.css)0
-rw-r--r--frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.html16
-rw-r--r--frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.spec.ts (renamed from frontend/src/app/_elements/item-experiment/item-experiment.component.spec.ts)12
-rw-r--r--frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.ts28
-rw-r--r--frontend/src/app/_modals/login-modal/login-modal.component.css37
-rw-r--r--frontend/src/app/_modals/login-modal/login-modal.component.html79
-rw-r--r--frontend/src/app/_modals/login-modal/login-modal.component.ts21
-rw-r--r--frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.css0
-rw-r--r--frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.html13
-rw-r--r--frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.spec.ts25
-rw-r--r--frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.ts28
-rw-r--r--frontend/src/app/_modals/register-modal/register-modal.component.css44
-rw-r--r--frontend/src/app/_modals/register-modal/register-modal.component.html159
-rw-r--r--frontend/src/app/_modals/register-modal/register-modal.component.ts48
-rw-r--r--frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.css0
-rw-r--r--frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.html13
-rw-r--r--frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.spec.ts25
-rw-r--r--frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.ts21
-rw-r--r--frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html6
-rw-r--r--frontend/src/app/_pages/archive/archive.component.css0
-rw-r--r--frontend/src/app/_pages/archive/archive.component.html49
-rw-r--r--frontend/src/app/_pages/archive/archive.component.spec.ts25
-rw-r--r--frontend/src/app/_pages/archive/archive.component.ts25
-rw-r--r--frontend/src/app/_pages/browse-datasets/browse-datasets.component.html1
-rw-r--r--frontend/src/app/_pages/browse-datasets/browse-datasets.component.spec.ts25
-rw-r--r--frontend/src/app/_pages/browse-datasets/browse-datasets.component.ts15
-rw-r--r--frontend/src/app/_pages/browse-predictors/browse-predictors.component.css7
-rw-r--r--frontend/src/app/_pages/browse-predictors/browse-predictors.component.html40
-rw-r--r--frontend/src/app/_pages/browse-predictors/browse-predictors.component.spec.ts25
-rw-r--r--frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts26
-rw-r--r--frontend/src/app/_pages/experiment/experiment.component.css55
-rw-r--r--frontend/src/app/_pages/experiment/experiment.component.html49
-rw-r--r--frontend/src/app/_pages/experiment/experiment.component.spec.ts (renamed from frontend/src/app/experiment/experiment.component.spec.ts)0
-rw-r--r--frontend/src/app/_pages/experiment/experiment.component.ts164
-rw-r--r--frontend/src/app/_pages/filter-datasets/filter-datasets.component.html38
-rw-r--r--frontend/src/app/_pages/filter-datasets/filter-datasets.component.spec.ts25
-rw-r--r--frontend/src/app/_pages/home/home.component.css20
-rw-r--r--frontend/src/app/_pages/home/home.component.html78
-rw-r--r--frontend/src/app/_pages/home/home.component.ts5
-rw-r--r--frontend/src/app/_pages/my-datasets/my-datasets.component.css8
-rw-r--r--frontend/src/app/_pages/my-datasets/my-datasets.component.html39
-rw-r--r--frontend/src/app/_pages/my-datasets/my-datasets.component.spec.ts25
-rw-r--r--frontend/src/app/_pages/my-datasets/my-datasets.component.ts63
-rw-r--r--frontend/src/app/_pages/my-models/my-models.component.css12
-rw-r--r--frontend/src/app/_pages/my-predictors/my-predictors.component.css13
-rw-r--r--frontend/src/app/_pages/my-predictors/my-predictors.component.html23
-rw-r--r--frontend/src/app/_pages/my-predictors/my-predictors.component.spec.ts25
-rw-r--r--frontend/src/app/_pages/my-predictors/my-predictors.component.ts43
-rw-r--r--frontend/src/app/_pages/playground/playground.component.css0
-rw-r--r--frontend/src/app/_pages/playground/playground.component.html18
-rw-r--r--frontend/src/app/_pages/playground/playground.component.spec.ts (renamed from frontend/src/app/_elements/item-model/item-model.component.spec.ts)12
-rw-r--r--frontend/src/app/_pages/playground/playground.component.ts35
-rw-r--r--frontend/src/app/_pages/predict/predict.component.css3
-rw-r--r--frontend/src/app/_pages/predict/predict.component.html73
-rw-r--r--frontend/src/app/_pages/predict/predict.component.ts45
-rw-r--r--frontend/src/app/_pages/profile/profile.component.css51
-rw-r--r--frontend/src/app/_pages/profile/profile.component.html66
-rw-r--r--frontend/src/app/_pages/test/test.component.css0
-rw-r--r--frontend/src/app/_pages/test/test.component.html5
-rw-r--r--frontend/src/app/_pages/test/test.component.spec.ts25
-rw-r--r--frontend/src/app/_pages/test/test.component.ts15
-rw-r--r--frontend/src/app/_services/auth.service.ts30
-rw-r--r--frontend/src/app/_services/configuration.service.spec.ts (renamed from frontend/src/app/configuration.service.spec.ts)2
-rw-r--r--frontend/src/app/_services/configuration.service.ts (renamed from frontend/src/app/configuration.service.ts)2
-rw-r--r--frontend/src/app/_services/csv-parse.service.ts40
-rw-r--r--frontend/src/app/_services/datasets.service.ts7
-rw-r--r--frontend/src/app/_services/experiments.service.ts6
-rw-r--r--frontend/src/app/_services/models.service.ts20
-rw-r--r--frontend/src/app/_services/predictors.service.ts10
-rw-r--r--frontend/src/app/_services/signal-r.service.ts13
-rw-r--r--frontend/src/app/_services/user-info.service.ts2
-rw-r--r--frontend/src/app/app-routing.module.ts32
-rw-r--r--frontend/src/app/app.component.html17
-rw-r--r--frontend/src/app/app.component.ts30
-rw-r--r--frontend/src/app/app.module.ts108
-rw-r--r--frontend/src/app/experiment/experiment.component.css48
-rw-r--r--frontend/src/app/experiment/experiment.component.html36
-rw-r--r--frontend/src/app/experiment/experiment.component.ts38
-rw-r--r--frontend/src/app/material.module.ts119
-rw-r--r--frontend/src/app/scatterchart/scatterchart.component.css6
-rw-r--r--frontend/src/app/scatterchart/scatterchart.component.html4
-rw-r--r--frontend/src/app/training/training.component.css39
-rw-r--r--frontend/src/app/training/training.component.html63
-rw-r--r--frontend/src/app/training/training.component.ts105
-rw-r--r--frontend/src/assets/images/add_model_background.jpgbin56906 -> 0 bytes
-rw-r--r--frontend/src/assets/images/logo.pngbin13315 -> 11962 bytes
-rw-r--r--frontend/src/assets/images/logo_igrannonica_temp - Copy.pngbin0 -> 16336 bytes
-rw-r--r--frontend/src/assets/images/logo_igrannonica_temp.pngbin0 -> 29448 bytes
-rw-r--r--frontend/src/custom-theme.scss267
-rw-r--r--frontend/src/styles.css11
-rw-r--r--frontend/src/styles/font.css6
-rw-r--r--frontend/src/styles/fx.css24
-rw-r--r--frontend/src/styles/helper.css144
-rw-r--r--frontend/src/styles/layout.css69
-rw-r--r--frontend/src/styles/scrollbar.css28
-rw-r--r--frontend/src/styles/theme.css68
201 files changed, 5258 insertions, 2179 deletions
diff --git a/frontend/src/app/Shared.ts b/frontend/src/app/Shared.ts
index 59a2716d..d088fff9 100644
--- a/frontend/src/app/Shared.ts
+++ b/frontend/src/app/Shared.ts
@@ -1,4 +1,4 @@
-import { ElementRef } from "@angular/core";
+import { ElementRef, EventEmitter } from "@angular/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { AlertDialogComponent } from './_modals/alert-dialog/alert-dialog.component';
@@ -27,18 +27,24 @@ class Shared {
});
}
}
- openYesNoDialog(title: string, message: string,yesFunction:Function): void {
+ openYesNoDialog(title: string, message: string, yesFunction: Function): void {
if (this.dialog) {
const dialogRef = this.dialog.open(YesNoDialogComponent, {
width: '350px',
- data: { title: title, message: message,yesFunction}
+ data: { title: title, message: message, yesFunction }
});
dialogRef.afterClosed().subscribe(res => {
//nesto
});
}
}
+
+ bgScroll: EventEmitter<number> = new EventEmitter();
+
+ emitBGScrollEvent(value: number) {
+ this.bgScroll.emit(value);
+ }
}
export default new Shared(false); \ No newline at end of file
diff --git a/frontend/src/app/_data/Dataset.ts b/frontend/src/app/_data/Dataset.ts
index 766040a3..4ff0a471 100644
--- a/frontend/src/app/_data/Dataset.ts
+++ b/frontend/src/app/_data/Dataset.ts
@@ -1,25 +1,27 @@
-export default class Dataset {
+import { FolderFile } from "./FolderFile";
+
+export default class Dataset extends FolderFile {
_id: string = '';
constructor(
- public name: string = 'Novi izvor podataka',
+ name: string = 'Novi izvor podataka',
public description: string = '',
- public header: string[] = [],
public fileId?: number,
public extension: string = '.csv',
public isPublic: boolean = false,
public accessibleByLink: boolean = false,
- public dateCreated: Date = new Date(),
- public lastUpdated: Date = new Date(),
+ dateCreated: Date = new Date(),
+ lastUpdated: Date = new Date(),
public uploaderId: string = '',
- public delimiter: string = '',
- public hasHeader: boolean = true,
+ public delimiter: string = ',',
public columnInfo: ColumnInfo[] = [],
public rowCount: number = 0,
public nullRows: number = 0,
public nullCols: number = 0,
- public preview: string[][] = [[]]
- ) { }
+ public cMatrix: number[][] = []
+ ) {
+ super(name, dateCreated, lastUpdated);
+ }
}
export class ColumnInfo {
@@ -28,9 +30,20 @@ export class ColumnInfo {
public isNumber: boolean = false,
public numNulls: number = 0,
public uniqueValues?: string[],
+ public uniqueValuesCount?: number[],
+ public uniqueValuesPercent?: number[],
public median?: number,
public mean?: number,
public min?: number,
- public max?: number
- ) { }
-} \ No newline at end of file
+ public max?: number,
+ public q1?: number,
+ public q3?: number,
+ ) {
+ /*if (isNumber)
+ this.columnType = ColumnType.numerical;
+ else
+ this.columnType = ColumnType.categorical;*/
+ }
+
+}
+
diff --git a/frontend/src/app/_data/Experiment.ts b/frontend/src/app/_data/Experiment.ts
index 95ef6e1e..cff77535 100644
--- a/frontend/src/app/_data/Experiment.ts
+++ b/frontend/src/app/_data/Experiment.ts
@@ -5,6 +5,7 @@ export default class Experiment {
constructor(
public name: string = 'Novi eksperiment',
public description: string = '',
+ public type: ProblemType = ProblemType.Regression,
public datasetId: string = '',
public inputColumns: string[] = [],
public outputColumn: string = '',
@@ -13,15 +14,11 @@ export default class Experiment {
public dateCreated: Date = new Date(),
public lastUpdated: Date = new Date(),
public modelIds: string[] = [],
-
- // Test set settings
- public randomOrder: boolean = true,
- 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
-
- public encodings: ColumnEncoding[] = [],
- public type: ProblemType = ProblemType.Regression
+ public columnTypes: ColumnType[] = [],
+ public encodings: ColumnEncoding[] = []//[{columnName: "", columnEncoding: Encoding.Label}]
) { }
+
+ _columnsSelected: boolean = false;
}
export enum NullValueOptions {
@@ -47,11 +44,11 @@ export class NullValReplacer {
export enum Encoding {
Label = 'label',
OneHot = 'onehot',
- Ordinal = 'ordinal',
+ /*Ordinal = 'ordinal',
Hashing = 'hashing',
Binary = 'binary',
BaseN = 'baseN'
- /*
+
BackwardDifference = 'backward difference',
CatBoost = 'cat boost',
Count = 'count',
@@ -69,9 +66,13 @@ export enum Encoding {
}
export class ColumnEncoding {
- constructor (
+ constructor(
public columnName: string,
public encoding: Encoding
- )
- {}
+ ) { }
+}
+
+export enum ColumnType {
+ categorical = "categorical",
+ numerical = "numerical"
} \ No newline at end of file
diff --git a/frontend/src/app/_data/FolderFile.ts b/frontend/src/app/_data/FolderFile.ts
new file mode 100644
index 00000000..c228f25e
--- /dev/null
+++ b/frontend/src/app/_data/FolderFile.ts
@@ -0,0 +1,14 @@
+export class FolderFile {
+ constructor(
+ public name: string,
+ public dateCreated: Date,
+ public lastUpdated: Date
+ ) { }
+}
+
+
+export enum FolderType {
+ Dataset,
+ Model,
+ Experiment
+} \ No newline at end of file
diff --git a/frontend/src/app/_data/Model.ts b/frontend/src/app/_data/Model.ts
index 7d383584..526a8290 100644
--- a/frontend/src/app/_data/Model.ts
+++ b/frontend/src/app/_data/Model.ts
@@ -1,12 +1,13 @@
import { NgIf } from "@angular/common";
+import { FolderFile } from "./FolderFile";
-export default class Model {
+export default class Model extends FolderFile {
_id: string = '';
constructor(
- public name: string = 'Novi model',
+ name: string = 'Novi model',
public description: string = '',
- public dateCreated: Date = new Date(),
- public lastUpdated: Date = new Date(),
+ dateCreated: Date = new Date(),
+ lastUpdated: Date = new Date(),
//public experimentId: string = '',
// Neural net training settings
@@ -14,17 +15,65 @@ export default class Model {
public optimizer: Optimizer = Optimizer.Adam,
public lossFunction: LossFunction = LossFunction.MeanSquaredError,
public inputNeurons: number = 1,
- public hiddenLayerNeurons: number = 1,
public hiddenLayers: number = 1,
- public batchSize: number = 4,
- public hiddenLayerActivationFunctions: string[] = ['sigmoid'],
+ public batchSize: BatchSize = BatchSize.O3,
public outputLayerActivationFunction: ActivationFunction = ActivationFunction.Sigmoid,
public uploaderId: string = '',
public metrics: string[] = [], // TODO add to add-model form
- public epochs: number = 5 // TODO add to add-model form
+ public epochs: number = 5, // TODO add to add-model form
+ public inputColNum: number = 5,
+ public learningRate: LearningRate = LearningRate.LR1,
+ public layers: Layer[] = [new Layer()],
+
+ // Test set settings
+ public randomOrder: boolean = true,
+ 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
+
+ public isPublic: boolean = false,
+ public accessibleByLink: boolean = false
+ ) {
+ super(name, dateCreated, lastUpdated);
+ }
+}
+export class Layer {
+ constructor(
+ public layerNumber: number = 0,
+ public activationFunction: ActivationFunction = ActivationFunction.Sigmoid,
+ public neurons: number = 3,
+ public regularisation: Regularisation = Regularisation.L1,
+ public regularisationRate: RegularisationRate = RegularisationRate.RR1,
) { }
}
-
+export enum LearningRate {
+ LR1 = '0.00001',
+ LR2 = '0.0001',
+ LR3 = '0.001',
+ LR4 = '0.003',
+ LR5 = '0.01',
+ LR6 = '0.03',
+ LR7 = '0.1',
+ LR8 = '0.3',
+ LR9 = '1',
+ LR10 = '3',
+ LR11 = '10',
+}
+export enum Regularisation {
+ L1 = 'l1',
+ L2 = 'l2'
+}
+export enum RegularisationRate {
+ RR1 = '0',
+ RR2 = '0.001',
+ RR3 = '0.003',
+ RR4 = '0.01',
+ RR5 = '0.03',
+ RR6 = '0.1',
+ RR7 = '0.3',
+ RR8 = '1',
+ RR9 = '3',
+ RR10 = '10',
+}
export enum ProblemType {
Regression = 'regresioni',
BinaryClassification = 'binarni-klasifikacioni',
@@ -94,7 +143,7 @@ export enum LossFunctionBinaryClassification {
HingeLoss = 'hinge_loss',
}
export enum LossFunctionMultiClassification {
- //CategoricalCrossEntropy = 'categorical_crossentropy',
+ CategoricalCrossEntropy = 'categorical_crossentropy',
SparseCategoricalCrossEntropy = 'sparse_categorical_crossentropy',
KLDivergence = 'kullback_leibler_divergence',
}
@@ -157,3 +206,16 @@ export enum MetricsMultiClassification {
Recall = 'recall_score',
F1 = 'f1_score',
}
+
+export enum BatchSize {
+ O1 = '2',
+ O2 = '4',
+ O3 = '8',
+ O4 = '16',
+ O5 = '32',
+ O6 = '64',
+ O7 = '128',
+ O8 = '256',
+ O9 = '512',
+ O10 = '1024'
+} \ No newline at end of file
diff --git a/frontend/src/app/barchart/barchart.component.css b/frontend/src/app/_elements/_charts/barchart/barchart.component.css
index c3634c9f..c3634c9f 100644
--- a/frontend/src/app/barchart/barchart.component.css
+++ b/frontend/src/app/_elements/_charts/barchart/barchart.component.css
diff --git a/frontend/src/app/barchart/barchart.component.html b/frontend/src/app/_elements/_charts/barchart/barchart.component.html
index 48b7bd3e..48b7bd3e 100644
--- a/frontend/src/app/barchart/barchart.component.html
+++ b/frontend/src/app/_elements/_charts/barchart/barchart.component.html
diff --git a/frontend/src/app/barchart/barchart.component.spec.ts b/frontend/src/app/_elements/_charts/barchart/barchart.component.spec.ts
index 8b346d1c..8b346d1c 100644
--- a/frontend/src/app/barchart/barchart.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/barchart/barchart.component.spec.ts
diff --git a/frontend/src/app/barchart/barchart.component.ts b/frontend/src/app/_elements/_charts/barchart/barchart.component.ts
index def64b7d..904335d7 100644
--- a/frontend/src/app/barchart/barchart.component.ts
+++ b/frontend/src/app/_elements/_charts/barchart/barchart.component.ts
@@ -8,6 +8,7 @@ import {Chart} from 'node_modules/chart.js';
})
export class BarchartComponent implements OnInit {
+
constructor() { }
ngOnInit(){
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.css b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.css
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.css
diff --git a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html
new file mode 100644
index 00000000..688eafae
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html
@@ -0,0 +1,3 @@
+<div class="chart-wrapper">
+ <canvas #boxplot [width]="width" [height]="height"></canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/training/training.component.spec.ts b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.spec.ts
index 1222cb40..759e7c5e 100644
--- a/frontend/src/app/training/training.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { TrainingComponent } from './training.component';
+import { BoxPlotComponent } from './box-plot.component';
-describe('TrainingComponent', () => {
- let component: TrainingComponent;
- let fixture: ComponentFixture<TrainingComponent>;
+describe('BoxPlotComponent', () => {
+ let component: BoxPlotComponent;
+ let fixture: ComponentFixture<BoxPlotComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ TrainingComponent ]
+ declarations: [ BoxPlotComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(TrainingComponent);
+ fixture = TestBed.createComponent(BoxPlotComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts
new file mode 100644
index 00000000..d6f4b6ec
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts
@@ -0,0 +1,102 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { Chart, LinearScale, CategoryScale } from 'chart.js';
+import { BoxPlotController, BoxAndWiskers } from '@sgratzl/chartjs-chart-boxplot';
+
+function randomValues(count: number, min: number, max: number) {
+ const delta = max - min;
+ return Array.from({ length: count }).map(() => Math.random() * delta + min);
+}
+
+Chart.register(BoxPlotController, BoxAndWiskers, LinearScale, CategoryScale);
+
+@Component({
+ selector: 'app-box-plot',
+ templateUrl: './box-plot.component.html',
+ styleUrls: ['./box-plot.component.css']
+})
+export class BoxPlotComponent implements AfterViewInit {
+
+ @Input()width?: number;
+ @Input()height?: number;
+
+ @ViewChild('boxplot') chartRef!: ElementRef;
+ constructor() { }
+
+ boxplotData = {
+ // define label tree
+ labels: ['January'/*, 'February', 'March', 'April', 'May', 'June', 'July'*/],
+ datasets: [{
+ label: 'Dataset 1',
+ backgroundColor: '#0063AB',
+ borderColor: '#dfd7d7',
+ borderWidth: 1,
+ outlierColor: '#999999',
+ scaleFontColor: '#0063AB',
+ padding: 10,
+ itemRadius: 0,
+ data: [
+ randomValues(100, 0, 100),
+ /*randomValues(100, 0, 20),
+ randomValues(100, 20, 70),
+ randomValues(100, 60, 100),
+ randomValues(40, 50, 100),
+ randomValues(100, 60, 120),
+ randomValues(100, 80, 100)*/
+ ]}/*, {
+ label: 'Dataset 2',
+ backgroundColor: 'rgba(0,0,255,0.5)',
+ borderColor: 'blue',
+ borderWidth: 1,
+ outlierColor: '#999999',
+ padding: 10,
+ itemRadius: 0,
+ data: [
+ randomValues(100, 60, 100),
+ randomValues(100, 0, 100),
+ randomValues(100, 0, 20),
+ randomValues(100, 20, 70),
+ randomValues(40, 60, 120),
+ randomValues(100, 20, 100),
+ randomValues(100, 80, 100)
+ ]
+ }*/]
+ };
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: "boxplot",
+ data: this.boxplotData,
+ options: {
+ /*title: {
+ display: true,
+ text: 'Predicted world population (millions) in 2050'
+ }*/
+ plugins:{
+ legend: {
+ display: false
+ },
+ },
+ scales : {
+ x: {
+ ticks: {
+ color: '#dfd7d7'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ },
+ y : {
+ min: -50,
+ max: 200,
+ ticks: {
+ color: '#dfd7d7'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ }
+ }
+ }
+ });
+}
+
+}
diff --git a/frontend/src/app/_elements/carousel/carousel.component.css b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/carousel/carousel.component.css
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.css
diff --git a/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html
new file mode 100644
index 00000000..9c464534
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html
@@ -0,0 +1 @@
+<canvas #doughnut [width]="width" [height]="height"></canvas>
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.spec.ts
index b5c2d91c..67309670 100644
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemPredictorComponent } from './item-predictor.component';
+import { DoughnutChartComponent } from './doughnut-chart.component';
-describe('ItemPredictorComponent', () => {
- let component: ItemPredictorComponent;
- let fixture: ComponentFixture<ItemPredictorComponent>;
+describe('DoughnutChartComponent', () => {
+ let component: DoughnutChartComponent;
+ let fixture: ComponentFixture<DoughnutChartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemPredictorComponent ]
+ declarations: [ DoughnutChartComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemPredictorComponent);
+ fixture = TestBed.createComponent(DoughnutChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts
new file mode 100644
index 00000000..fc13289c
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts
@@ -0,0 +1,37 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'node_modules/chart.js';
+
+@Component({
+ selector: 'app-doughnut-chart',
+ templateUrl: './doughnut-chart.component.html',
+ styleUrls: ['./doughnut-chart.component.css']
+})
+export class DoughnutChartComponent implements AfterViewInit {
+
+ @Input()width: number = 800;
+ @Input()height: number = 450;
+
+ @ViewChild('doughnut') chartRef!: ElementRef;
+ constructor() { }
+
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'doughnut',
+ data: {
+ labels: ["Africa", "Asia", "Europe", "Latin America", "North America"],
+ datasets: [{
+ label: "Population (millions)",
+ backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
+ data: [2478,5267,734,784,433]
+ }]
+ },
+ /*options: {
+ title: {
+ display: true,
+ text: 'Predicted world population (millions) in 2050'
+ }
+ }*/
+ });
+ }
+
+}
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.css b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.css
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.css
diff --git a/frontend/src/app/_elements/_charts/heatmap/heatmap.component.html b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.html
new file mode 100644
index 00000000..52d95516
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.html
@@ -0,0 +1,3 @@
+<div style="width:800px; height: 400px; background-color: red;">
+ <ejs-heatmap [dataSource]='dataSource' [xAxis]='xAxis' [yAxis]='yAxis'></ejs-heatmap>
+</div>
diff --git a/frontend/src/app/_pages/predict/predict.component.spec.ts b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.spec.ts
index 65871ecc..fa0a90cc 100644
--- a/frontend/src/app/_pages/predict/predict.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { PredictComponent } from './predict.component';
+import { HeatmapComponent } from './heatmap.component';
-describe('PredictComponent', () => {
- let component: PredictComponent;
- let fixture: ComponentFixture<PredictComponent>;
+describe('HeatmapComponent', () => {
+ let component: HeatmapComponent;
+ let fixture: ComponentFixture<HeatmapComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ PredictComponent ]
+ declarations: [ HeatmapComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(PredictComponent);
+ fixture = TestBed.createComponent(HeatmapComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/heatmap/heatmap.component.ts b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.ts
new file mode 100644
index 00000000..f3d1af98
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.ts
@@ -0,0 +1,108 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'chart.js';
+import { HeatMapAllModule } from '@syncfusion/ej2-angular-heatmap';
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+
+
+
+@Component({
+ selector: 'app-heatmap',
+ templateUrl: './heatmap.component.html',
+ styleUrls: ['./heatmap.component.css']
+})
+export class HeatmapComponent implements OnInit {
+
+
+ @Input()width?: number;
+ @Input()height?: number;
+
+ dataSource = [
+
+ [73, 39, 26, 39, 94, 0],
+
+ [93, 58, 53, 38, 26, 68],
+
+ [99, 28, 22, 4, 66, 90],
+
+ [14, 26, 97, 69, 69, 3],
+
+ [7, 46, 47, 47, 88, 6],
+
+ [41, 55, 73, 23, 3, 79],
+
+ [56, 69, 21, 86, 3, 33],
+
+ [45, 7, 53, 81, 95, 79],
+
+ [60, 77, 74, 68, 88, 51],
+
+ [25, 25, 10, 12, 78, 14],
+
+ [25, 56, 55, 58, 12, 82],
+
+ [74, 33, 88, 23, 86, 59]
+
+ ];
+
+ xAxis = {
+
+ labels: ['Nancy', 'Andrew', 'Janet', 'Margaret', 'Steven', 'Michael', 'Robert',
+
+ 'Laura', 'Anne', 'Paul', 'Karin', 'Mario'],
+
+ };
+
+ yAxis = {
+
+ labels: ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat'],
+
+ }
+
+ //@ViewChild('heatmap') chartRef!: ElementRef;
+ constructor() { }
+
+ ngOnInit(): void {
+
+
+
+ /*
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'pie',
+ data: {
+ datasets: [{
+ data: [
+
+ [73, 39, 26, 39, 94, 0],
+
+ [93, 58, 53, 38, 26, 68],
+
+ [99, 28, 22, 4, 66, 90],
+
+ [14, 26, 97, 69, 69, 3],
+
+ [7, 46, 47, 47, 88, 6],
+
+ [41, 55, 73, 23, 3, 79],
+
+ [56, 69, 21, 86, 3, 33],
+
+ [45, 7, 53, 81, 95, 79],
+
+ [60, 77, 74, 68, 88, 51],
+
+ [25, 25, 10, 12, 78, 14],
+
+ [25, 56, 55, 58, 12, 82],
+
+ [74, 33, 88, 23, 86, 59]
+
+ ],
+ }]
+ }
+});
+ */
+
+ }
+
+}
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.css b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/line-chart/line-chart.component.css
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
new file mode 100644
index 00000000..7f18256a
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
@@ -0,0 +1,3 @@
+
+ <canvas id="myChart" style="width: 100%; height: 530px;">
+ </canvas> \ 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/_charts/line-chart/line-chart.component.spec.ts
index 0c5e7ef5..0c5e7ef5 100644
--- a/frontend/src/app/_elements/line-chart/line-chart.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.spec.ts
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.ts b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts
index 1a8579a0..655db9ec 100644
--- a/frontend/src/app/_elements/line-chart/line-chart.component.ts
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, Input } from '@angular/core';
+import { Component, AfterViewInit, ViewChild } from '@angular/core';
import { Chart } from 'chart.js';
@Component({
@@ -7,7 +7,7 @@ import { Chart } from 'chart.js';
styleUrls: ['./line-chart.component.css']
})
-export class LineChartComponent implements OnInit {
+export class LineChartComponent implements AfterViewInit {
dataAcc: number[] = [];
dataMAE: number[] = [];
@@ -47,7 +47,7 @@ export class LineChartComponent implements OnInit {
this.myChart.update();
}
- ngOnInit(): void {
+ ngAfterViewInit(): void {
this.myChart = new Chart("myChart",
{
type: 'line',
@@ -77,10 +77,27 @@ export class LineChartComponent implements OnInit {
},
options: {
scales: {
+ x: {
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ },
y: {
- beginAtZero: true
+ beginAtZero: true,
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
}
+
}
+
+
}
}
);
diff --git a/frontend/src/app/mixed-chart/mixed-chart.component.css b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/mixed-chart/mixed-chart.component.css
+++ b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.css
diff --git a/frontend/src/app/mixed-chart/mixed-chart.component.html b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.html
index 806ea9e8..806ea9e8 100644
--- a/frontend/src/app/mixed-chart/mixed-chart.component.html
+++ b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.html
diff --git a/frontend/src/app/mixed-chart/mixed-chart.component.spec.ts b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.spec.ts
index 361cd047..361cd047 100644
--- a/frontend/src/app/mixed-chart/mixed-chart.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.spec.ts
diff --git a/frontend/src/app/mixed-chart/mixed-chart.component.ts b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.ts
index 2524ee36..2524ee36 100644
--- a/frontend/src/app/mixed-chart/mixed-chart.component.ts
+++ b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.ts
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.css b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.css
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.css
diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html
new file mode 100644
index 00000000..7faf3af0
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html
@@ -0,0 +1,3 @@
+<div class="chart-wrapper">
+ <canvas #piechart [width]="width" [height]="height"></canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/my-models/my-models.component.spec.ts b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.spec.ts
index e431d04c..64f36b7d 100644
--- a/frontend/src/app/_pages/my-models/my-models.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { MyModelsComponent } from './my-models.component';
+import { PieChartComponent } from './pie-chart.component';
-describe('MyModelsComponent', () => {
- let component: MyModelsComponent;
- let fixture: ComponentFixture<MyModelsComponent>;
+describe('PieChartComponent', () => {
+ let component: PieChartComponent;
+ let fixture: ComponentFixture<PieChartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ MyModelsComponent ]
+ declarations: [ PieChartComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(MyModelsComponent);
+ fixture = TestBed.createComponent(PieChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts
new file mode 100644
index 00000000..f141f522
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts
@@ -0,0 +1,46 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'chart.js';
+
+@Component({
+ selector: 'app-pie-chart',
+ templateUrl: './pie-chart.component.html',
+ styleUrls: ['./pie-chart.component.css']
+})
+export class PieChartComponent implements AfterViewInit {
+
+ @Input()width?: number;
+ @Input()height?: number;
+
+ @ViewChild('piechart') chartRef!: ElementRef;
+ constructor() { }
+
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'pie',
+ data: {
+ labels: ["Africa", "Asia", "Europe", "Latin America", "North America"],
+ datasets: [{
+ label: "Population (millions)",
+ backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
+ data: [2478,5267,734,784,433],
+ }]
+ },
+ options: {
+ /*title: {
+ display: true,
+ text: 'Predicted world population (millions) in 2050'
+ }*/
+ plugins:{
+ legend: {
+ display: false
+ },
+ },
+ layout: {
+ padding: 15}
+ }
+});
+
+ }
+
+
+}
diff --git a/frontend/src/app/point-linechart/point-linechart.component.css b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/point-linechart/point-linechart.component.css
+++ b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.css
diff --git a/frontend/src/app/point-linechart/point-linechart.component.html b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.html
index f9f9a24a..f9f9a24a 100644
--- a/frontend/src/app/point-linechart/point-linechart.component.html
+++ b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.html
diff --git a/frontend/src/app/point-linechart/point-linechart.component.spec.ts b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.spec.ts
index fe08fe7c..fe08fe7c 100644
--- a/frontend/src/app/point-linechart/point-linechart.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.spec.ts
diff --git a/frontend/src/app/point-linechart/point-linechart.component.ts b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.ts
index 3497a20c..3497a20c 100644
--- a/frontend/src/app/point-linechart/point-linechart.component.ts
+++ b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.ts
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
new file mode 100644
index 00000000..005cb692
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
@@ -0,0 +1,4 @@
+#divScatterChart{
+
+ display: block;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
new file mode 100644
index 00000000..ef41775a
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
@@ -0,0 +1,4 @@
+
+<div id="divScatterChart" style="width: 100%;height: 100%;">
+ <canvas id="ScatterCharts" style="width: 100%;height: 280px;"> </canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/scatterchart/scatterchart.component.spec.ts b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.spec.ts
index 1db81051..1db81051 100644
--- a/frontend/src/app/scatterchart/scatterchart.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.spec.ts
diff --git a/frontend/src/app/scatterchart/scatterchart.component.ts b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts
index 9dfef4c3..12795c70 100644
--- a/frontend/src/app/scatterchart/scatterchart.component.ts
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts
@@ -24,15 +24,32 @@ export class ScatterchartComponent implements OnInit {
{x: 15, y: 5},
{x: 16, y: 3},
{x: 17, y: 2}],
- backgroundColor: 'rgb(255, 99, 132)'
+ borderColor: 'white',
}]
},
options: {
scales: {
+ x:{
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ },
y: {
- beginAtZero: true
+ beginAtZero: true,
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
}
+
}
+
+
}
});
}
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
deleted file mode 100644
index e5d4cd23..00000000
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<div class="row mb-4">
- <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="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?
- <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;
- <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-3 mt-4">
-
- <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?
- <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 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>
-</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> \ No newline at end of file
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.css b/frontend/src/app/_elements/annvisual/annvisual.component.css
deleted file mode 100644
index 857a3390..00000000
--- a/frontend/src/app/_elements/annvisual/annvisual.component.css
+++ /dev/null
@@ -1,4 +0,0 @@
-#graph{
- width: 100%;
- text-align: center;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.html b/frontend/src/app/_elements/annvisual/annvisual.component.html
deleted file mode 100644
index 09251398..00000000
--- a/frontend/src/app/_elements/annvisual/annvisual.component.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<div style="text-align: center; " >
- <button (click)="d3()" mat-raised-button color="primary">Prikaz veštačke neuronske mreže</button>
- <div id="graph" align-items-center style="width: 12rem;"></div>
- </div>
-
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.ts b/frontend/src/app/_elements/annvisual/annvisual.component.ts
deleted file mode 100644
index df0a3898..00000000
--- a/frontend/src/app/_elements/annvisual/annvisual.component.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Component, OnInit, Input } from '@angular/core';
-import Model from 'src/app/_data/Model';
-import { graphviz } from 'd3-graphviz';
-
-@Component({
- selector: 'app-annvisual',
- templateUrl: './annvisual.component.html',
- styleUrls: ['./annvisual.component.css']
-})
-export class AnnvisualComponent implements OnInit {
- ngOnInit(): void {
- }
-
- @Input() model: Model = new Model();
-
- d3() {
- let inputlayerstring: string = '';
- let hiddenlayerstring: string = '';
- let digraphstring: string = 'digraph {';
-
- for (let i = 0; i < /*this.model.inputColumns.length*/ 10; i++) {
- inputlayerstring = inputlayerstring + 'i' + i + ',';
- }
- inputlayerstring = inputlayerstring.slice(0, -1);
-
- digraphstring = digraphstring + inputlayerstring + '->';
-
- for (let j = 0; j < this.model.hiddenLayers; j++) {
- for (let i = 0; i < this.model.hiddenLayerNeurons; i++) {
- hiddenlayerstring = hiddenlayerstring + 'h' + j + '_' + i + ',';
- }
- hiddenlayerstring = hiddenlayerstring.slice(0, -1);
- digraphstring = digraphstring + hiddenlayerstring + '->';
- hiddenlayerstring = '';
- }
- digraphstring = digraphstring + 'o}';
-
- graphviz('#graph').renderDot(digraphstring);
- }
-
- //'digraph {i0,i1,i2->h1,h2,h3->h21,h22,h23->o}'
-}
-
-
-
diff --git a/frontend/src/app/_elements/carousel/carousel.component.html b/frontend/src/app/_elements/carousel/carousel.component.html
deleted file mode 100644
index eb1041ce..00000000
--- a/frontend/src/app/_elements/carousel/carousel.component.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<div class="container">
- <div class="row d-flex align-items-stretch flex-row mx-5 align-items-stretch">
- <div class="col my-1" *ngFor="let item of items" [ngSwitch]="type">
- <ng-template ngSwitchCase="Object">
- Unknown item type
- </ng-template>
- <ng-template ngSwitchCase="Dataset">
- <app-item-dataset [dataset]="item">
- </app-item-dataset>
- </ng-template>
- <ng-template ngSwitchCase="Predictor">
- <app-item-predictor [predictor]="item">
- </app-item-predictor>
- </ng-template>
- </div>
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/carousel/carousel.component.ts b/frontend/src/app/_elements/carousel/carousel.component.ts
deleted file mode 100644
index e0112121..00000000
--- a/frontend/src/app/_elements/carousel/carousel.component.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-carousel',
- templateUrl: './carousel.component.html',
- styleUrls: ['./carousel.component.css']
-})
-export class CarouselComponent {
-
- @Input() items: any[] = [];
- @Input() type: string = "Object";
-
- constructor() { }
-
- ngOnInit(): void {
- }
-
-}
diff --git a/frontend/src/app/_elements/column-table/column-table.component.css b/frontend/src/app/_elements/column-table/column-table.component.css
new file mode 100644
index 00000000..0477b7be
--- /dev/null
+++ b/frontend/src/app/_elements/column-table/column-table.component.css
@@ -0,0 +1,266 @@
+table.fixed {
+ table-layout: fixed;
+ display: block;
+ overflow-x: auto;
+ white-space: nowrap;
+ font-size: 12px;
+ border-radius: 4px;
+}
+
+#divTable {
+ height: 100%;
+ overflow-y: auto;
+}
+
+table.fixed td {
+ overflow: hidden;
+ max-width: 200px;
+ min-width: 200px;
+ vertical-align: middle;
+ margin: 4px;
+}
+
+table.fixed th {
+ overflow: hidden;
+ max-width: 120px;
+ min-width: 120px;
+ vertical-align: middle;
+ background-color: var(--ns-primary-50);
+ font-size: 14px;
+}
+
+table.fixed th:first-child {
+ text-align: center;
+}
+
+.columnNames {
+ background-color: var(--ns-primary-50) !important;
+}
+
+.brighter {
+ background-color: var(--ns-primary) !important;
+ border-color: var(--offwhite);
+}
+
+.border-bottom {
+ border-bottom-color: var(--offwhite) !important;
+}
+
+mat-slider {
+ width: 300px;
+}
+
+.slider {
+ background-color: var(--ns-bg-dark-100);
+}
+
+#missingValuesHeader {
+ font-size: 13px;
+ line-height: 140% !important;
+}
+
+.verticalAlign {
+ vertical-align: center;
+}
+
+.cell-align {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ vertical-align: middle;
+ height: 100%;
+}
+
+.text-left {
+ text-align: left !important;
+}
+
+table ::ng-deep .mat-form-field-wrapper {
+ margin-top: -2rem;
+}
+
+.graphics-row {
+ height: 100px;
+ padding: 1px;
+ margin: 0;
+}
+
+.no-pad {
+ padding: 2px;
+ margin: 0;
+}
+
+.text-overflow {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.row-height {
+ height: 30px;
+ border: none;
+ outline: none;
+}
+
+.graphic-class {
+ opacity: 0.5;
+}
+
+
+/* TABS STYLE */
+
+#folder-table {
+ border: 1px solid var(--ns-primary);
+ border-radius: 4px;
+ height: 70%;
+}
+
+#tabs {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ height: 3.2rem;
+}
+
+#tabs>.folder-tab:not(:first-child) {
+ margin-left: -5px;
+}
+
+.folder-tab-end {
+ margin-left: auto;
+ color: var(--offwhite) !important;
+ overflow: hidden;
+}
+
+.folder-tab,
+.folder-tab-end {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ position: relative;
+ overflow-x: hidden;
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-color: var(--ns-primary);
+ color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 0 1px;
+}
+
+.folder-tab:not(:first-child) {
+ margin-block-start: auto;
+}
+
+.folder-tab {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.selected-tab {
+ height: 3rem;
+ background-color: var(--ns-primary);
+ color: var(--offwhite);
+}
+
+.hover-tab {
+ height: 3.2rem;
+}
+
+.selected-tab,
+.hover-tab {
+ width: fit-content !important;
+}
+
+.tab-link {
+ color: var(--offwhite) !important;
+ text-decoration: none !important;
+ cursor: pointer;
+}
+
+.tab-link:active {
+ text-decoration: underline !important;
+}
+
+.selected-tab {
+ background-color: var(--ns-primary);
+}
+
+.hidden {
+ /*visibility: hidden;
+ height: 1px;*/
+ display: none;
+}
+
+.bottom-button {
+ font-size: large;
+ position: relative;
+ background-color: var(--ns-primary);
+ width: 10rem;
+ height: 2.3rem;
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 0px 1px 1px 1px;
+}
+
+#footer {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+}
+
+.pad-fix {
+ padding-top: 0.6rem;
+ padding-bottom: 0;
+}
+
+.long {
+ height: 3rem;
+}
+
+.col-disabled {
+ background-color: rgb(0, 45, 69);
+}
+
+.text-disabled {
+ color: gray;
+}
+
+.header-disabled {
+ color: gray;
+}
+
+.menu-disabled {
+ pointer-events: none;
+ opacity: .5;
+}
+
+col:not(.col-disabled) {
+ background-color: var(--ns-bg-dark-100);
+}
+
+.col-first {
+ background-color: rgb(1, 56, 86) !important;
+}
+
+
+/* mat-icon rotate */
+
+.rotate {
+ animation: rotation 3s infinite linear;
+}
+
+.rotate:hover {
+ cursor: pointer;
+}
+
+@keyframes rotation {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(359deg);
+ }
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/column-table/column-table.component.html b/frontend/src/app/_elements/column-table/column-table.component.html
new file mode 100644
index 00000000..efc093d2
--- /dev/null
+++ b/frontend/src/app/_elements/column-table/column-table.component.html
@@ -0,0 +1,249 @@
+<div *ngIf="loaded">
+ <div id="tabs">
+ <div class="folder-tab p-1 rounded-top" *ngFor="let tab of tabs; let i = index" [style]="'z-index:' + calcZIndex(i) + ' ;'" [ngClass]="{'selected-tab' : selectedTab.index == i, 'hover-tab' : hoveringOverTab?.index == i}">
+ <a class="m-1 stretched-link tab-link" (click)="selectTab(i)" (mouseenter)="hoverOverTab(i)" (mouseleave)="hoverOverTab(-1)">
+ {{tab.name}}
+ </a>
+ </div>
+ <!--
+ <button mat-button class="p-1 folder-tab-end rounded-top">
+ Kolone
+ <mat-icon>keyboard_double_arrow_down</mat-icon>
+ </button>
+ -->
+ </div>
+ <div id="folder-table" *ngIf="dataset && experiment">
+ <!--<div [ngSwitch]="tabToDisplay">-->
+ <div id="divTable">
+
+ <div [ngClass]="{'hidden': tabToDisplay != Table.Data}">
+ <table class="table text-offwhite fixed bg-blur">
+ <colgroup>
+ <col class="col-first">
+ <col *ngFor="let column of dataset.columnInfo; let i = index" [ngClass]="{'col-disabled' : !experiment.inputColumns.includes(column.columnName)}">
+ </colgroup>
+ <thead>
+ <tr>
+ <th>#</th>
+ <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'header-disabled' : !columnsChecked[i]}">
+ <div class="cell-align">
+ #{{i + 1}}&nbsp;&nbsp;{{colInfo.columnName}}
+ <mat-checkbox color="primary" [(ngModel)]="columnsChecked[i]" (change)="changeInputColumns($event, colInfo.columnName)"></mat-checkbox>
+ </div>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let row of tableData; let i = index">
+ <th>#{{i}}</th>
+ <td *ngFor="let col of row; let j = index" [ngClass]="{'text-disabled' : !columnsChecked[j]}">
+ <div class="text-overflow">
+ {{col}}
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div [ngClass]="{'hidden': tabToDisplay != Table.CorrelationMatrix}">
+ <table class="table text-offwhite fixed bg-blur">
+ <colgroup>
+ <col class="col-first">
+ <col *ngFor="let column of dataset.columnInfo; let i = index" [ngClass]="{'col-disabled' : !experiment.inputColumns.includes(column.columnName)}">
+ </colgroup>
+ <thead>
+ <tr>
+ <th>Kolona</th>
+ <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'header-disabled' : !columnsChecked[i]}">
+ <div class="cell-align">
+ #{{i + 1}}&nbsp;&nbsp;{{colInfo.columnName}}
+ <mat-checkbox color="primary" [(ngModel)]="columnsChecked[i]" (change)="changeInputColumns($event, colInfo.columnName)"></mat-checkbox>
+ </div>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let row of dataset.cMatrix; let i = index">
+ <th [ngClass]="{'header-disabled col-disabled' : !columnsChecked[i]}">
+ <div class="text-left">
+ {{dataset.columnInfo[i].columnName}}
+ </div>
+ </th>
+ <td *ngFor="let corrVal of row; let j = index" [ngClass]="{'text-disabled col-disabled' : !columnsChecked[j] || !columnsChecked[i]}">
+ <div class="text-overflow">
+ {{corrVal}}
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div [ngClass]="{'hidden': tabToDisplay != Table.Columns}">
+ <table class="table text-offwhite fixed bg-blur">
+ <colgroup>
+ <col class="col-first">
+ <col *ngFor="let column of dataset.columnInfo; let i = index" [ngClass]="{'col-disabled' : !experiment.inputColumns.includes(column.columnName)}">
+ </colgroup>
+ <thead>
+ <tr>
+ <th>Naziv</th>
+ <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'header-disabled' : !columnsChecked[i]}">
+ <div class="cell-align">
+ #{{i + 1}}&nbsp;&nbsp;{{colInfo.columnName}}
+ <mat-checkbox color="primary" [(ngModel)]="columnsChecked[i]" (change)="changeInputColumns($event, colInfo.columnName)"></mat-checkbox>
+ </div>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th>Tip</th>
+ <td *ngFor="let colInfo of dataset.columnInfo; let i = index" class="pad-fix" [ngClass]="{'text-disabled' : !columnsChecked[i]}">
+ <p class="verticalAlign text-left" style="font-size:13px;" *ngIf="!colInfo.isNumber">Kategorijski</p>
+ <mat-form-field *ngIf="colInfo.isNumber">
+ <mat-select matNativeControl [(value)]="this.experiment.columnTypes[i]" [disabled]="!columnsChecked[i]" (selectionChange)="columnTypeChanged(colInfo.columnName);">
+ <mat-option [value]="ColumnType.categorical">Kategorijski</mat-option>
+ <mat-option [value]="ColumnType.numerical">Numerički</mat-option>
+ </mat-select>
+ </mat-form-field>
+ </td>
+ </tr>
+ <tr class="graphics-row">
+ <th class="no-pad">Grafik</th>
+ <td class="no-pad" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'graphic-class' : !columnsChecked[i]}">
+ <app-box-plot *ngIf="this.experiment.columnTypes[i] == ColumnType.numerical" [width]="150" [height]="150"></app-box-plot>
+ <app-pie-chart *ngIf="this.experiment.columnTypes[i] == ColumnType.categorical" [width]="150" [height]="150"></app-pie-chart>
+ </td>
+ </tr>
+ <tr>
+ <th class="border-bottom">Statistika</th>
+ <td *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'text-disabled' : !columnsChecked[i]}" class="text-left">
+ <span *ngIf="this.experiment.columnTypes[i] == ColumnType.numerical">
+ Mean: {{colInfo.mean}}<br>
+ Median: {{colInfo.median}}<br>
+ Min: {{colInfo.min}}<br>
+ Max: {{colInfo.max}}<br>
+ Q1: {{colInfo.q1}}<br>
+ Q3: {{colInfo.q3}}<br>
+ </span>
+ <div class="text-overflow" *ngIf="this.experiment.columnTypes[i] == ColumnType.categorical && colInfo.uniqueValuesPercent">
+ <span *ngFor="let uniqueValue of colInfo.uniqueValues | slice:0:6; let i = index">
+ ({{(colInfo.uniqueValuesPercent[i] * 100).toFixed(2)}}%) {{uniqueValue}}<br>
+ </span>
+ </div>
+ </td>
+ </tr>
+ <tr style="padding: 0">
+ <th class="brighter cell-align long" (click)="openEncodingDialog()">
+ <span class="verticalAlign">Enkodiranje</span>&nbsp;
+ <span class="material-icons-round verticalAlign rotate">settings</span>
+ </th>
+ <td *ngFor="let colInfo of dataset.columnInfo; let i = index" class="pad-fix" [ngClass]="{'text-disabled' : !columnsChecked[i]}">
+ <mat-form-field>
+ <mat-select matNativeControl [(value)]="experiment.encodings[i].encoding" [disabled]="!columnsChecked[i]" (selectionChange)="columnTableChangeDetected()">
+ <mat-option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </td>
+ </tr>
+ <tr>
+ <th class="brighter cell-align" (click)="openMissingValuesDialog()">
+ <div id="missingValuesHeader">Nedostajuće<br>vrednosti<br></div>
+ <span class="material-icons-round rotate">settings</span>
+ </th>
+ <td *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'text-disabled' : !columnsChecked[i]}">
+
+ <div *ngIf="colInfo.numNulls > 0">
+ <button class="w-100" mat-raised-button [ngClass]="{ 'menu-disabled' : !columnsChecked[i]}" [matMenuTriggerFor]="menu" id="main_{{colInfo.columnName}}" #nullValMenu>
+ <div class="cell-align">
+ {{nullValOption[i]}}
+ <mat-icon>arrow_drop_down</mat-icon>
+ </div>
+ </button>
+ <mat-menu #menu="matMenu">
+ <!--<button mat-menu-item (click)="MissValsDeleteClicked($event, NullValueOptions.DeleteColumns, i)" value={{colInfo.columnName}}>Obriši kolonu</button>-->
+ <button mat-menu-item (click)="MissValsDeleteClicked($event, NullValueOptions.DeleteRows, i)" value={{colInfo.columnName}}>Obriši redove ({{colInfo.numNulls}})</button>
+ <button mat-menu-item [matMenuTriggerFor]="fillWith">Popuni sa</button>
+ </mat-menu>
+
+ <mat-menu #fillWith="matMenu">
+ <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.mean}}>Mean ({{colInfo.mean}})</button>
+ <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.median}}>Median ({{colInfo.median}})</button>
+ <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.max}}>Max ({{colInfo.max}})</button>
+ <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.min}}>Min ({{colInfo.min}})</button>
+
+ <button *ngIf="!colInfo.isNumber" mat-menu-item [matMenuTriggerFor]="uniques">Najčešće vrednosti</button>
+
+ <button mat-menu-item [matMenuTriggerFor]="replaceWith">Unesi vrednost...</button>
+ </mat-menu>
+
+ <mat-menu #uniques="matMenu">
+ <button mat-menu-item *ngFor="let uniqueValue of colInfo.uniqueValues" (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{uniqueValue}}>{{uniqueValue}}</button>
+ </mat-menu>
+
+ <mat-menu #replaceWith="matMenu">
+ <input type="text" id={{colInfo.columnName}} mat-menu-item placeholder="Unesi vrednost..." [value] #enterAValue>
+ <button [disabled]="getValue(colInfo.columnName) == ''" mat-menu-item value={{getValue(colInfo.columnName)}} (click)="MissValsReplaceClicked($event, colInfo.columnName, i)">Potvrdi unos</button>
+ </mat-menu>
+ </div>
+ <div *ngIf="colInfo.numNulls == 0" class="text-left">
+ Nema nedostajućih vrednosti.
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <div *ngIf="dataset && experiment" class="container-fluid text-offwhite belowColumn mt-3">
+ <div class="ns-row">
+ <div class="break-2"></div>
+
+ <div class="ns-col rounded">
+ <mat-form-field appearance="fill" class="align-items-center justify-content-center pt-3 w-100">
+ <mat-label>Izlazna kolona</mat-label>
+ <mat-select [(value)]="experiment.outputColumn" (selectionChange)="changeOutputColumn(this.experiment.inputColumns[0])">
+ <mat-option *ngFor="let inputColumn of experiment.inputColumns" [value]="inputColumn">{{inputColumn}}</mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="ns-col rounded">
+ <mat-form-field appearance="fill" class="align-items-center justify-content-center pt-3 w-100">
+ <mat-label>Tip problema</mat-label>
+ <mat-select [(value)]="experiment.type">
+ <mat-option [value]="ProblemType.Regression">Regresioni</mat-option>
+ <mat-option [value]="ProblemType.BinaryClassification">Binarni-klasifikacioni</mat-option>
+ <mat-option [value]="ProblemType.MultiClassification">Multi-klasifikacioni</mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="break-1"></div>
+ <div class="ns-col d-flex align-items-center justify-content-center">
+ <button *ngIf="experiment._id == ''" mat-button (click)="saveExperiment()" class="bottom-button text-offwhite rounded-bottom">
+ <div class="f-row" style="justify-content: space-around; width: 100%;">
+ <div>Sačuvaj</div>
+ <div class="icon-double pt-1">
+ <mat-icon>check</mat-icon>
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ <button *ngIf="experiment._id != ''" mat-button (click)="updateExperiment()" class="bottom-button text-offwhite rounded-bottom">
+ <div class="f-row" style="justify-content: space-around; width: 100%;">
+ <div>Sačuvaj izmene</div>
+ <div class="icon-double pt-1">
+ <mat-icon>check</mat-icon>
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts b/frontend/src/app/_elements/column-table/column-table.component.spec.ts
index 603889b2..360a8109 100644
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts
+++ b/frontend/src/app/_elements/column-table/column-table.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemDatasetComponent } from './item-dataset.component';
+import { ColumnTableComponent } from './column-table.component';
-describe('ItemDatasetComponent', () => {
- let component: ItemDatasetComponent;
- let fixture: ComponentFixture<ItemDatasetComponent>;
+describe('ColumnTableComponent', () => {
+ let component: ColumnTableComponent;
+ let fixture: ComponentFixture<ColumnTableComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemDatasetComponent ]
+ declarations: [ ColumnTableComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemDatasetComponent);
+ fixture = TestBed.createComponent(ColumnTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/column-table/column-table.component.ts b/frontend/src/app/_elements/column-table/column-table.component.ts
new file mode 100644
index 00000000..c200e674
--- /dev/null
+++ b/frontend/src/app/_elements/column-table/column-table.component.ts
@@ -0,0 +1,346 @@
+import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChildren } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import Experiment, { ColumnEncoding, Encoding, ColumnType, NullValueOptions } from 'src/app/_data/Experiment';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+import { EncodingDialogComponent } from 'src/app/_modals/encoding-dialog/encoding-dialog.component';
+import { MatDialog } from '@angular/material/dialog';
+import { MissingvaluesDialogComponent } from 'src/app/_modals/missingvalues-dialog/missingvalues-dialog.component';
+import { MatCheckboxChange } from '@angular/material/checkbox';
+import { CsvParseService } from 'src/app/_services/csv-parse.service';
+import { ProblemType } from 'src/app/_data/Model';
+import { ExperimentsService } from 'src/app/_services/experiments.service';
+import { SaveExperimentDialogComponent } from 'src/app/_modals/save-experiment-dialog/save-experiment-dialog.component';
+import { AlertDialogComponent } from 'src/app/_modals/alert-dialog/alert-dialog.component';
+import Shared from 'src/app/Shared';
+
+@Component({
+ selector: 'app-column-table',
+ templateUrl: './column-table.component.html',
+ styleUrls: ['./column-table.component.css']
+})
+export class ColumnTableComponent implements AfterViewInit {
+
+ @Input() dataset?: Dataset;
+ @Input() experiment!: Experiment;
+ @Output() okPressed: EventEmitter<string> = new EventEmitter();
+ @Output() columnTableChanged = new EventEmitter();
+
+ Object = Object;
+ Encoding = Encoding;
+ NullValueOptions = NullValueOptions;
+ ColumnType = ColumnType;
+ ProblemType = ProblemType;
+ tableData?: any[][];
+ nullValOption: string[] = [];
+
+ columnsChecked: boolean[] = []; //niz svih kolona
+ loaded: boolean = false;
+
+
+ constructor(private datasetService: DatasetsService, private experimentService: ExperimentsService, public csvParseService: CsvParseService, public dialog: MatDialog) {
+ //ovo mi nece trebati jer primam dataset iz druge komponente
+ }
+
+ loadDataset(dataset: Dataset) {
+ this.dataset = dataset;
+
+ this.setColumnTypeInitial();
+
+ this.dataset.columnInfo.forEach(column => {
+ this.columnsChecked.push(true);
+ });
+
+ this.resetInputColumns();
+ this.resetOutputColumn();
+ this.resetColumnEncodings(Encoding.Label);
+ this.setDeleteRowsForMissingValTreatment();
+
+ this.nullValOption = [];
+ this.dataset.columnInfo.forEach(colInfo => {
+ this.nullValOption.push(`Obriši redove (${colInfo.numNulls})`);
+ });
+
+ this.datasetService.getDatasetFilePartial(this.dataset.fileId, 0, 10).subscribe((response: string | undefined) => {
+ if (response && this.dataset != undefined) {
+ this.tableData = this.csvParseService.csvToArray(response, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter.toString() == "") ? "," : this.dataset.delimiter);
+ }
+ });
+ this.loaded = true;
+ }
+
+ ngAfterViewInit(): void {
+
+ }
+
+ setColumnTypeInitial() {
+ if (this.dataset != undefined) {
+ for (let i = 0; i < this.dataset.columnInfo.length; i++) {
+ this.experiment.columnTypes[i] = (this.dataset.columnInfo[i].isNumber) ? ColumnType.numerical : ColumnType.categorical;
+ }
+ }
+ }
+
+ resetInputColumns() {
+ if (this.dataset != undefined) {
+ this.experiment.inputColumns = [];
+ for (let i = 0; i < this.dataset?.columnInfo.length; i++) {
+ this.experiment.inputColumns.push(this.dataset.columnInfo[i].columnName);
+ }
+ }
+ }
+ resetOutputColumn() {
+ this.experiment.outputColumn = this.experiment.inputColumns[0];
+ }
+
+ setDeleteRowsForMissingValTreatment() {
+ if (this.experiment != undefined) {
+ this.experiment.nullValues = NullValueOptions.DeleteRows;
+ this.experiment.nullValuesReplacers = [];
+ for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ this.experiment.nullValuesReplacers.push({
+ column: this.experiment.inputColumns[i],
+ option: NullValueOptions.DeleteRows,
+ value: ""
+ });
+ }
+ }
+ }
+
+ columnTableChangeDetected() {
+ this.columnTableChanged.emit();
+ }
+
+ columnTypeChanged(columnName: string) {
+ if (this.experiment.outputColumn == columnName)
+ this.changeOutputColumn(columnName);
+ else
+ this.columnTableChangeDetected();
+ }
+
+ changeInputColumns(targetMatCheckbox: MatCheckboxChange, columnName: string) {
+ if (this.experiment != undefined) {
+
+ if (targetMatCheckbox.checked) {
+ if (this.experiment.inputColumns.filter(x => x == columnName)[0] == undefined) {
+ this.experiment.inputColumns.push(columnName);
+ }
+ }
+ else {
+ this.experiment.inputColumns = this.experiment.inputColumns.filter(x => x != columnName);
+ //console.log("Input columns: ", this.experiment.inputColumns);
+ //TODO: da se zatamni kolona koja je unchecked
+ //this.experiment.encodings = this.experiment.encodings.filter(x => x.columnName != columnName); samo na kraju iz enkodinga skloni necekirane
+ this.experiment.nullValuesReplacers = this.experiment.nullValuesReplacers.filter(x => x.column != columnName);
+ if (columnName == this.experiment.outputColumn)
+ this.experiment.outputColumn = this.experiment.inputColumns[0];
+ }
+ this.columnTableChangeDetected();
+ }
+ }
+
+ changeOutputColumn(columnName: string) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+ let i = this.dataset.columnInfo.findIndex(x => x.columnName == this.experiment!.outputColumn);
+ if (this.experiment.columnTypes[i] == ColumnType.numerical) {
+ this.experiment.type = ProblemType.Regression;
+ }
+ else {
+ if (this.dataset.columnInfo[i].uniqueValues!.length == 2)
+ this.experiment.type = ProblemType.BinaryClassification;
+ else
+ this.experiment.type = ProblemType.MultiClassification;
+ }
+ this.columnTableChangeDetected();
+ }
+ }
+
+ resetColumnEncodings(encodingType: Encoding) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+ this.experiment.encodings = [];
+ for (let i = 0; i < this.dataset?.columnInfo.length; i++) {
+ this.experiment.encodings.push(new ColumnEncoding(this.dataset?.columnInfo[i].columnName, encodingType));
+ //console.log(this.experiment.encodings);
+ }
+ this.columnTableChangeDetected();
+ }
+ }
+ openEncodingDialog() {
+ const dialogRef = this.dialog.open(EncodingDialogComponent, {
+ width: '300px'
+ });
+ dialogRef.afterClosed().subscribe(selectedEncoding => {
+ if (selectedEncoding != undefined)
+ this.resetColumnEncodings(selectedEncoding);
+ });
+ }
+
+ resetMissingValuesTreatment(selectedMissingValuesOption: NullValueOptions) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+
+ if (selectedMissingValuesOption == NullValueOptions.DeleteColumns) {
+ this.experiment.nullValues = NullValueOptions.DeleteColumns;
+ this.experiment.nullValuesReplacers = [];
+ for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ this.experiment.nullValuesReplacers.push({
+ column: this.experiment.inputColumns[i],
+ option: NullValueOptions.DeleteColumns,
+ value: ""
+ });
+ this.nullValOption[i] = "Obriši kolonu";
+ }
+ }
+ else if (selectedMissingValuesOption == NullValueOptions.DeleteRows) {
+ this.experiment.nullValues = NullValueOptions.DeleteRows;
+ this.experiment.nullValuesReplacers = [];
+ for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ this.experiment.nullValuesReplacers.push({
+ column: this.experiment.inputColumns[i],
+ option: NullValueOptions.DeleteRows,
+ value: ""
+ });
+ let numOfRowsToDelete = (this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.inputColumns[i])[0]).numNulls;
+ this.nullValOption[i] = "Obriši redove (" + numOfRowsToDelete + ")";
+ }
+ }
+ this.columnTableChangeDetected();
+ }
+ }
+ openMissingValuesDialog() {
+ const dialogRef = this.dialog.open(MissingvaluesDialogComponent, {
+ width: '400px'
+ });
+ dialogRef.afterClosed().subscribe(selectedMissingValuesOption => {
+ if (selectedMissingValuesOption != undefined)
+ this.resetMissingValuesTreatment(selectedMissingValuesOption);
+ });
+ }
+
+ openSaveExperimentDialog() {
+ const dialogRef = this.dialog.open(SaveExperimentDialogComponent, {
+ width: '400px'
+ });
+ dialogRef.afterClosed().subscribe(selectedName => {
+ this.experiment.name = selectedName;
+ //napravi odvojene dugmice za save i update -> za update nece da se otvara dijalog za ime
+ this.experimentService.addExperiment(this.experiment).subscribe((response) => {
+ this.experiment._id = response._id;
+ this.okPressed.emit();
+ });
+ });
+ }
+
+ openUpdateExperimentDialog() {
+ this.experimentService.updateExperiment(this.experiment).subscribe((response) => {
+ this.experiment = response;
+ Shared.openDialog("Izmena eksperimenta", "Uspešno ste izmenili podatke o eksperimentu.");
+ });
+ }
+
+ MissValsDeleteClicked(event: Event, replacementType: NullValueOptions, index: number) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+ let columnName = (<HTMLInputElement>event.currentTarget).value;
+ let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0];
+
+ if (arrayElement == undefined) {
+ this.experiment.nullValuesReplacers.push({
+ column: columnName,
+ option: (replacementType == NullValueOptions.DeleteColumns) ? NullValueOptions.DeleteColumns : NullValueOptions.DeleteRows,
+ value: ""
+ });
+ }
+ else {
+ arrayElement.option = (replacementType == NullValueOptions.DeleteColumns) ? NullValueOptions.DeleteColumns : NullValueOptions.DeleteRows;
+ arrayElement.value = "";
+ }
+
+ let numOfRowsToDelete = (this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.inputColumns[index])[0]).numNulls;
+ this.nullValOption[index] = (replacementType == NullValueOptions.DeleteColumns) ? "Obriši kolonu" : "Obriši redove (" + numOfRowsToDelete + ")";
+ this.columnTableChangeDetected();
+ }
+ }
+
+ MissValsReplaceClicked(event: Event, columnName: string, index: number) {
+ if (this.experiment != undefined) {
+ let fillValue = (<HTMLInputElement>event.currentTarget).value;
+ let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0];
+
+ if (arrayElement == undefined) {
+ this.experiment.nullValuesReplacers.push({
+ column: columnName,
+ option: NullValueOptions.Replace,
+ value: fillValue
+ });
+ }
+ else {
+ arrayElement.option = NullValueOptions.Replace;
+ arrayElement.value = fillValue;
+ }
+
+ this.nullValOption[index] = "Popuni sa: " + fillValue;
+ this.columnTableChangeDetected();
+ }
+ }
+ getValue(columnName: string): string {
+ if (<HTMLInputElement>document.getElementById(columnName) != undefined)
+ return (<HTMLInputElement>document.getElementById(columnName)).value;
+ return '0';
+ }
+ saveExperiment() {
+ this.openSaveExperimentDialog();
+ }
+ updateExperiment() {
+ this.openUpdateExperimentDialog();
+ }
+
+
+ tabs = [
+ new Tab(0, 'Podešavanja kolona', Table.Columns),
+ new Tab(1, 'Podaci', Table.Data),
+ new Tab(2, 'Korelaciona matrica', Table.CorrelationMatrix)
+ ]
+
+ selectedTab: Tab = this.tabs[0];
+ hoveringOverTab: (Tab | null) = null;
+
+ tabToDisplay: Table = Table.Columns;
+
+ selectTab(index: number) {
+ this.selectedTab = this.tabs[index];
+ this.tabToDisplay = this.tabs[index].value;
+ }
+
+ hoverOverTab(index: number) {
+ if (index < 0) {
+ this.hoveringOverTab = null;
+ this.tabToDisplay = this.selectedTab.value;
+ } else {
+ this.hoveringOverTab = this.tabs[index];
+ this.tabToDisplay = this.tabs[index].value;
+ }
+ }
+
+ calcZIndex(i: number) {
+ let zIndex = (this.tabs.length - i - 1)
+ if (this.selectedTab.index == i)
+ zIndex = this.tabs.length + 1;
+ if (this.hoveringOverTab?.index == i)
+ zIndex = this.tabs.length + 2;
+ return zIndex;
+ }
+
+ Table = Table;
+}
+
+export enum Table {
+ Columns,
+ Data,
+ CorrelationMatrix
+}
+
+export class Tab {
+ constructor(
+ public index: number,
+ public name: string,
+ public value: Table
+ ) { }
+}
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.css b/frontend/src/app/_elements/dataset-load/dataset-load.component.css
deleted file mode 100644
index ff6e2750..00000000
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.btnType1 {
- background-color: #003459;
- color: white;
- padding-top: 2vh;
- padding-bottom: 2vh;
-}
-.btnType2 {
- background-color: white;
- color: #003459;
- border-color: #003459;
- padding-top: 2vh;
- padding-bottom: 2vh;
-
-}
-.selectedDatasetClass {
- /*border-color: 2px solid #003459;*/
- background-color: lightblue;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.html b/frontend/src/app/_elements/dataset-load/dataset-load.component.html
deleted file mode 100644
index f244a882..00000000
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<div>
-
- <!--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}">
- 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}">
- 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="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>
- </ul>
- </div>
- <div class="px-5 mt-5">
- <app-datatable [tableData]="tableData"></app-datatable>
- </div>
- </div>
-
- <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 be1dc097..73dbf2d2 100644
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
+++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
@@ -8,7 +8,6 @@ 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',
@@ -34,15 +33,7 @@ export class DatasetLoadComponent implements OnInit {
term: string = "";
- constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService, private authService: AuthService) {
- this.fetchDatasets();
-
- authService.loggedInEvent.subscribe(_ => {
- this.fetchDatasets();
- })
- }
-
- fetchDatasets() {
+ constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService) {
this.datasets.getMyDatasets().subscribe((datasets) => {
this.myDatasets = datasets;
});
@@ -61,6 +52,13 @@ export class DatasetLoadComponent implements OnInit {
//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;
@@ -90,19 +88,10 @@ 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", (dName: string, dId: string) => {
- this.refreshMyDatasets(dId);
+ this.signalRService.hubConnection.on("NotifyDataset", _ => {
+ this.refreshMyDatasets();
});
} else {
console.warn("Dataset-Load: No connection!");
diff --git a/frontend/src/app/_elements/datatable/datatable.component.html b/frontend/src/app/_elements/datatable/datatable.component.html
index 8db62aff..27d66dd3 100644
--- a/frontend/src/app/_elements/datatable/datatable.component.html
+++ b/frontend/src/app/_elements/datatable/datatable.component.html
@@ -1,18 +1,11 @@
-<div *ngIf="tableData.hasInput">
- <div>
- <div *ngIf="!tableData.loaded" backgroundColor="secondary" style="width: 100%; height: 100%;"
- class="d-flex justify-content-center align-items-center">
+<div *ngIf="tableData.hasInput" class="position-relative">
+ <div class="text-white">
+ <div *ngIf="!tableData.loaded" style="width: 100%; height: 100%;" class="d-flex justify-content-center align-items-center">
<app-loading></app-loading>
</div>
<div *ngIf="tableData.loaded && tableData.data">
- <div id="info" *ngIf="tableData.data.length > 0 && tableData.data[0].length > 0"
- class="d-flex flex-row justify-content-center align-items-center">
- <div class="fs-5 mb-3">
- Tabela {{tableData.numCols}}x{{tableData.numRows}}
- </div>
- </div>
- <div class="table-responsive" style="overflow: auto; border-radius: 5px;">
- <table *ngIf="tableData.data.length > 0 && tableData.hasHeader && tableData.data[0].length > 0" class="table table-bordered table-light">
+ <div style="border-radius: 5px; overflow-x: auto; overflow-y: hidden;">
+ <table *ngIf="tableData.data.length && tableData.data[0].length > 0" class="table table-responsive table-sm text-offwhite row-height">
<thead>
<tr>
<th *ngFor="let item of tableData.data[0]; let i = index">{{item}}</th>
@@ -22,22 +15,23 @@
<tr *ngFor="let row of tableData.data | slice:1">
<td *ngFor="let col of row">{{col}}</td>
</tr>
- <tr>
- <td colspan="100" class="text-lg-center fs-6">+ {{tableData.numRows - 11}} redova...</td>
- </tr>
- </tbody>
- </table>
- <table *ngIf="tableData.data.length > 0 && !tableData.hasHeader && tableData.data[0].length > 0" class="table table-bordered table-light">
- <tbody>
- <tr *ngFor="let row of tableData.data">
- <td *ngFor="let col of row">{{col}}</td>
- </tr>
- <tr>
- <td colspan="100" class="text-lg-center fs-6">+ {{tableData.numRows - 10}} redova...</td>
- </tr>
</tbody>
</table>
</div>
+ <div class="row" >
+ <div id="info" *ngIf="tableData.data.length > 0 && tableData.data[0].length > 0" class="d-flex flex-row justify-content-right align-items-right col-sm">
+ <div class=" mb-3">
+ Tabela {{tableData.numCols}} x {{tableData.numRows}}
+ </div>
+ </div>
+ <div class="col-sm"></div>
+ <div class="col-sm"></div>
+ <div class="footer-center col-sm">Prikazano 10/{{tableData.numRows}} redova</div>
+ <div class="col-sm"></div>
+ <div class="col-sm"></div>
+ <div class="col-sm"></div>
+
+ </div>
</div>
</div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/datatable/datatable.component.ts b/frontend/src/app/_elements/datatable/datatable.component.ts
index 82374f4d..560a1c21 100644
--- a/frontend/src/app/_elements/datatable/datatable.component.ts
+++ b/frontend/src/app/_elements/datatable/datatable.component.ts
@@ -18,7 +18,6 @@ export class DatatableComponent implements OnInit {
export class TableData {
constructor(
- public hasHeader = true,
public hasInput = false,
public loaded = false,
public numRows = 0,
diff --git a/frontend/src/app/_elements/folder/folder.component.css b/frontend/src/app/_elements/folder/folder.component.css
new file mode 100644
index 00000000..62324d62
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.css
@@ -0,0 +1,213 @@
+#folder {
+ /*position: absolute;
+ left: 50%;
+ transform: translateX(-50%);*/
+}
+
+#tabs {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ height: 3.1rem;
+}
+
+#tabs>.folder-tab:not(:first-child) {
+ margin-left: -5px;
+}
+
+.folder-tab {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ position: relative;
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-color: var(--ns-primary);
+ color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 0 1px;
+}
+
+.folder-tab:not(:first-child) {
+ margin-block-start: auto;
+}
+
+.selected-tab {
+ height: 3rem;
+ background-color: var(--ns-primary);
+ color: var(--offwhite);
+}
+
+.hover-tab {
+ height: 3.2rem;
+}
+
+.selected-tab,
+.hover-tab {
+ width: fit-content !important;
+}
+
+.tab-link {
+ color: var(--offwhite) !important;
+ text-decoration: none !important;
+ cursor: pointer;
+ padding: 0.5rem;
+}
+
+.tab-link:active {
+ text-decoration: underline !important;
+}
+
+.selected-tab {
+ background-color: var(--ns-primary);
+}
+
+#searchbar {
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-bottom: 1px solid var(--ns-primary);
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: flex-start;
+ width: 100%;
+}
+
+.collapse-horizontal {
+ white-space: nowrap;
+ height: 2.5rem;
+ overflow-x: hidden;
+}
+
+#search-options {
+ margin-left: auto;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ height: 100%;
+}
+
+#selected-content {
+ background-color: var(--ns-bg-dark-50);
+ width: 100%;
+ /*backdrop-filter: blur(2px);*/
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 1px 1px;
+ border-top-right-radius: 4px;
+}
+
+#footer {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+}
+
+.bottom-button {
+ font-size: large;
+ position: relative;
+ background-color: var(--ns-primary);
+ width: 10rem;
+ height: 2.3rem;
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 0px 1px 1px 1px;
+}
+
+.rounded-bottom {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.separator {
+ border-left-color: var(--ns-primary);
+ border-left-width: 1px;
+ border-left-style: solid;
+}
+
+.list-view {
+ height: 100%;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+}
+
+.list-item {
+ height: 3rem;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ border-bottom: 1px solid var(--ns-primary);
+ flex-shrink: 0;
+}
+
+.list-item:hover {
+ background-color: var(--ns-bg-dark-100);
+ box-shadow: 0px 3px 3px var(--ns-primary);
+}
+
+.list-item:hover>.hover-hide {
+ display: none;
+}
+
+.hover-show {
+ display: none;
+}
+
+.list-item:hover>.hover-show {
+ display: initial;
+}
+
+.list-add {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ flex-grow: 1;
+ height: 100%;
+}
+
+.folder-inside {
+ width: 100%;
+ height: 40rem;
+ overflow-y: auto;
+}
+
+.file-content {
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+
+.file-bottom-buttons {
+ position: absolute;
+ bottom: 15px;
+ right: 15px;
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+.file-button {
+ position: relative;
+ color: var(--offwhite);
+ border-radius: 4px;
+ border: 1px solid var(--ns-primary);
+ margin: 5px;
+ padding: 5px;
+ cursor: pointer;
+ z-index: 1001;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+.file-button:hover {
+ background-color: var(--ns-primary);
+}
+
+.form-hidden {
+ display: none;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/folder/folder.component.html b/frontend/src/app/_elements/folder/folder.component.html
new file mode 100644
index 00000000..bff066be
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.html
@@ -0,0 +1,111 @@
+<div id="folder">
+ <div id="tabs">
+ <div id="new-file-tab" *ngIf="newFile" class="folder-tab p-1 rounded-top" [style]="'z-index:' + (selectedTab == TabType.NewFile ? 11 : 10) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == TabType.NewFile, 'hover-tab' : hoverTab == TabType.NewFile}">
+ <mat-icon class="text-offwhite">add</mat-icon>
+ <a class="stretched-link tab-link" (click)="selectTab(TabType.NewFile)" (mouseenter)="hoverOverTab(TabType.NewFile)" (mouseleave)="hoverOverTab(TabType.None)">
+ {{newFile.name}}
+ </a>
+ </div>
+ <!--<div class="folder-tab p-1 rounded-top" *ngFor="let file of filteredFiles; let i = index" [style]="'z-index:' + calcZIndex(i) + ' ;'" [ngClass]="{'selected-tab' : selectedFileIndex == i, 'hover-tab' : hoveringOverFileIndex == i}">
+ <a class="m-1 stretched-link tab-link" (click)="selectFile(i)" (mouseenter)="hoverOverFile(i)" (mouseleave)="hoverOverFile(-1)">{{file.name}}</a>
+ </div>-->
+ <div class="folder-tab p-1 rounded-top" *ngFor="let tab of tabsToShow; let i = index" [style]="'z-index:' + (selectedTab == tab ? 11 : (tabsToShow.length - i)) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == tab, 'hover-tab' : hoverTab == tab}">
+ <a class="m-1 stretched-link tab-link" (click)="selectTab(tab)" (mouseenter)="hoverOverTab(tab)" (mouseleave)="hoverOverTab(TabType.None)">{{tabTitles[tab]}}</a>
+ </div>
+
+ <div class="folder-tab p-1 rounded-top" *ngIf="selectedFile" [style]="'z-index:' + (selectedTab == TabType.File ? 11 : (tabsToShow.length)) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == TabType.File, 'hover-tab' : hoverTab == TabType.File}">
+ <a class="m-1 stretched-link tab-link" (click)="selectTab(TabType.File)" (mouseenter)="hoverOverTab(TabType.File)" (mouseleave)="hoverOverTab(TabType.None)">{{selectedFile.name}}</a>
+ </div>
+ </div>
+ <div id="selected-content" class="rounded-bottom text-offwhite">
+ <div id="searchbar" *ngIf="listView">
+ <!-- <div id="path" class="ps-2">{{folderName}}
+ </div>
+ <mat-icon>keyboard_arrow_right</mat-icon> -->
+ <div id="search" class="text-offwhite mx-1">
+ <mat-form-field>
+ <button matPrefix class="btn-clear input-icon"><mat-icon>search</mat-icon></button>
+ <input type="search" matInput name="search" [(ngModel)]="searchTerm" (input)="searchTermsChanged()">
+ <button matSuffix class="btn-clear input-icon" (click)="clearSearchTerm()"><mat-icon>clear</mat-icon></button>
+ </mat-form-field>
+ </div>
+ <div id="search-options">
+ <!-- <div id="collapseFilters" class="collapse collapse-horizontal">
+ <mat-icon class="text-offwhite ">timeline</mat-icon>
+ Regresioni
+ <mat-icon class="text-offwhite ">looks_two</mat-icon>
+ Binarni klasifikacioni
+ <mat-icon class="text-offwhite ">auto_awesome_motion</mat-icon>
+ Multiklasifikacioni
+ </div> -->
+ <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters">
+ <mat-icon>filter_alt</mat-icon>
+ </button>
+ <!-- <div id="collapseSort" class="collapse collapse-horizontal">
+ [sort options here TODO]
+ </div> -->
+ <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseSort" aria-expanded="false" aria-controls="collapseSort">
+ <mat-icon>sort</mat-icon>
+ </button>
+ <!-- <button class="btn-clear icon-toggle separator" [ngClass]="{'icon-toggle-on': listView}" (click)="toggleListView()">
+ <mat-icon>view_list</mat-icon>
+ </button> -->
+ </div>
+ </div>
+ <!--{{fileToDisplay ? fileToDisplay.name : 'No file selected.'}} {{selectedFileIndex}} {{hoveringOverFileIndex}}-->
+ <div class="folder-inside bg-blur">
+ <div class="file-content" [ngClass]="{'form-hidden' : listView}">
+ <div class="file-bottom-buttons" *ngIf="selectedTab != TabType.NewFile">
+ <button *ngIf="this.selectedFile && selectedTab == TabType.File" class="btn-clear file-button" (click)="deleteFile(this.selectedFile, $event)">
+ <mat-icon>delete</mat-icon>
+ </button>
+ <!-- <button class="btn-clear file-button">
+ <mat-icon>zoom_out_map</mat-icon>
+ </button> -->
+ </div>
+ <app-form-model [ngClass]="{'form-hidden': type != FolderType.Model}" [forExperiment]="forExperiment"></app-form-model>
+ <app-form-dataset [ngClass]="{'form-hidden': type != FolderType.Dataset}" [forExperiment]="forExperiment"></app-form-dataset>
+ </div>
+ <div [ngClass]="{'form-hidden' : !listView}" class="list-view">
+ <div *ngFor="let file of filteredFiles; let i = index" class="list-item force-link" (click)="selectFile(file)">
+ <div class="mx-2">
+ {{file.name}}
+ </div>
+ <div class="mx-2 hover-hide">
+ {{file.lastUpdated | date}}
+ </div>
+ <div class="mx-2 hover-show" *ngIf="selectedTab !== TabType.PublicDatasets && selectedTab !== TabType.PublicModels">
+ <button class="btn-clear file-button" (click)="deleteFile(file, $event)">
+ <mat-icon>delete</mat-icon>
+ </button>
+ </div>
+
+ </div>
+ <div class="list-add" [ngSwitch]="type">
+ <button mat-raised-button *ngSwitchCase="FolderType.Dataset" (click)="selectNewFile()">Dodaj {{privacy == Privacy.Public ? 'javni ' : ' '}}izvor podataka</button>
+ <button mat-raised-button *ngSwitchCase="FolderType.Model" (click)="selectNewFile()">Dodaj {{privacy == Privacy.Public ? 'javnu ' : ' '}}konfiguraciju neuronske mreže</button>
+ <button mat-raised-button *ngSwitchCase="FolderType.Experiment" routerLink="/experiment">Dodaj eksperiment</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer" [ngSwitch]="newFileSelected" *ngIf="!listView">
+ <button mat-button (click)="saveNewFile()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="true">
+ <div class="f-row">
+ <div>Sačuvaj</div>
+ <div class="pt-1">
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ <button mat-button (click)="ok()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="false">
+ <div class="f-row">
+ <div>Ok</div>
+ <div class="icon-double pt-1">
+ <mat-icon>check</mat-icon>
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/carousel/carousel.component.spec.ts b/frontend/src/app/_elements/folder/folder.component.spec.ts
index 9196e044..33a573a7 100644
--- a/frontend/src/app/_elements/carousel/carousel.component.spec.ts
+++ b/frontend/src/app/_elements/folder/folder.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { CarouselComponent } from './carousel.component';
+import { FolderComponent } from './folder.component';
-describe('CarouselComponent', () => {
- let component: CarouselComponent;
- let fixture: ComponentFixture<CarouselComponent>;
+describe('FolderComponent', () => {
+ let component: FolderComponent;
+ let fixture: ComponentFixture<FolderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ CarouselComponent ]
+ declarations: [ FolderComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(CarouselComponent);
+ fixture = TestBed.createComponent(FolderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts
new file mode 100644
index 00000000..fabb524c
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.ts
@@ -0,0 +1,379 @@
+import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import { FolderFile, FolderType } from 'src/app/_data/FolderFile';
+import Model from 'src/app/_data/Model';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+import Shared from 'src/app/Shared';
+import { ModelsService } from 'src/app/_services/models.service';
+import { FormDatasetComponent } from '../form-dataset/form-dataset.component';
+import Experiment from 'src/app/_data/Experiment';
+import { ExperimentsService } from 'src/app/_services/experiments.service';
+import { PredictorsService } from 'src/app/_services/predictors.service';
+import { SignalRService } from 'src/app/_services/signal-r.service';
+import { FormModelComponent } from '../form-model/form-model.component';
+
+@Component({
+ selector: 'app-folder',
+ templateUrl: './folder.component.html',
+ styleUrls: ['./folder.component.css']
+})
+export class FolderComponent implements AfterViewInit {
+
+ @ViewChild(FormDatasetComponent) formDataset!: FormDatasetComponent;
+ @ViewChild(FormModelComponent) formModel!: FormModelComponent;
+
+ @Input() folderName: string = 'Moji podaci';
+ @Input() files!: FolderFile[]
+
+ newFile?: Dataset | Model;
+
+ @Input() type: FolderType = FolderType.Dataset;
+ @Input() forExperiment!: Experiment;
+ @Input() startingTab!: TabType;
+
+ newFileSelected: boolean = true;
+
+ selectedFileIndex: number = -1;
+ selectedFile?: FolderFile;
+ hoveringOverFileIndex: number = -1;
+
+ fileToDisplay?: FolderFile;
+
+ @Output() selectedFileChanged: EventEmitter<FolderFile> = new EventEmitter();
+ @Output() okPressed: EventEmitter<string> = new EventEmitter();
+
+ searchTerm: string = '';
+
+ constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService, private signalRService: SignalRService) {
+ this.tabsToShow.forEach(tab => this.folders[tab] = []);
+ }
+
+ ngAfterViewInit(): void {
+ this.refreshFiles(null);
+
+ if (this.signalRService.hubConnection) {
+ this.signalRService.hubConnection.on("NotifyDataset", (dName: string, dId: string) => {
+ if (this.type == FolderType.Dataset) {
+ this.refreshFiles(dId);
+ }
+ });
+ } else {
+ console.warn("Dataset-Load: No connection!");
+ }
+ }
+
+ displayFile() {
+ if (this.type == FolderType.Dataset)
+ this.formDataset.dataset = <Dataset>this.fileToDisplay;
+ else if (this.type == FolderType.Model)
+ this.formModel.newModel = <Model>this.fileToDisplay;
+ }
+
+ hoverOverFile(i: number) {
+ /*this.hoveringOverFileIndex = i;
+ if (i != -1) {
+ this.fileToDisplay = this.files[i];
+ } else {
+ if (this.newFileSelected) {
+ this.fileToDisplay = this.newFile;
+ } else {
+ this.fileToDisplay = this.files[this.selectedFileIndex];
+ }
+ }
+ this.displayFile();*/
+ }
+
+ selectNewFile() {
+ if (!this.newFile) {
+ this.createNewFile();
+ }
+ this.fileToDisplay = this.newFile;
+ this.newFileSelected = true;
+ this.listView = false;
+ this.displayFile();
+ if(this.type == FolderType.Dataset)
+ this.formDataset.clear();
+ }
+
+ selectFile(file?: FolderFile) {
+ this.selectedFile = file;
+ this.fileToDisplay = file;
+ this.newFileSelected = false;
+ this.listView = false;
+ this.selectedFileChanged.emit(this.selectedFile);
+ this.selectTab(TabType.File);
+ this.displayFile();
+
+ if(this.type == FolderType.Dataset)
+ this.formDataset.loadExisting();
+ }
+
+ createNewFile() {
+ if (this.type == FolderType.Dataset) {
+ this.newFile = new Dataset();
+ } else if (this.type == FolderType.Model) {
+ this.newFile = new Model();
+ }
+ }
+
+ ok() {
+ this.okPressed.emit();
+ }
+
+ _initialized: boolean = false;
+
+ refreshFiles(selectedDatasetId: string | null) {
+ this.files = []
+ this.filteredFiles.length = 0;
+ this.folders[TabType.NewFile] = [];
+ this.folders[TabType.File] = [];
+ this.tabsToShow.forEach(tab => {
+ this.folders[tab] = [];
+ });
+
+ this.datasetsService.getMyDatasets().subscribe((datasets) => {
+ this.folders[TabType.MyDatasets] = datasets;
+ if (selectedDatasetId) {
+ this.selectFile(datasets.filter(x => x._id == selectedDatasetId)[0]);
+ }
+ });
+
+ this.experimentsService.getMyExperiments().subscribe((experiments) => {
+ this.folders[TabType.MyExperiments] = experiments;
+ });
+
+ this.datasetsService.getPublicDatasets().subscribe((datasets) => {
+ this.folders[TabType.PublicDatasets] = datasets;
+ });
+
+ this.modelsService.getMyModels().subscribe((models) => {
+ this.folders[TabType.MyModels] = models;
+ });
+
+ /*this.modelsService.getMyModels().subscribe((models) => {
+ this.folders[TabType.PublicModels] = models;
+ });*/
+ this.folders[TabType.PublicModels] = [];
+
+ this.experimentsService.getMyExperiments().subscribe((experiments) => {
+ this.folders[TabType.MyExperiments] = experiments;
+ });
+
+ if (!this._initialized) {
+ this.files = this.folders[this.startingTab];
+ this.filteredFiles = [];
+ this.selectTab(this.startingTab);
+ this._initialized = true;
+ }
+
+ this.searchTermsChanged();
+ }
+
+ saveNewFile() {
+ switch (this.type) {
+ case FolderType.Dataset:
+ this.formDataset!.uploadDataset((dataset: Dataset) => {
+ this.newFile = undefined;
+ Shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira.");
+ this.refreshFiles(null);
+ },
+ () => {
+ Shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset.");
+ });
+ break;
+ case FolderType.Model:
+ this.modelsService.addModel(this.formModel.newModel).subscribe(model => {
+ this.newFile = undefined;
+ Shared.openDialog("Obaveštenje", "Uspešno ste dodali novu konfiguraciju neuronske mreže u kolekciju.");
+ this.refreshFiles(null); // todo select model
+ }, (err) => {
+ Shared.openDialog("Neuspeo pokušaj!", "Konfiguracija neuronske mreže sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeću konfiguraciju.");
+ });
+ break;
+ }
+ }
+
+
+ /*calcZIndex(i: number) {
+ let zIndex = (this.files.length - i - 1)
+ if (this.selectedFileIndex == i)
+ zIndex = this.files.length + 2;
+ if (this.hoveringOverFileIndex == i)
+ zIndex = this.files.length + 3;
+ return zIndex;
+ }
+
+ newFileZIndex() {
+ return (this.files.length + 1);
+ }*/
+
+ clearSearchTerm() {
+ this.searchTerm = '';
+ this.searchTermsChanged();
+ }
+
+ filteredFiles: FolderFile[] = [];
+
+ searchTermsChanged() {
+ this.filteredFiles.length = 0;
+ if (!this.files) return;
+ this.filteredFiles.push(...this.files.filter((file) => file.name.toLowerCase().includes(this.searchTerm.toLowerCase())));
+ /*if (this.selectedFile) {
+ if (!this.filteredFiles.includes(this.selectedFile)) {
+ if (this.hoverTab === TabType.None && this.getFolderType(this.selectedTab) === this.type) {
+ this.selectFile(undefined);
+ console.log(this.getFolderType(this.selectedTab), this.type);
+ }
+ } else {
+ //this.selectedFileIndex = this.filteredFiles.indexOf(this.selectedFile);
+ }
+ }*/
+ }
+
+ listView: boolean = true;
+
+ toggleListView() {
+ this.listView = !this.listView;
+ }
+
+ deleteFile(file: FolderFile, event: Event) {
+ event.stopPropagation();
+ //console.log('delete');
+ switch (this.type) {
+ case FolderType.Dataset:
+ this.datasetsService.deleteDataset(<Dataset>file).subscribe((response) => {
+ this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1);
+ this.refreshFiles(null);
+ });
+ break;
+ case FolderType.Model:
+ this.modelsService.deleteModel(<Model>file).subscribe((response) => {
+ this.refreshFiles(null);
+ });
+ break;
+ case FolderType.Experiment:
+ // this.experimentsService.deleteExperiment(<Model>file).subscribe((response) => {
+ // console.log(response);
+ // });
+ //todo delete za predictor
+ break;
+ }
+ }
+
+ folders: { [tab: number]: FolderFile[] } = {};
+
+ tabTitles: { [tab: number]: string } = {
+ [TabType.File]: 'Fajl',
+ [TabType.NewFile]: 'Novi fajl',
+ [TabType.MyDatasets]: 'Moji izvori podataka',
+ [TabType.PublicDatasets]: 'Javni izvori podataka',
+ [TabType.MyModels]: 'Moje konfiguracije neuronske mreže',
+ [TabType.PublicModels]: 'Javne konfiguracije neuronske mreže',
+ [TabType.MyExperiments]: 'Eksperimenti',
+ };
+
+ FolderType = FolderType;
+ Privacy = Privacy;
+ TabType = TabType;
+
+ privacy: Privacy = Privacy.Private;
+
+ @Input() tabsToShow: TabType[] = [
+ TabType.MyDatasets,
+ TabType.PublicDatasets,
+ TabType.MyModels,
+ TabType.PublicModels,
+ TabType.MyExperiments
+ ]
+
+ @Input() selectedTab: TabType = TabType.NewFile;
+ hoverTab: TabType = TabType.None;
+
+ selectTab(tab: TabType) {
+ setTimeout(() => {
+ if (tab == TabType.NewFile) {
+ this.selectNewFile();
+ }
+
+ this.listView = this.getListView(tab);
+ this.type = this.getFolderType(tab);
+ this.privacy = this.getPrivacy(tab);
+ this.selectedTab = tab;
+ this.files = this.folders[tab];
+
+ if (tab !== TabType.File && tab !== TabType.NewFile)
+ this.searchTermsChanged();
+ });
+ }
+
+ getListView(tab: TabType) {
+ switch (tab) {
+ case TabType.File:
+ case TabType.NewFile:
+ case TabType.None:
+ return false;
+ case TabType.MyExperiments:
+ case TabType.MyDatasets:
+ case TabType.MyModels:
+ case TabType.PublicDatasets:
+ case TabType.PublicModels:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ getFolderType(tab: TabType) {
+ switch (tab) {
+ case TabType.MyExperiments:
+ return FolderType.Experiment;
+ case TabType.MyDatasets:
+ case TabType.PublicDatasets:
+ return FolderType.Dataset;
+ case TabType.MyModels:
+ case TabType.PublicModels:
+ return FolderType.Model;
+ default:
+ return this.type;
+ }
+ }
+
+ getPrivacy(tab: TabType) {
+ switch (tab) {
+ case TabType.PublicDatasets:
+ case TabType.PublicModels:
+ return Privacy.Public;
+ default:
+ return Privacy.Private;
+ }
+ }
+
+ hoverOverTab(tab: TabType) {
+ this.listView = this.getListView(tab);
+ this.privacy = this.getPrivacy(tab);
+ this.hoverTab = tab;
+ if (tab == TabType.None) {
+ this.listView = this.getListView(this.selectedTab);
+ this.files = this.folders[this.selectedTab];
+ } else {
+ this.files = this.folders[tab];
+ }
+ this.searchTermsChanged();
+ }
+}
+
+export enum Privacy {
+ Private,
+ Public
+}
+
+export enum TabType {
+ NewFile,
+ File,
+ MyDatasets,
+ PublicDatasets,
+ MyModels,
+ PublicModels,
+ MyExperiments,
+ None
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.css b/frontend/src/app/_elements/form-dataset/form-dataset.component.css
new file mode 100644
index 00000000..953daa0c
--- /dev/null
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.css
@@ -0,0 +1,64 @@
+.folderBox {
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+
+.topBar {
+ width: 100%;
+ margin: 1rem;
+ align-items: flex-start;
+}
+
+.topBar label{
+ font-size: 30px;
+}
+.topBar mat-form-field{
+ width: 250px;
+}
+
+.toptop{
+ margin-left: 1.5%;
+ width: 50%;
+}
+
+.fileButton{
+ margin-top: 10px;
+}
+
+.file-container {
+ border: 4px solid transparent;
+ position: relative;
+ margin-left: 3%;
+ width: 94%;
+ min-height: 400px;
+ height: 95%;
+}
+
+.dottedClass {
+ border: 4px dotted white;
+ border-radius: 25px;
+}
+
+.icon-display {
+ position: absolute;
+ top: 45%;
+ left: 50%;
+ transform: translate(-50%, -50%) scale(4);
+}
+
+.hidden {
+ visibility: hidden;
+}
+
+.file {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+}
+
+.file-container input{
+ border-radius: 5px;
+ left: 0%;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.html b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
new file mode 100644
index 00000000..281f9c05
--- /dev/null
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
@@ -0,0 +1,65 @@
+<div class="folderBox" *ngIf="dataset">
+
+ <div class="row" style="margin-right: 0;">
+ <div class="topBar">
+ <div class="row toptop">
+ <div class="col-sm mb-3">
+ <div class="fileButton">
+ <button type="button" mat-raised-button (click)="fileInput.click()">Dodaj izvor podataka</button>
+
+ </div>
+ </div>
+
+ <div class="col-sm">
+ <div role="group">
+ <div class="row">
+ <mat-form-field class="example-full-width" appearance="fill">
+ <mat-label>Naziv</mat-label>
+ <input type="text" matInput value="{{dataset?.name}}" [(ngModel)]="dataset.name">
+
+
+ <mat-error *ngIf="nameFormControl.hasError('required')">
+ Naziv je <strong>obavezan</strong>
+ </mat-error>
+ </mat-form-field>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm">
+ <mat-form-field appearance="fill">
+ <mat-label>Delimiter</mat-label>
+ <mat-select id="delimiterOptions" [(ngModel)]="dataset.delimiter" (change)="update()" value=",">
+ <mat-option *ngFor="let option of delimiterOptions" [value]="option">
+ {{ option }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="col-sm">
+
+ </div>
+ </div>
+ <div class="row" *ngIf="firstInput">
+ <label class=" mt-5">{{filename}}</label>
+
+ </div>
+ </div>
+ </div>
+
+
+ <div class="row" style="margin-right: 0;">
+ <div class="file-container" [ngClass]="{'dottedClass': !tableData.hasInput}">
+
+ <i class="material-icons-outlined icon-display" [ngClass]="{'hidden': tableData.hasInput}">file_upload</i>
+
+ <input class="file" id="file-upload" (change)="changeListener($event)" #fileInput type="file" accept=".csv">
+
+ <div class="mt-5 datatable">
+ <app-datatable [tableData]="tableData"></app-datatable>
+ </div>
+
+
+ </div>
+ </div>
+
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.spec.ts b/frontend/src/app/_elements/form-dataset/form-dataset.component.spec.ts
index 5601b57b..51491c58 100644
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.spec.ts
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { DatasetLoadComponent } from './dataset-load.component';
+import { FormDatasetComponent } from './form-dataset.component';
-describe('DatasetLoadComponent', () => {
- let component: DatasetLoadComponent;
- let fixture: ComponentFixture<DatasetLoadComponent>;
+describe('FormDatasetComponent', () => {
+ let component: FormDatasetComponent;
+ let fixture: ComponentFixture<FormDatasetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ DatasetLoadComponent ]
+ declarations: [ FormDatasetComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(DatasetLoadComponent);
+ fixture = TestBed.createComponent(FormDatasetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
index 1f395105..1eed2cdc 100644
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
@@ -1,38 +1,50 @@
-import { Component, EventEmitter, Output, ViewChild } from '@angular/core';
+import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import Dataset from 'src/app/_data/Dataset';
import { DatasetsService } from 'src/app/_services/datasets.service';
import { ModelsService } from 'src/app/_services/models.service';
import shared from 'src/app/Shared';
import { DatatableComponent, TableData } from '../datatable/datatable.component';
import { CsvParseService } from 'src/app/_services/csv-parse.service';
+import { FormControl, Validators } from '@angular/forms';
@Component({
- selector: 'app-add-new-dataset',
- templateUrl: './add-new-dataset.component.html',
- styleUrls: ['./add-new-dataset.component.css']
+ selector: 'app-form-dataset',
+ templateUrl: './form-dataset.component.html',
+ styleUrls: ['./form-dataset.component.css']
})
-export class AddNewDatasetComponent {
+export class FormDatasetComponent {
@ViewChild(DatatableComponent) datatable!: DatatableComponent;
- delimiterOptions: Array<string> = [",", ";", "|", "razmak", "novi red"]; //podrazumevano ","
+ nameFormControl = new FormControl('', [Validators.required, Validators.email]);
+
+ delimiterOptions: Array<string> = [",", ";", "|", "razmak", "novi red"]; //podrazumevano ","
csvRecords: any[] = [];
files: File[] = [];
rowsNumber: number = 0;
colsNumber: number = 0;
- dataset: Dataset; //dodaj ! potencijalno
+ @Input() dataset: Dataset; //dodaj ! potencijalno
tableData: TableData = new TableData();
+ @ViewChild('fileInput') fileInput!: ElementRef
+
+ filename: String;
+
constructor(private modelsService: ModelsService, private datasetsService: DatasetsService, private csv: CsvParseService) {
this.dataset = new Dataset();
this.dataset.delimiter = ',';
+ this.filename = "";
}
//@ViewChild('fileImportInput', { static: false }) fileImportInput: any; cemu je ovo sluzilo?
+ clear(){
+ this.tableData.hasInput = false;
+ }
+
changeListener($event: any): void {
this.files = $event.srcElement.files;
if (this.files.length == 0 || this.files[0] == null) {
@@ -42,12 +54,17 @@ export class AddNewDatasetComponent {
else
this.tableData.hasInput = true;
+ this.filename = this.files[0].name;
this.tableData.loaded = false;
this.update();
}
+ firstInput = false;
+
update() {
+ this.firstInput = true
+
if (this.files.length < 1)
return;
@@ -56,48 +73,73 @@ export class AddNewDatasetComponent {
if (typeof fileReader.result === 'string') {
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);
- else
- this.csvRecords = result.splice(0, 10);
+
+ this.csvRecords = result.splice(0, 11);
this.colsNumber = result[0].length;
this.rowsNumber = result.length;
- this.tableData.data = this.csvRecords
- this.tableData.hasHeader = this.dataset.hasHeader;
+ this.tableData.data = this.csvRecords;
this.tableData.loaded = true;
this.tableData.numCols = this.colsNumber;
this.tableData.numRows = this.rowsNumber;
}
}
fileReader.readAsText(this.files[0]);
+
+ this.dataset.name = this.filename.slice(0, this.filename.length - 4);
+ }
+
+ loadExisting(){
+ this.firstInput = false;
+
+ this.tableData.hasInput = true;
+ this.tableData.loaded = false;
+
+ this.datasetsService.getDatasetFile(this.dataset.fileId).subscribe((file: string | undefined) => {
+ if (file) {
+ this.tableData.loaded = true;
+ this.tableData.numRows = this.dataset.rowCount;
+ this.tableData.numCols = this.dataset.columnInfo.length;
+ this.tableData.data = this.csv.csvToArray(file, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter);
+
+ }
+ });
+
+
}
+ /*exportAsXLSX():void {
+ this.excelService.exportAsExcelFile(this.data, 'sample');
+ }*/
+
checkAccessible() {
if (this.dataset.isPublic)
this.dataset.accessibleByLink = true;
}
- uploadDataset() {
+ uploadDataset(onSuccess: Function = (dataset: Dataset) => { }, onError: Function = () => { }) {
if (this.files[0] == undefined) {
shared.openDialog("Greška", "Niste izabrali fajl za učitavanje.");
return;
}
- this.modelsService.uploadData(this.files[0]).subscribe((file) => {
+ return this.modelsService.uploadData(this.files[0]).subscribe((file) => {
//console.log('ADD MODEL: STEP 2 - ADD DATASET WITH FILE ID ' + file._id);
+ this.dataset._id = "";
this.dataset.fileId = file._id;
this.dataset.uploaderId = shared.userId;
this.datasetsService.addDataset(this.dataset).subscribe((dataset) => {
- shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira.");
+ onSuccess();
}, (error) => {
- shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset.");
+ onError();
}); //kraj addDataset subscribe
}, (error) => {
-
+ onError();
}); //kraj uploadData subscribe
}
+
+
}
diff --git a/frontend/src/app/_elements/form-model/form-model.component.css b/frontend/src/app/_elements/form-model/form-model.component.css
new file mode 100644
index 00000000..11b6ef5e
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.css
@@ -0,0 +1,104 @@
+#container {
+ color: var(--offwhite);
+}
+
+mat-label {
+ color: var(--offwhite) !important;
+}
+
+select {
+ color: var(--offwhite) !important;
+}
+
+mat-form-field {
+ color: var(--offwhite) !important;
+ padding: 0;
+ margin: 5px;
+ font-size: 12px;
+ width: 100%;
+}
+
+hr {
+ color: var(--offwhite) !important;
+ margin-bottom: 30px;
+}
+
+.neuron {
+ text-align: justify;
+ border: 1px solid white;
+ border-radius: 5px;
+ padding: 0;
+ color: var(--offwhite) !important;
+ background-color: var(--ns-bg-dark-100) !important;
+ min-width: none;
+ max-width: 12.5rem;
+}
+
+.row {
+ margin: 0;
+ padding: 0;
+}
+
+.mat-fix ::ng-deep .mat-form-field-wrapper {
+ margin-bottom: -1.85em;
+}
+
+#layers-control {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+#layers {
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: row;
+ overflow-x: auto;
+ overflow-wrap: break-word;
+ overflow-y: hidden;
+ width: 100%;
+}
+
+.layer {
+ border: 1px solid var(--ns-primary);
+ border-radius: 4px;
+ margin: 5px;
+ padding: 0px;
+ width: 12rem;
+ height: 11.1rem;
+}
+
+.tm {
+ margin-left: 10px;
+}
+
+.layer>* {
+ margin-top: 0;
+}
+
+.layer>mat-form-field {
+ margin-left: 0;
+}
+
+.m-2 {
+ max-height: 20 rem;
+}
+
+mat-slider {
+ width: 50%;
+}
+
+.slider {
+ background-color: transparent;
+}
+
+.center-center {
+ text-align: center;
+ margin-right: 10px;
+ padding-right: 10px;
+ padding-bottom: 15px;
+ font-size: 20px !important;
+ font-weight: 600;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-model/form-model.component.html b/frontend/src/app/_elements/form-model/form-model.component.html
new file mode 100644
index 00000000..96a5e1b6
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.html
@@ -0,0 +1,228 @@
+<div *ngIf="newModel">
+ <div id="container">
+ <div class="ns-row">
+
+ <div class="ns-col">
+ <mat-form-field class="example-full-width" appearance="fill" class="mat-fix">
+ <mat-label>Naziv</mat-label>
+ <input type="text" matInput [(ngModel)]="newModel.name">
+ </mat-form-field>
+ </div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Tip problema</mat-label>
+ <mat-select [(ngModel)]="newModel.type">
+ <mat-option *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <div class="break-1"></div>
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Optimizacija</mat-label>
+ <mat-select [(ngModel)]="newModel.optimizer">
+ <mat-option *ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+
+ </mat-form-field>
+ </div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Funkcija troška</mat-label>
+ <mat-select [(ngModel)]="newModel.lossFunction">
+ <mat-option *ngFor="let option of Object.keys(LossFunction); let optionName of Object.values(LossFunction)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <div class="break-2"></div>
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Funkcija aktivacije izlaznog sloja</mat-label>
+ <mat-select name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction">
+ <mat-option *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Stopa učenja</mat-label>
+ <mat-select [(ngModel)]="newModel.learningRate">
+ <mat-option *ngFor="let option of Object.keys(LearningRate); let optionName of Object.values(LearningRate)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <div class="break-1"></div>
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Broj epoha</mat-label>
+ <input type="number" matInput [(ngModel)]="newModel.epochs" min="1" max="1000">
+ </mat-form-field>
+ </div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Broj uzoraka po iteraciji</mat-label>
+
+ <mat-select matNativeControl [(value)]="newModel.batchSize">
+ <mat-option *ngFor="let option of Object.keys(BatchSize); let optionName of Object.values(BatchSize)" [value]="option">{{option}}</mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ </div>
+ </div>
+
+ <!-- GRAF -->
+
+ <div class="m-2">
+ <div class="row">
+ <div class="col-sm-3 rounded" style="border:1px solid var(--ns-primary);margin-top: 10px;">
+ <div class="row slider rounded mb-3" style="margin-left: 10px;">
+
+ <div class="text-center pt-3 pb-0 mb-0"><b>{{testSetDistribution}}%</b> : <b>{{100-testSetDistribution}}%</b></div>
+ <div class="text-center pt-0 mt-0">Trening
+ <mat-slider min="10" max="90" step="10" [(ngModel)]="testSetDistribution" (input)="updateTestSet($event)"></mat-slider>
+ Test</div>
+
+ </div>
+ <div class="row slider rounded text-offwhite justify-content-center align-items-center" style="margin-left: 10px;">
+ <mat-checkbox class="pt-4 mb-3" color="accent">Nasumični redosled podataka</mat-checkbox>
+ </div>
+
+
+ </div>
+
+ <div class="col-sm-9">
+ <!-- {{forExperiment._columnsSelected}} -->
+ <app-graph [model]="newModel" *ngIf="forExperiment._columnsSelected" [inputColumns]="forExperiment.inputColumns"></app-graph>
+ <app-graph [model]="newModel" *ngIf="!forExperiment._columnsSelected" [inputColumns]="['Nisu odabrane ulazne kolone']"></app-graph>
+ </div>
+ </div>
+ </div>
+
+ <!-- SVI LAYERI -->
+
+ <div class="ns-row">
+
+ <div class="ns-col" id="layers-control">
+ <div>Broj Skrivenih Slojeva</div>
+ <button class="btn-clear btn-icon bubble" (click)="addLayer()">
+ <mat-icon>add</mat-icon>
+ </button>
+ <div>{{newModel.hiddenLayers}}</div>
+ <button class="btn-clear btn-icon bubble" (click)="removeLayer()">
+ <mat-icon>remove</mat-icon>
+ </button>
+
+ </div>
+ <div class="break-1"></div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Aktivaciona funkcija svih slojeva</mat-label>
+
+ <mat-select [(ngModel)]="selectedActivation" (selectionChange)="changeAllActivation()">
+ <mat-option *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Broj neurona svih slojeva</mat-label>
+ <input matInput type="number" min="1" max="18" [(ngModel)]="selectedNumberOfNeurons" (change)="changeAllNumberOfNeurons()">
+ </mat-form-field>
+ </div>
+ <div class="break-2"></div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Regularizacija svih slojeva</mat-label>
+ <mat-select [(ngModel)]="selectedRegularisation" (selectionChange)="changeAllRegularisation()">
+ <mat-option *ngFor="let option of Object.keys(Regularisation); let optionName of Object.values(Regularisation)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Stopa regularizacije svih slojeva</mat-label>
+ <mat-select [(ngModel)]="selectedRegularisationRate" (selectionChange)="changeAllRegularisationRate()">
+ <mat-option *ngFor="let option of Object.keys(RegularisationRate); let optionName of Object.values(RegularisationRate)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <!-- LAYERI -->
+
+ <div id="layers">
+ <div class="layer" *ngFor="let item of newModel.layers; let i=index">
+
+
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Aktivacija</mat-label>
+ <button matPrefix class="btn-clear center-center text-offwhite">
+ <div>
+ #{{i+1}}
+ </div>
+ </button>
+ <mat-select [(ngModel)]="newModel.layers[i].activationFunction">
+ <mat-option *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+
+ <div class="d-flex flex-row align-items-center justify-content-center tm">
+ <div class="col-6" style="font-size: 13px;">Broj čvorova</div>
+ <button class="btn-clear btn-icon bubble" (click)="addNeuron(i)">
+ <mat-icon>add</mat-icon>
+ </button>
+ <div class="col-2 text-center">{{newModel.layers[i].neurons}}</div>
+ <button class="btn-clear btn-icon bubble" (click)="removeNeuron(i)">
+ <mat-icon>remove</mat-icon>
+ </button>
+ </div>
+
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Regularizacija</mat-label>
+ <mat-select [(ngModel)]="newModel.layers[i].regularisation">
+ <mat-option *ngFor="let option of Object.keys(Regularisation); let optionName of Object.values(Regularisation)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Stopa regularizacije</mat-label>
+ <mat-select [(ngModel)]="newModel.layers[i].regularisationRate">
+ <mat-option *ngFor="let option of Object.keys(RegularisationRate); let optionName of Object.values(RegularisationRate)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.spec.ts b/frontend/src/app/_elements/form-model/form-model.component.spec.ts
index cb07ef1d..af1091cc 100644
--- a/frontend/src/app/_elements/annvisual/annvisual.component.spec.ts
+++ b/frontend/src/app/_elements/form-model/form-model.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { AnnvisualComponent } from './annvisual.component';
+import { FormModelComponent } from './form-model.component';
-describe('AnnvisualComponent', () => {
- let component: AnnvisualComponent;
- let fixture: ComponentFixture<AnnvisualComponent>;
+describe('FormModelComponent', () => {
+ let component: FormModelComponent;
+ let fixture: ComponentFixture<FormModelComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ AnnvisualComponent ]
+ declarations: [ FormModelComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(AnnvisualComponent);
+ fixture = TestBed.createComponent(FormModelComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/form-model/form-model.component.ts b/frontend/src/app/_elements/form-model/form-model.component.ts
new file mode 100644
index 00000000..71b374b0
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.ts
@@ -0,0 +1,138 @@
+import { Component, OnInit, Input, ViewChild, Output, EventEmitter, AfterViewInit } from '@angular/core';
+import { FormControl, Validators } from '@angular/forms';
+import Shared from 'src/app/Shared';
+import Experiment from 'src/app/_data/Experiment';
+import Model, { Layer, ActivationFunction, LossFunction, LearningRate, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType, Regularisation, RegularisationRate, BatchSize } from 'src/app/_data/Model';
+import { GraphComponent } from '../graph/graph.component';
+import { MatSliderChange } from '@angular/material/slider';
+
+@Component({
+ selector: 'app-form-model',
+ templateUrl: './form-model.component.html',
+ styleUrls: ['./form-model.component.css']
+})
+export class FormModelComponent implements AfterViewInit {
+ @ViewChild(GraphComponent) graph!: GraphComponent;
+ @Input() forExperiment!: Experiment;
+ @Output() selectedModelChangeEvent = new EventEmitter<Model>();
+ testSetDistribution: number = 70;
+ constructor() { }
+
+ ngAfterViewInit(): void { }
+
+ selectFormControl = new FormControl('', Validators.required);
+ nameFormControl = new FormControl('', [Validators.required, Validators.email]);
+ selectTypeFormControl = new FormControl('', Validators.required);
+ selectOptFormControl = new FormControl('', Validators.required);
+ selectLFFormControl = new FormControl('', Validators.required);
+ selectLRFormControl = new FormControl('', Validators.required);
+ selectEpochFormControl = new FormControl('', Validators.required);
+ selectAFFormControl = new FormControl('', Validators.required);
+ selectBSFormControl = new FormControl('', Validators.required);
+ selectActivationFormControl = new FormControl('', Validators.required);
+ selectRegularisationFormControl = new FormControl('', Validators.required);
+ selectRRateFormControl = new FormControl('', Validators.required);
+
+ newModel!: Model;
+
+ selectedModel?: Model;
+
+ ProblemType = ProblemType;
+ ActivationFunction = ActivationFunction;
+ RegularisationRate = RegularisationRate;
+ Regularisation = Regularisation;
+ metrics: any = Metrics;
+ LossFunction = LossFunction;
+ Optimizer = Optimizer;
+ BatchSize = BatchSize;
+ Object = Object;
+ document = document;
+ shared = Shared;
+ LearningRate = LearningRate;
+ Layer = Layer;
+
+ term: string = "";
+ selectedMetrics = [];
+ lossFunction: any = LossFunction;
+
+ loadModel(model: Model) {
+ this.newModel = model;
+ }
+
+ updateGraph() {
+ //console.log(this.newModel.layers);
+ this.graph.update();
+ }
+
+ removeLayer() {
+ if (this.newModel.hiddenLayers > 1) {
+ this.newModel.layers.splice(this.newModel.layers.length - 1, 1);
+ this.newModel.hiddenLayers -= 1;
+ this.updateGraph();
+ }
+ }
+ addLayer() {
+ if (this.newModel.hiddenLayers < 128) {
+ this.newModel.layers.push(new Layer(this.newModel.layers.length, this.selectedActivation, this.selectedNumberOfNeurons, this.selectedRegularisation, this.selectedRegularisationRate));
+
+ this.newModel.hiddenLayers += 1;
+ this.updateGraph();
+ }
+
+ }
+ /*
+ setNeurons()
+ {
+ for(let i=0;i<this.newModel.hiddenLayers;i++){
+ this.newModel.hiddenLayerNeurons[i]=1;
+ }
+ }*/
+ numSequence(n: number): Array<number> {
+ return Array(n);
+ }
+
+ removeNeuron(index: number) {
+ if (this.newModel.layers[index].neurons > 1) {
+ this.newModel.layers[index].neurons -= 1;
+ this.updateGraph();
+ }
+ }
+ addNeuron(index: number) {
+ if (this.newModel.layers[index].neurons < 18) {
+ this.newModel.layers[index].neurons += 1;
+ this.updateGraph();
+ }
+ }
+ selectedActivation: ActivationFunction = ActivationFunction.Sigmoid;
+ selectedRegularisationRate: RegularisationRate = RegularisationRate.RR1;
+ selectedRegularisation: Regularisation = Regularisation.L1;
+ selectedNumberOfNeurons: number = 3;
+
+ changeAllActivation() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].activationFunction = this.selectedActivation;
+
+ }
+
+ }
+ changeAllRegularisation() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].regularisation = this.selectedRegularisation;
+ }
+ }
+ changeAllRegularisationRate() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].regularisationRate = this.selectedRegularisationRate;
+ }
+ }
+ changeAllNumberOfNeurons() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].neurons = this.selectedNumberOfNeurons;
+ this.updateGraph();
+ }
+ }
+
+ updateTestSet(event: MatSliderChange) {
+ this.testSetDistribution = event.value!;
+ }
+}
diff --git a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.css b/frontend/src/app/_elements/gradient-background/gradient-background.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.css
+++ b/frontend/src/app/_elements/gradient-background/gradient-background.component.css
diff --git a/frontend/src/app/_elements/gradient-background/gradient-background.component.html b/frontend/src/app/_elements/gradient-background/gradient-background.component.html
new file mode 100644
index 00000000..3f30c35e
--- /dev/null
+++ b/frontend/src/app/_elements/gradient-background/gradient-background.component.html
@@ -0,0 +1 @@
+<div #holder style="position: fixed; z-index: -1;" [ngStyle]="{'background': color}"></div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.spec.ts b/frontend/src/app/_elements/gradient-background/gradient-background.component.spec.ts
index a9ea25b4..969c73b7 100644
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.spec.ts
+++ b/frontend/src/app/_elements/gradient-background/gradient-background.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { AddNewDatasetComponent } from './add-new-dataset.component';
+import { GradientBackgroundComponent } from './gradient-background.component';
-describe('AddNewDatasetComponent', () => {
- let component: AddNewDatasetComponent;
- let fixture: ComponentFixture<AddNewDatasetComponent>;
+describe('GradientBackgroundComponent', () => {
+ let component: GradientBackgroundComponent;
+ let fixture: ComponentFixture<GradientBackgroundComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ AddNewDatasetComponent ]
+ declarations: [ GradientBackgroundComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(AddNewDatasetComponent);
+ fixture = TestBed.createComponent(GradientBackgroundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/gradient-background/gradient-background.component.ts b/frontend/src/app/_elements/gradient-background/gradient-background.component.ts
new file mode 100644
index 00000000..1414bc60
--- /dev/null
+++ b/frontend/src/app/_elements/gradient-background/gradient-background.component.ts
@@ -0,0 +1,47 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+
+@Component({
+ selector: 'app-gradient-background',
+ templateUrl: './gradient-background.component.html',
+ styleUrls: ['./gradient-background.component.css']
+})
+export class GradientBackgroundComponent implements AfterViewInit {
+
+ @ViewChild('holder') holderRef!: ElementRef;
+ private holder!: HTMLDivElement;
+
+
+ @Input() colorHorizontal1 = 'rgba(0, 8, 45, 0.5)';
+ @Input() colorHorizontal2 = 'rgba(0, 52, 89, 0.5)';
+
+ @Input() colorVertical1 = 'rgba(0, 52, 89, 0.5)';
+ @Input() colorVertical2 = 'rgba(0, 152, 189, 0.5)';
+
+ constructor() { }
+
+ color: string = this.gradientHorizontal();
+
+ private width = 0;
+ private height = 0;
+
+ gradientHorizontal(): string {
+ return `linear-gradient(90deg, ${this.colorHorizontal1} 0%, ${this.colorHorizontal2} 50%, ${this.colorHorizontal1} 100%), linear-gradient(0deg, ${this.colorVertical1} 0%, ${this.colorVertical2} 100%)`;
+ }
+
+ resize() {
+ this.width = window.innerWidth;
+ this.height = window.innerHeight;
+
+ this.holder.style.width = this.width + 'px';
+ this.holder.style.height = this.height + 'px';
+ }
+
+ ngAfterViewInit(): void {
+ this.holder = <HTMLDivElement>this.holderRef.nativeElement;
+
+ window.addEventListener('resize', () => this.resize());
+ this.resize();
+
+ }
+
+}
diff --git a/frontend/src/app/_elements/graph/graph.component.css b/frontend/src/app/_elements/graph/graph.component.css
index e69de29b..456a8df1 100644
--- a/frontend/src/app/_elements/graph/graph.component.css
+++ b/frontend/src/app/_elements/graph/graph.component.css
@@ -0,0 +1,20 @@
+.node-text {
+ position: absolute;
+ width: 100px;
+ height: 40px;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ /*border: 1px solid red;*/
+ transform: translate(-50%, -50%);
+}
+
+.node-text:not(.inputs) {
+ color: transparent;
+}
+
+.node-text:not(.inputs):hover {
+ color: var(--offwhite);
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/graph/graph.component.html b/frontend/src/app/_elements/graph/graph.component.html
index 1c21fb6c..35753d40 100644
--- a/frontend/src/app/_elements/graph/graph.component.html
+++ b/frontend/src/app/_elements/graph/graph.component.html
@@ -1,3 +1,8 @@
-<div #graphWrapper class="w-100" style="height: 16rem;">
- <canvas #graphCanvas class="border"></canvas>
+<div #graphWrapper class="w-100 position-relative" style="height: 14rem;">
+ <!-- <ng-container *ngFor="let layer of layers; let i = index">
+ <div [ngClass]="{'inputs': i==0}" class="node-text" *ngFor="let node of layer; let j = index" [style.left.%]="node.x * 99.4" [style.top.%]="node.y * 100">
+ {{ i == 0 ? (inputColumns && inputColumns.length >= j ? inputColumns[j] : 'nepoznato') : (i > 0 && i
+ < layers.length - 1 ? model!.layers[i-1].activationFunction : (i==layers.length - 1 ? 'out' : '')) }} </div>
+ </ng-container> -->
+ <canvas #graphCanvas></canvas>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/graph/graph.component.ts b/frontend/src/app/_elements/graph/graph.component.ts
index 8051acc3..da2c7767 100644
--- a/frontend/src/app/_elements/graph/graph.component.ts
+++ b/frontend/src/app/_elements/graph/graph.component.ts
@@ -1,6 +1,7 @@
import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
-import Dataset from 'src/app/_data/Dataset';
-import Model from 'src/app/_data/Model';
+import { RgbColor } from '@syncfusion/ej2-angular-heatmap';
+import Dataset, { ColumnInfo } from 'src/app/_data/Dataset';
+import Model, { Layer } from 'src/app/_data/Model';
@Component({
selector: 'app-graph',
@@ -15,17 +16,19 @@ export class GraphComponent implements AfterViewInit {
canvas!: ElementRef;
@Input() model?: Model;
- @Input() inputCols: number = 1;
+ //@Input() inputCols: number = 1;
@Input() lineThickness: number = 2;
@Input() nodeRadius: number = 15;
- @Input() lineColor: string = '#00a8e8';
+ @Input() lineColor1: RgbColor = new RgbColor(0, 168, 232);
+ @Input() lineColor2: RgbColor = new RgbColor(0, 70, 151);
@Input() nodeColor: string = '#222277';
@Input() borderColor: string = '#00a8e8';
- @Input() inputNodeColor: string = '#ffdd11';
- @Input() outputNodeColor: string = '#44ee22';
+ @Input() inputNodeColor: string = '#00a8e8';
+ @Input() outputNodeColor: string = '#dfd7d7';
- private ctx?: CanvasRenderingContext2D;
+ private ctx!: CanvasRenderingContext2D;
+ @Input() inputColumns?: string[];
constructor() { }
@@ -40,18 +43,19 @@ export class GraphComponent implements AfterViewInit {
window.addEventListener('resize', () => { this.resize() });
this.update();
this.resize();
+ //console.log(this.layers);
}
- layers?: Node[][];
+ layers: Node[][] = [];
update() {
- this.layers = [];
+ this.layers.length = 0;
let inputNodeIndex = 0;
const inputLayer: Node[] = [];
- while (inputNodeIndex < this.inputCols) {
+ while (this.inputColumns && inputNodeIndex < this.inputColumns.length) {
const x = 0.5 / (this.model!.hiddenLayers + 2);
- const y = (inputNodeIndex + 0.5) / this.inputCols;
+ const y = (inputNodeIndex + 0.5) / this.inputColumns.length;
const node = new Node(x, y, this.inputNodeColor);
inputLayer.push(node);
inputNodeIndex += 1;
@@ -62,9 +66,9 @@ export class GraphComponent implements AfterViewInit {
while (layerIndex < this.model!.hiddenLayers + 1) {
const newLayer: Node[] = [];
let nodeIndex = 0;
- while (nodeIndex < this.model!.hiddenLayerNeurons) {
+ while (nodeIndex < this.model!.layers[layerIndex - 1].neurons) {
const x = (layerIndex + 0.5) / (this.model!.hiddenLayers + 2);
- const y = (nodeIndex + 0.5) / this.model!.hiddenLayerNeurons;
+ const y = (nodeIndex + 0.5) / this.model!.layers[layerIndex - 1].neurons;
const node = new Node(x, y, this.nodeColor);
newLayer.push(node);
nodeIndex += 1;
@@ -80,7 +84,7 @@ export class GraphComponent implements AfterViewInit {
}
draw() {
- this.ctx!.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
+ this.ctx.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
let index = 0;
while (index < this.layers!.length - 1) {
@@ -93,29 +97,38 @@ export class GraphComponent implements AfterViewInit {
}
for (let layer of this.layers!) {
- for (let node of layer) {
- this.drawNode(node);
- }
+ layer.forEach((node, index) => {
+ this.drawNode(node, 0.5 / layer.length + 0.5);
+ });
}
}
+ bezierOffset = 5;
+
drawLine(node1: Node, node2: Node) {
- this.ctx!.strokeStyle = this.lineColor;
- this.ctx!.lineWidth = this.lineThickness;
- this.ctx!.beginPath();
- this.ctx!.moveTo(node1.x * this.width, node1.y * this.height);
- this.ctx!.lineTo(node2.x * this.width, node2.y * this.height);
- this.ctx!.stroke();
+ const lineColor: RgbColor = this.lerpColor(this.lineColor1, this.lineColor2, node1.y);
+ this.ctx.strokeStyle = `rgb(${lineColor.R}, ${lineColor.G}, ${lineColor.B})`;
+ this.ctx.lineWidth = this.lineThickness;
+ this.ctx.beginPath();
+ this.ctx.moveTo(node1.x * this.width, node1.y * this.height);
+ //this.ctx.lineTo(node2.x * this.width, node2.y * this.height);
+ const middle = (node1.x + (node2.x - node1.x) / 2) * this.width;
+ this.ctx.bezierCurveTo(
+ middle, node1.y * this.height,
+ middle, node2.y * this.height,
+ node2.x * this.width, node2.y * this.height);
+ this.ctx.stroke();
}
- drawNode(node: Node) {
- this.ctx!.fillStyle = node.color;
- this.ctx!.strokeStyle = this.borderColor;
- this.ctx!.lineWidth = this.lineThickness;
- this.ctx!.beginPath();
- this.ctx!.arc(node.x * this.width, node.y * this.height, this.nodeRadius, 0, 2 * Math.PI);
- this.ctx!.fill();
- this.ctx!.stroke();
+ drawNode(node: Node, sizeMult: number) {
+ const lineColor: RgbColor = this.lerpColor(this.lineColor1, this.lineColor2, node.y);
+ this.ctx.strokeStyle = `rgb(${lineColor.R}, ${lineColor.G}, ${lineColor.B})`;
+ this.ctx.fillStyle = node.color;
+ this.ctx.lineWidth = this.lineThickness;
+ this.ctx.beginPath();
+ this.ctx.arc(node.x * this.width, node.y * this.height, this.nodeRadius * sizeMult, 0, 2 * Math.PI);
+ this.ctx.fill();
+ this.ctx.stroke();
}
width = 200;
@@ -134,6 +147,16 @@ export class GraphComponent implements AfterViewInit {
this.draw();
}
+
+ lerpColor(value1: RgbColor, value2: RgbColor, amount: number): RgbColor {
+ const newColor = new RgbColor(0, 0, 0);
+ amount = amount < 0 ? 0 : amount;
+ amount = amount > 1 ? 1 : amount;
+ newColor.R = value1.R + (value2.R - value1.R) * amount;
+ newColor.G = value1.G + (value2.G - value1.G) * amount;
+ newColor.B = value1.B + (value2.B - value1.B) * amount;
+ return newColor;
+ };
}
class Node {
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.css b/frontend/src/app/_elements/item-dataset/item-dataset.component.css
deleted file mode 100644
index dc851671..00000000
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.css
+++ /dev/null
@@ -1,23 +0,0 @@
-.card{
- margin-top:0;
- padding: 0;
-}
-.p-2{
- margin: 0;
- padding: 0;
-}
-hr{
- margin: 0;
- padding: 0;
-}
-b{
- margin-left: 5px;
- margin-right: 10px;
-}
-th{
- margin: 10px;
- padding: 10px;
-}
-p{
- text-align: justify;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.html b/frontend/src/app/_elements/item-dataset/item-dataset.component.html
deleted file mode 100644
index 11ff61c3..00000000
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<div class="card" style="min-width: 12rem;">
-<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;">Naziv</b></div>
- <div class=" p-2 float-left"><b>{{dataset.name}}</b></div>
-</div>
-<div class="card-body overflow-hidden">
- <b style="color: gray;">Opis</b>
- <hr style="width: 20%;"> <p> {{dataset.description}}</p>
- <hr>
- <div class="d-flex justify-content-center">
- <div class=" p-2" >
- <h4><span class="badge bg-secondary">{{dataset.extension}}</span></h4>
- </div>
- <div class="p-2">
- <span class="material-icons">{{visibleicon}}</span>
- </div>
- <div class="p-2">
- <span class="material-icons">{{accessibleicon}}</span>
- </div>
- </div>
- <hr>
- <div class="col text-center">
-<button (click)=toggleDisplayDiv() class="btn btn-primary btn-sm active " mat-raised-button color="primary" style="margin: 0.5rem;">Kolone</button>
- <div [hidden]="isShowDiv" style="overflow: scroll; overflow-y: hidden;">
- <table class="table table-bordered table-md" >
- <thead>
- <th scope="col" *ngFor="let column of dataset.columnInfo" >{{column.columnName}}</th>
- </thead>
- </table>
- </div>
-</div>
- <table>
- <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{dataset.dateCreated |date}}</td></tr>
- <tr><td><span class="material-icons">edit_calendar</span></td><td><span style="color: grey;"> <b> Poslednja izmena</b></span></td><td>{{dataset.lastUpdated |date}}</td></tr>
- </table>
-
-</div>
-<div class="card-footer">
-
- </div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.ts b/frontend/src/app/_elements/item-dataset/item-dataset.component.ts
deleted file mode 100644
index 44b95310..00000000
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import Dataset from 'src/app/_data/Dataset';
-
-@Component({
- selector: 'app-item-dataset',
- templateUrl: './item-dataset.component.html',
- styleUrls: ['./item-dataset.component.css']
-})
-export class ItemDatasetComponent {
-
- @Input() dataset: Dataset = new Dataset();
- visibleicon='';
- accessibleicon='';
- isShowDiv = true;
- toggleDisplayDiv() {
- this.isShowDiv = !this.isShowDiv;
- }
- constructor() {
- }
- ngOnInit(): void {
- if(this.dataset.isPublic==true)
- {
- this.visibleicon='visibility'
- }
- else
- {
- this.visibleicon='visibility_off';
- }
-
- if(this.dataset.accessibleByLink==true)
- {
- this.accessibleicon='link'
- }
- else
- {
- this.accessibleicon='link_off';
- }
- }
-}
-
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.html b/frontend/src/app/_elements/item-experiment/item-experiment.component.html
deleted file mode 100644
index 51fbfef3..00000000
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<div class="card" style="min-width: 12rem;">
- <div class="card-header">
- Naziv eksperimenta: <b>{{experiment.name}}</b>
- </div>
- <div class="card-body overflow-hidden">
- <p class="card-text">
- Opis: {{experiment.description}}
- </p>
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.ts b/frontend/src/app/_elements/item-experiment/item-experiment.component.ts
deleted file mode 100644
index 31900d35..00000000
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import Experiment from 'src/app/_data/Experiment';
-
-@Component({
- selector: 'app-item-experiment',
- templateUrl: './item-experiment.component.html',
- styleUrls: ['./item-experiment.component.css']
-})
-export class ItemExperimentComponent{
-
- @Input() experiment: Experiment = new Experiment();
-
- constructor() { }
-
-}
diff --git a/frontend/src/app/_elements/item-model/item-model.component.css b/frontend/src/app/_elements/item-model/item-model.component.css
deleted file mode 100644
index 5ea24c72..00000000
--- a/frontend/src/app/_elements/item-model/item-model.component.css
+++ /dev/null
@@ -1,23 +0,0 @@
-.card{
- margin: 0.5rem;
- padding: 0;
-}
-.p-2{
- margin: 0;
- padding: 0;
-}
-hr{
- margin: 0;
- padding: 0;
-}
-b{
- margin-left: 5px;
- margin-right: 10px;
-}
-th{
- margin: 10px;
- padding: 10px;
-}
-p{
- text-align: justify;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-model/item-model.component.html b/frontend/src/app/_elements/item-model/item-model.component.html
deleted file mode 100644
index 447f023e..00000000
--- a/frontend/src/app/_elements/item-model/item-model.component.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<div class="card" style="min-width: 12rem;">
- <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;">Naziv</b></div>
- <div class=" p-2 float-left"><b>{{model.name}}</b></div>
- </div>
- <div class="card-body overflow-hidden">
- <app-graph [model]="model"></app-graph>
- <br>
- <b style="color: gray;">Opis</b><hr style="width: 20%;">
- <p class="card-text">
- {{model.description}}
- </p>
- <hr>
-
- <div>
- <table>
- <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{model.dateCreated |date}}</td></tr>
- <tr><td><span class="material-icons">edit_calendar</span></td><td><span style="color: grey;"> <b> Poslednja izmena</b></span></td><td>{{model.lastUpdated |date}}</td></tr>
- </table>
- </div>
-
- </div>
- <button (click)=toggleDisplayDiv() class="btn btn-default btn-lg " mat-raised-button color="primary" style="margin: 0.5rem;">Parametri</button>
- <div [hidden]="isShowDiv">
- <!-- <table>
- <tr>
- <td><span style="color: grey;"> <b> Nasumično raspoređivanje podataka</b></span></td><td>{{randomOrd}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Podela podataka na trening i test skup</b></span></td><td>{{randomOrd}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Veličina skupa za treniranje</b></span></td><td>{{randomOrd}}</td>
- </tr>
- </table>-->
- <hr>
- <table>
- <tr>
- <td><span style="color: grey;"> <b> Tip problema</b></span></td><td>{{model.type}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Optimizator</b></span></td><td>{{model.optimizer}}</td>
- </tr>
- <tr>
- <td> <span style="color: grey;"> <b> Funkcija gubitka</b></span></td><td>{{model.lossFunction}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Batch size</b></span></td><td>{{model.batchSize}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Broj epoha</b></span></td><td>{{model.epochs}}</td>
- </tr>
-
- </table>
-
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-model/item-model.component.ts b/frontend/src/app/_elements/item-model/item-model.component.ts
deleted file mode 100644
index b837667b..00000000
--- a/frontend/src/app/_elements/item-model/item-model.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import Model from 'src/app/_data/Model';
-
-@Component({
- selector: 'app-item-model',
- templateUrl: './item-model.component.html',
- styleUrls: ['./item-model.component.css']
-})
-export class ItemModelComponent implements OnInit {
-
- @Input() model: Model = new Model();
- isShowDiv = true;
- randomOrd='';
-
- toggleDisplayDiv() {
- this.isShowDiv = !this.isShowDiv;
- }
-
- constructor() { }
-
- ngOnInit(): void {
- /*if(this.model.randomOrder)
- {
- this.randomOrd='Da';
- }
- else
- {
- this.randomOrd='Ne';
- }
-*/
- }
-
-}
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.html b/frontend/src/app/_elements/item-predictor/item-predictor.component.html
deleted file mode 100644
index 3199dcc8..00000000
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<div class="card" style="min-width: 12rem;">
- <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 overflow-hidden">
- <b style="color: gray;">Opis</b><hr style="width: 20%;">
- <p class="card-text">
- {{predictor.description}}
- </p>
-
- <b style="color: gray;">Ulazne kolone</b>
- <div style="overflow: scroll; overflow-y: hidden;">
-
- <table class="table table-bordered table-md" >
- <thead>
- <th scope="col" *ngFor="let column of predictor.inputs" >{{column}}</th>
- </thead>
- </table>
- </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-md col-4" style="background-color:#003459; color:white;"
- (click)="openPredictor();">Iskoristi</button>
-
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.ts b/frontend/src/app/_elements/item-predictor/item-predictor.component.ts
deleted file mode 100644
index 246032e0..00000000
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import { Router } from '@angular/router';
-import Predictor from 'src/app/_data/Predictor';
-
-@Component({
- selector: 'app-item-predictor',
- templateUrl: './item-predictor.component.html',
- styleUrls: ['./item-predictor.component.css']
-})
-export class ItemPredictorComponent implements OnInit {
-
- @Input() predictor: Predictor = new Predictor();
-
- constructor(private router: Router) { }
-
- ngOnInit(): void {
- }
-
- openPredictor() {
- this.router.navigate(['predict/'+ this.predictor._id]);
- }
-
-}
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.html b/frontend/src/app/_elements/line-chart/line-chart.component.html
deleted file mode 100644
index c8f406f4..00000000
--- a/frontend/src/app/_elements/line-chart/line-chart.component.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<div class="chart-wrapper">
- <canvas id="myChart">
-
- </canvas>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.css b/frontend/src/app/_elements/metric-view/metric-view.component.css
index e69de29b..f91c1ccc 100644
--- a/frontend/src/app/_elements/metric-view/metric-view.component.css
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.css
@@ -0,0 +1,10 @@
+#container{
+ width: 100%;
+ height: 90%;
+ border-radius: 5px;
+ background-color:var(--ns-primary-25);
+ border:1px solid var(--ns-accent);
+}
+#line{
+ background-color:#dfd7d7f0 ;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.html b/frontend/src/app/_elements/metric-view/metric-view.component.html
index e7a4c547..d72bc92b 100644
--- a/frontend/src/app/_elements/metric-view/metric-view.component.html
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.html
@@ -1,5 +1,8 @@
-<div>
- <app-line-chart>
-
- </app-line-chart>
+<div id="container" class="d-flex justify-content-center flex-row w-100">
+ <div id="line" style="width: 100%;height: 100%;background-color:var(--ns-bg-dark-100);">
+ <app-line-chart></app-line-chart>
+ </div>
+ <div style="background-color: var(--ns-bg-dark-100);width: 50%;height: 50%;">
+ <app-scatterchart></app-scatterchart>
+ </div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.ts b/frontend/src/app/_elements/metric-view/metric-view.component.ts
index 9193a0e5..6fd2f320 100644
--- a/frontend/src/app/_elements/metric-view/metric-view.component.ts
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.ts
@@ -1,6 +1,6 @@
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';
+import { LineChartComponent } from '../_charts/line-chart/line-chart.component';
+
@Component({
selector: 'app-metric-view',
templateUrl: './metric-view.component.html',
@@ -9,13 +9,13 @@ import { LineChartComponent } from '../line-chart/line-chart.component';
export class MetricViewComponent implements OnInit {
@ViewChild(LineChartComponent) linechartComponent!: LineChartComponent;
- @Input() history!: any[];
-
- constructor(private signalRService: SignalRService) { }
+ constructor() { }
ngOnInit(): void {
}
+ history: any[] = [];
+
update(history: any[]) {
const myAcc: number[] = [];
const myMae: number[] = [];
@@ -28,7 +28,7 @@ export class MetricViewComponent implements OnInit {
myEpochs.push(epoch + 1);
for (let key in metrics) {
let value = metrics[key];
- console.log(key, ':::', value, epoch);
+ //console.log(key, ':::', value, epoch);
if (key === 'accuracy') {
myAcc.push(parseFloat(value));
}
diff --git a/frontend/src/app/_elements/model-load/model-load.component.css b/frontend/src/app/_elements/model-load/model-load.component.css
deleted file mode 100644
index c716f964..00000000
--- a/frontend/src/app/_elements/model-load/model-load.component.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.btnType1 {
- background-color: #003459;
- color: white;
- padding-top: 2vh;
- padding-bottom: 2vh;
-}
-.btnType2 {
- background-color: white;
- color: #003459;
- border-color: #003459;
- padding-top: 2vh;
- padding-bottom: 2vh;
-}
-.selectedModelClass {
- /*border-color: 2px solid #003459;*/
- background-color: lightblue;
-} \ 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 1f9852d1..dcb35c21 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.html
+++ b/frontend/src/app/_elements/model-load/model-load.component.html
@@ -1,10 +1,12 @@
<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>
@@ -15,7 +17,8 @@
<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:((forExperiment != undefined) ? forExperiment.type : '')" [ngClass]="{'selectedModelClass': this.selectedModel == model}">
+ <li class="list-group-item p-3" *ngFor="let model of myModels|filter:term|filter:(forExperiment ? forExperiment.type : '')"
+ [ngClass]="{'selectedModelClass': this.selectedModel == model}">
<app-item-model name="usersModel" [model]="model" (click)="selectThisModel(model);">
</app-item-model>
</li>
@@ -40,7 +43,7 @@
<textarea class="form-control" name="desc" rows="3" [(ngModel)]="newModel.description"></textarea>
</div>
</div>
-
+
</div>
<h2 class="mt-5 mb-4 mx-5">Parametri treniranja modela:</h2>
<div>
@@ -51,7 +54,8 @@
<label for="type" class="col-form-label">Tip problema: </label>
</div>
<div class="col-2">
- <select id="typeOptions" class="form-select" name="type" [(ngModel)]="newModel.type" (change)="filterOptions()">
+ <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">
@@ -64,7 +68,10 @@
<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>
@@ -75,7 +82,7 @@
<label for="optimizer" class="col-form-label">Optimizacija: </label>
</div>
<div class="col-2">
- <select id="optimizerOptions" class="form-select" name="optimizer" [(ngModel)]="newModel.optimizer">
+ <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">
@@ -89,7 +96,8 @@
<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>
@@ -99,7 +107,8 @@
<label for="lossFunction" class="col-form-label">Funkcija troška: </label>
</div>
<div class="col-2">
- <select id="lossFunctionOptions" class="form-select" name="lossFunction" [(ngModel)]="newModel.lossFunction" aria-checked="true">
+ <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">
@@ -109,21 +118,23 @@
</div>
<div class="col-1"></div>
<div class="col-3">
- <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji:&nbsp;<b>{{newModel.batchSize}}</b><br>(izaberite stepen dvojke)</label>
+ <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label>
</div>
<div class="col-1">
-
- <input type="number" min="0" step="1" max="7" class="form-control" name="batchSizePower" [(ngModel)]="batchSizePower" (click)="updateBatchSize()">
-
+
+ <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">
+ <input type="number" min="1" max="1000" class="form-control" name="epochs"
+ [(ngModel)]="newModel.epochs">
</div>
</div>
</div>
@@ -137,7 +148,8 @@
<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">
@@ -145,7 +157,8 @@
<div class="input-group-prepend">
<span class="input-group-text">#{{i+1}}</span>
</div>
- <select [id]="'hiddenLayerActivationFunctionOption_'+i" class="form-select" [(ngModel)]="newModel.hiddenLayerActivationFunctions[i]">
+ <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">
@@ -157,10 +170,12 @@
</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-select" 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">
@@ -173,23 +188,26 @@
</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.spec.ts b/frontend/src/app/_elements/model-load/model-load.component.spec.ts
deleted file mode 100644
index 1dafd966..00000000
--- a/frontend/src/app/_elements/model-load/model-load.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { ModelLoadComponent } from './model-load.component';
-
-describe('ModelLoadComponent', () => {
- let component: ModelLoadComponent;
- let fixture: ComponentFixture<ModelLoadComponent>;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ ModelLoadComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(ModelLoadComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
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 fb4b3fd0..dbca3d17 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.ts
+++ b/frontend/src/app/_elements/model-load/model-load.component.ts
@@ -2,9 +2,7 @@ import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from '@angu
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';
@@ -16,7 +14,7 @@ import { GraphComponent } from '../graph/graph.component';
export class ModelLoadComponent implements OnInit {
@ViewChild(GraphComponent) graph!: GraphComponent;
- @Input() forExperiment?: Experiment;
+ @Input() forExperiment?:Experiment;
@Output() selectedModelChangeEvent = new EventEmitter<Model>();
newModel: Model = new Model();
@@ -33,42 +31,24 @@ export class ModelLoadComponent implements OnInit {
shared = Shared;
term: string = "";
+ selectedProblemType: string = '';
selectedMetrics = [];
lossFunction: any = LossFunction;
showMyModels: boolean = true;
- 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) {
+ constructor(private modelsService: ModelsService) {
this.modelsService.getMyModels().subscribe((models) => {
- this.myModels = models.reverse();
- this.selectThisModel(this.myModels.filter(x => x._id == andSelectWithId)[0]);
+ this.myModels = models;
});
- /*}
- 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;
+ batchSizePower:number=1;
+ updateBatchSize()
+ {
+ this.newModel.batchSize=2**this.batchSizePower;
}
updateGraph() {
@@ -92,14 +72,9 @@ export class ModelLoadComponent implements OnInit {
this.newModel.uploaderId = Shared.userId;
this.modelsService.addModel(this.newModel).subscribe((response) => {
- 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();
+ Shared.openDialog('Model dodat', 'Model je uspešno dodat u bazu.');
+ // treba da se selektuje nov model u listi modela
+ //this.selectedModel =
}, (error) => {
Shared.openDialog('Greška', 'Model sa unetim nazivom već postoji u Vašoj kolekciji. Promenite naziv modela i nastavite sa kreiranim datasetom.');
});
diff --git a/frontend/src/app/_elements/navbar/navbar.component.css b/frontend/src/app/_elements/navbar/navbar.component.css
index e69de29b..fcfad876 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.css
+++ b/frontend/src/app/_elements/navbar/navbar.component.css
@@ -0,0 +1,8 @@
+.dropdown-item:hover {
+ background-color: var(--ns-primary);
+}
+
+h4 {
+ margin-top: 0.82rem;
+ margin-right: 10px;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/navbar/navbar.component.html b/frontend/src/app/_elements/navbar/navbar.component.html
index 1988b834..105151aa 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.html
+++ b/frontend/src/app/_elements/navbar/navbar.component.html
@@ -1,17 +1,15 @@
-<header class="sticky-top p-3 bg-dark text-white">
+<header class="text-offwhite" style="background-color: #002b49;">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a routerLink="" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
- <img src="../../../assets/svg/logo_no_text.svg" class="bi me-2" width="64" height="40">
+ <img src="../../../assets/images/logo.png" class="bi me-2" width="64" height="64">
+ <h4>Igr<span class="highlight">ann</span>onica</h4>
</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="experiment" class="nav-link px-2" [class]="(currentUrl === '/experiment') ? 'text-primary' : 'text-offwhite'">Napravi eksperiment</a>
</li>
- <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="archive" class="nav-link px-2" [class]="(currentUrl === '/archive') ? 'text-primary' : 'text-offwhite'">Arhiva</a>
</li>
</ul>
@@ -19,12 +17,8 @@
<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="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>
+ <ul class="dropdown-menu text-small ns-bg-dark-100" 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="profile">Moj profil</a></li>
- <li><a class="dropdown-item" routerLink="settings" disabled>Podešavanja</a></li>
<li>
<hr class="dropdown-divider">
</li>
@@ -32,10 +26,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="accent" 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="accent" 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 d5d1744f..e2551f7a 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.ts
+++ b/frontend/src/app/_elements/navbar/navbar.component.ts
@@ -1,30 +1,26 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Location } from '@angular/common';
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',
templateUrl: './navbar.component.html',
- styleUrls: ['./navbar.component.css']
+ styleUrls: ['./navbar.component.css'],
+ encapsulation: ViewEncapsulation.Emulated
})
export class NavbarComponent implements OnInit {
currentUrl: string;
shared = shared;
- constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService, private matDialog: MatDialog, private signalRService: SignalRService) {
+ constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService, private matDialog: MatDialog) {
shared.dialog = matDialog;
this.currentUrl = this.location.path();
this.location.onUrlChange(() => {
this.currentUrl = this.location.path();
- });
-
- this.auth.loggedInEvent.subscribe(_ => {
- this.signalRService.startConnection();
})
}
@@ -39,6 +35,5 @@ export class NavbarComponent implements OnInit {
logOut() {
this.auth.logOut();
- this.signalRService.stopConnection();
}
}
diff --git a/frontend/src/app/_elements/notifications/notifications.component.ts b/frontend/src/app/_elements/notifications/notifications.component.ts
index f324662a..5716c1e6 100644
--- a/frontend/src/app/_elements/notifications/notifications.component.ts
+++ b/frontend/src/app/_elements/notifications/notifications.component.ts
@@ -25,6 +25,7 @@ export class NotificationsComponent implements OnInit {
const existingNotification = this.notifications.find(x => x.id === mId)
const progress = ((currentEpoch + 1) / totalEpochs);
//console.log("Ukupno epoha", totalEpochs, "Trenutna epoha:", currentEpoch);
+ //console.log("stat:", stat);
if (!existingNotification)
this.notifications.push(new Notification(`Treniranje modela: ${mName}`, mId, progress, true));
else {
diff --git a/frontend/src/app/_elements/playlist/playlist.component.css b/frontend/src/app/_elements/playlist/playlist.component.css
new file mode 100644
index 00000000..353a094c
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.css
@@ -0,0 +1,61 @@
+.ns-wrapper {
+ width: 100%;
+ max-width: 800px;
+ max-height: 600px;
+ height: 100%;
+ transform-style: preserve-3d;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+}
+
+.ns-cards {
+ position: relative;
+ width: 300%;
+ height: 25rem;
+ margin-bottom: 20px;
+}
+
+.ns-card {
+ position: absolute;
+ width: 60%;
+ height: 100%;
+ left: 0;
+ right: 0;
+ margin: auto;
+ transition: transform 0.4s ease;
+ cursor: pointer;
+}
+
+.ns-card:hover {
+ opacity: 1 !important;
+}
+
+input[type=radio] {
+ display: none;
+}
+
+#item-1:checked~.ns-cards #view-item-3,
+#item-2:checked~.ns-cards #view-item-1,
+#item-3:checked~.ns-cards #view-item-2 {
+ transform: translatex(-40%) scale(0.8);
+ opacity: 0.5;
+ z-index: 0;
+}
+
+#item-1:checked~.ns-cards #view-item-2,
+#item-2:checked~.ns-cards #view-item-3,
+#item-3:checked~.ns-cards #view-item-1 {
+ transform: translatex(40%) scale(0.8);
+ opacity: 0.5;
+ z-index: 0;
+}
+
+#item-1:checked~.ns-cards #view-item-1,
+#item-2:checked~.ns-cards #view-item-2,
+#item-3:checked~.ns-cards #view-item-3 {
+ transform: translatex(0) scale(1);
+ opacity: 1;
+ z-index: 1;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/playlist/playlist.component.html b/frontend/src/app/_elements/playlist/playlist.component.html
new file mode 100644
index 00000000..b82de163
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.html
@@ -0,0 +1,19 @@
+<div class="ns-wrapper" *ngIf="tableDatas && tableDatas.length==3">
+ <input type="radio" name="slider" id="item-1" value="0" [(ngModel)]="selectedId">
+ <input type="radio" name="slider" id="item-2" value="1" [(ngModel)]="selectedId">
+ <input type="radio" name="slider" id="item-3" value="2" [(ngModel)]="selectedId">
+ <div class="ns-cards">
+ <label class="ns-card ns-bg-dark-100 ns-border-primary rounded" for="item-1" id="view-item-1">
+ <app-datatable [tableData]="tableDatas[0]"></app-datatable>
+ </label>
+ <label class="ns-card ns-bg-dark-100 ns-border-primary rounded" for="item-2" id="view-item-2">
+ <app-datatable [tableData]="tableDatas[1]"></app-datatable>
+ </label>
+ <label class="ns-card ns-bg-dark-100 ns-border-primary rounded" for="item-3" id="view-item-3">
+ <app-datatable [tableData]="tableDatas[2]"></app-datatable>
+ </label>
+ </div>
+ <div class="ns-infobox text-offwhite">
+ <h2>{{datasets[getIndex(selectedId)].name}}</h2>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/playlist/playlist.component.spec.ts b/frontend/src/app/_elements/playlist/playlist.component.spec.ts
new file mode 100644
index 00000000..0afe8041
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PlaylistComponent } from './playlist.component';
+
+describe('PlaylistComponent', () => {
+ let component: PlaylistComponent;
+ let fixture: ComponentFixture<PlaylistComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PlaylistComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PlaylistComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/playlist/playlist.component.ts b/frontend/src/app/_elements/playlist/playlist.component.ts
new file mode 100644
index 00000000..7f476178
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.ts
@@ -0,0 +1,49 @@
+import { Component, Input, OnInit } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import { TableData } from 'src/app/_elements/datatable/datatable.component';
+import { CsvParseService } from 'src/app/_services/csv-parse.service';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+
+@Component({
+ selector: 'app-playlist',
+ templateUrl: './playlist.component.html',
+ styleUrls: ['./playlist.component.css']
+})
+export class PlaylistComponent implements OnInit {
+
+ selectedId: string = "0";
+
+ @Input() datasets!: Dataset[];
+
+ tableDatas?: TableData[];
+
+ constructor(private datasetService: DatasetsService, private csv: CsvParseService) {
+
+ }
+
+ getIndex(str: string) {
+ return parseInt(str);
+ }
+
+ ngOnInit(): void {
+ this.tableDatas = [];
+
+ this.datasets.forEach((dataset, index) => {
+ if (index < 3) {
+ this.datasetService.getDatasetFile(dataset.fileId).subscribe((file: string | undefined) => {
+ if (file) {
+ const tableData = new TableData();
+ tableData.hasInput = true;
+ tableData.loaded = true;
+ tableData.numRows = dataset.rowCount;
+ tableData.numCols = dataset.columnInfo.length;
+ tableData.data = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter.toString() == "") ? "," : dataset.delimiter);
+ this.tableDatas!.push(tableData);
+ }
+ });
+ }
+ });
+
+ //console.log(this.tableDatas);
+ }
+}
diff --git a/frontend/src/app/_elements/reactive-background/reactive-background.component.html b/frontend/src/app/_elements/reactive-background/reactive-background.component.html
index 756952fb..c63dd6ac 100644
--- a/frontend/src/app/_elements/reactive-background/reactive-background.component.html
+++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.html
@@ -1 +1 @@
-<canvas id="bgCanvas" width="200" height="200" style="position: fixed; z-index: -1;"></canvas> \ No newline at end of file
+<canvas #bgCanvas width="200" height="200" style="position: fixed; z-index: -1;"></canvas> \ No newline at end of file
diff --git a/frontend/src/app/_elements/reactive-background/reactive-background.component.ts b/frontend/src/app/_elements/reactive-background/reactive-background.component.ts
index 980e3e6f..1a6157e3 100644
--- a/frontend/src/app/_elements/reactive-background/reactive-background.component.ts
+++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.ts
@@ -1,14 +1,19 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { CookieService } from 'ngx-cookie-service';
+import Shared from 'src/app/Shared';
@Component({
selector: 'app-reactive-background',
templateUrl: './reactive-background.component.html',
styleUrls: ['./reactive-background.component.css']
})
-export class ReactiveBackgroundComponent implements OnInit {
+export class ReactiveBackgroundComponent implements AfterViewInit {
+
+ @ViewChild('bgCanvas') canvasRef!: ElementRef;
@Input() numPoints: number = 450;
@Input() speed: number = 0.001; // 0-1
+ @Input() scrollSpeed: number = 1;
@Input() maxSize: number = 6;
@Input() minDistance: number = 0.07; //0-1
@@ -19,30 +24,43 @@ export class ReactiveBackgroundComponent implements OnInit {
@Input() pointColor: string = '#ffffff';
@Input() cursorLineColor: string = '#ff0000';
+ @Input() animate: boolean = true;
+ @Input() fill: number = 1.0;
+
+ private fleeSpeed = 0.005;
+
private points: Point[] = [];
private width = 200;
private height = 200;
private ratio = 1;
- private canvas?: HTMLCanvasElement;
- private ctx?: CanvasRenderingContext2D;
+ private canvas!: HTMLCanvasElement;
+ private ctx!: CanvasRenderingContext2D;
private time: number = 0;
- constructor() { }
+ constructor(private cookie: CookieService) { }
private mouseX = 0;
private mouseY = 0;
- ngOnInit(): void {
-
+ ngAfterViewInit(): void {
document.addEventListener('mousemove', (e) => {
this.mouseX = e.clientX / this.width;
this.mouseY = e.clientY / this.height;
})
- this.canvas = (<HTMLCanvasElement>document.getElementById('bgCanvas'));
+ document.addEventListener('mouseleave', _ => {
+ this.mouseX = -1;
+ this.mouseY = -1;
+ })
+
+ document.addEventListener('scroll', (e) => {
+ this.scrollBackground(e);
+ })
+
+ this.canvas = (<HTMLCanvasElement>this.canvasRef.nativeElement);
const ctx = this.canvas.getContext('2d');
if (ctx) {
this.ctx = ctx;
@@ -64,41 +82,77 @@ export class ReactiveBackgroundComponent implements OnInit {
this.resize();
setInterval(() => {
+ if (this.cookie.check('animateBackground')) {
+ this.animate = this.cookie.get('animateBackground') == 'true';
+ }
+ if (this.cookie.check('backgroundFill')) {
+ this.fill = parseFloat(this.cookie.get('backgroundFill'));
+ }
+
this.drawBackground();
}, 1000 / 60);
+
+ Shared.bgScroll.subscribe((amount) => {
+ this.scrollBackgroundFromSharedEvent(amount);
+ })
+ }
+
+ private lastScrollY: number = 0;
+
+ scrollBackgroundFromSharedEvent(amount: number) {
+ const scrolledAmount = amount - this.lastScrollY;
+ this.scrollPoints(scrolledAmount);
+ this.lastScrollY = amount;
+ }
+
+ scrollBackground(e: Event) {
+ const scrolledAmount = window.scrollY - this.lastScrollY;
+ this.scrollPoints(scrolledAmount);
+ this.lastScrollY = window.scrollY;
+ }
+
+ scrollPoints(amount: number) {
+ this.points.forEach((point, index) => {
+ if (index > this.numPoints * this.fill) return;
+ point.y = point.y - (amount / this.height) * this.scrollSpeed;
+ this.keepPointWithinBounds(point);
+ })
}
drawBackground() {
if (!this.ctx || !this.canvas) return;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.ctx.fillStyle = this.bgColor;
- this.ctx.fillRect(0, 0, this.width, this.height);
+ //this.ctx.fillStyle = this.bgColor;
+ //this.ctx.fillRect(0, 0, this.width, this.height);
this.points.forEach((point, index) => {
+ if (index > this.numPoints * this.fill) return;
+
this.drawLines(point, index);
this.drawPoint(point);
- this.updatePoint(point);
+
+ if (this.animate)
+ this.updatePoint(point);
});
//this.drawPoint(new Point(this.mouseX, this.mouseY, 12, 0));
-
- this.time += 1;
}
drawLines(p: Point, index: number) {
let i = index + 1;
- while (i < this.points.length) {
+ while (i < this.points.length * this.fill) {
const otherPoint = this.points[i];
const dist = this.distance(p.x, p.y, otherPoint.x, otherPoint.y);
if (dist < this.minDistance) {
const h = HEX[Math.round((1 - dist / this.minDistance) * 16)]
- this.ctx!.strokeStyle = this.lineColor + h + h;
- this.ctx!.beginPath();
- this.ctx!.moveTo(p.x * this.width, p.y * this.height);
- this.ctx!.lineTo(otherPoint.x * this.width, otherPoint.y * this.height);
- this.ctx!.stroke();
+ this.ctx.strokeStyle = this.lineColor + h;
+ this.ctx.lineWidth = this.maxSize / 2;
+ this.ctx.beginPath();
+ this.ctx.moveTo(p.x * this.width, p.y * this.height);
+ this.ctx.lineTo(otherPoint.x * this.width, otherPoint.y * this.height);
+ this.ctx.stroke();
}
i++;
@@ -106,10 +160,10 @@ export class ReactiveBackgroundComponent implements OnInit {
}
drawPoint(p: Point) {
- this.ctx!.fillStyle = this.pointColor;
- this.ctx!.beginPath();
- this.ctx!.arc(p.x * this.width, p.y * this.height, p.size, 0, 2 * Math.PI);
- this.ctx!.fill();
+ this.ctx.fillStyle = this.pointColor;
+ this.ctx.beginPath();
+ this.ctx.arc(p.x * this.width, p.y * this.height, p.size * this.screenDepth(p.x), 0, 2 * Math.PI);
+ this.ctx.fill();
}
resize() {
@@ -122,40 +176,66 @@ export class ReactiveBackgroundComponent implements OnInit {
this.canvas.height = this.height;
}
+ if (this.cookie.check('animateBackground')) {
+ this.animate = this.cookie.get('animateBackground') == 'true';
+ }
+ if (this.cookie.check('backgroundFill')) {
+ this.fill = parseFloat(this.cookie.get('backgroundFill'));
+ }
+
this.drawBackground();
}
updatePoint(p: Point) {
+ const mx = this.mouseX;
+ const my = this.mouseY;
+ const distToCursor = this.distance(p.x, p.y, mx, my);
+
+ if (distToCursor < this.cursorDistance) {
+
+ const t = (distToCursor / this.cursorDistance);
+ p.x -= ((mx - p.x) / distToCursor) * this.speed * (1 + t * 2);
+ p.y -= ((my - p.y) / distToCursor) * this.speed * (1 + t * 2);
+
+ p.direction = this.lerp(p.direction, Math.atan2(my - p.y, mx - p.x) * 180 / Math.PI, t);
+
+ const grd = this.ctx.createLinearGradient(p.x * this.width, p.y * this.height, mx * this.width, my * this.height);
+ const alpha = HEX[Math.round(p.size / this.maxSize * (HEX.length - 1))];
+ grd.addColorStop(0, this.cursorLineColor + alpha);
+ grd.addColorStop(0.5, this.cursorLineColor + '00');
+ this.ctx.strokeStyle = grd;
+ this.ctx.beginPath();
+ this.ctx.moveTo(p.x * this.width, p.y * this.height);
+ this.ctx.lineTo(mx * this.width, my * this.height);
+ this.ctx.stroke();
+ }
+
const vx = Math.sin(p.direction);
const vy = Math.cos(p.direction);
p.x = p.x + vx * this.speed;
p.y = p.y + vy * this.speed;
- const mx = this.mouseX;
- const my = this.mouseY;
- const distToCursor = this.distance(p.x, p.y, mx, my);
- if (distToCursor < this.cursorDistance) {
+ this.keepPointWithinBounds(p);
+ }
- p.x -= ((mx - p.x) / distToCursor) / 500;
- p.y -= ((my - p.y) / distToCursor) / 500;
-
- const grd = this.ctx!.createLinearGradient(p.x * this.width, p.y * this.height, mx * this.width, my * this.height);
- grd.addColorStop(0, this.cursorLineColor + 'ff');
- grd.addColorStop(1, this.cursorLineColor + '00');
- this.ctx!.strokeStyle = grd;
- this.ctx!.beginPath();
- this.ctx!.moveTo(p.x * this.width, p.y * this.height);
- this.ctx!.lineTo(mx * this.width, my * this.height);
- this.ctx!.stroke();
- }
+ lerp(start: number, end: number, amt: number) {
+ return (1 - amt) * start + amt * end
+ }
- p.x %= 1;
- p.y %= 1;
+ keepPointWithinBounds(p: Point) {
+ p.x = p.x % 1.0;
+ p.y = p.y % 1.0;
+ p.x = ((1 - Math.sign(p.x)) / 2) + p.x;
+ p.y = ((1 - Math.sign(p.y)) / 2) + p.y;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
- return Math.sqrt(((x2 - x1) ** 2) + ((y2 / this.ratio - y1 / this.ratio) ** 2));
+ return Math.sqrt(((x2 - x1) ** 2) + ((y2 / this.ratio - y1 / this.ratio) ** 2) / this.screenDepth(x1)) * this.ratio;
+ }
+
+ screenDepth(x: number): number {
+ return (1.5 - Math.sin(x * Math.PI));
}
}
@@ -168,4 +248,4 @@ class Point {
) { }
}
-const HEX = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; \ No newline at end of file
+const HEX = ['00', '11', '22', '33', '44', '55', '66', '77', '88', '99', 'aa', 'bb', 'cc', 'dd', 'ee', 'ff']; \ No newline at end of file
diff --git a/frontend/src/app/_modals/alert-dialog/alert-dialog.component.css b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.css
index e69de29b..bdfd06c9 100644
--- a/frontend/src/app/_modals/alert-dialog/alert-dialog.component.css
+++ b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.css
@@ -0,0 +1,3 @@
+mat-dialog-container{
+ background: var(ns-bg-dark-50) !important;
+} \ No newline at end of file
diff --git a/frontend/src/app/_modals/alert-dialog/alert-dialog.component.html b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.html
index 82365193..2d7e4d86 100644
--- a/frontend/src/app/_modals/alert-dialog/alert-dialog.component.html
+++ b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.html
@@ -1,7 +1,9 @@
-<h2 mat-dialog-title class="text-muted">{{data.title}}</h2>
-<div mat-dialog-content class="mt-4" style="color: rgb(81, 76, 76);">
- {{data.message}}
-</div>
-<div mat-dialog-actions class="d-flex justify-content-center mt-4">
- <button mat-button cdkFocusInitial (click)="onOkClick()" style="background-color: lightgray;">OK</button>
-</div> \ No newline at end of file
+
+
+ <h2 mat-dialog-title >{{data.title}}</h2>
+ <div mat-dialog-content class="mt-4 text-offwhite" >
+ {{data.message}}
+ </div>
+ <div mat-dialog-actions class="d-flex justify-content-center mt-4">
+ <button mat-raised-button cdkFocusInitial (click)="onOkClick()" color="basic">OK</button>
+ </div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.css b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.css
+++ b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.css
diff --git a/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.html b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.html
new file mode 100644
index 00000000..7ba286cb
--- /dev/null
+++ b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.html
@@ -0,0 +1,16 @@
+<h1 mat-dialog-title>Enkodiranje svih kolona</h1>
+<div mat-dialog-content>
+ <p>Odaberite tip enkodinga za sve kolone zajedno:</p>
+ <mat-form-field>
+ <mat-select matNativeControl [(value)]="selectedEncodingType">
+ <mat-option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ <p>Da li ste sigurni u izbor?</p>
+</div>
+<div mat-dialog-actions>
+ <button mat-button [mat-dialog-close]="selectedEncodingType" cdkFocusInitial>Da</button>
+ <button mat-button (click)="onNoClick()">Odustani</button>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.spec.ts b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.spec.ts
index 1da7d05d..77f30ae3 100644
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.spec.ts
+++ b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemExperimentComponent } from './item-experiment.component';
+import { EncodingDialogComponent } from './encoding-dialog.component';
-describe('ItemExperimentComponent', () => {
- let component: ItemExperimentComponent;
- let fixture: ComponentFixture<ItemExperimentComponent>;
+describe('EncodingDialogComponent', () => {
+ let component: EncodingDialogComponent;
+ let fixture: ComponentFixture<EncodingDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemExperimentComponent ]
+ declarations: [ EncodingDialogComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemExperimentComponent);
+ fixture = TestBed.createComponent(EncodingDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.ts b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.ts
new file mode 100644
index 00000000..3b7560bf
--- /dev/null
+++ b/frontend/src/app/_modals/encoding-dialog/encoding-dialog.component.ts
@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+import { MatDialogRef } from '@angular/material/dialog';
+import { Encoding } from 'src/app/_data/Experiment';
+
+
+@Component({
+ selector: 'app-encoding-dialog',
+ templateUrl: './encoding-dialog.component.html',
+ styleUrls: ['./encoding-dialog.component.css']
+})
+export class EncodingDialogComponent implements OnInit {
+
+ selectedEncodingType?: Encoding;
+ Encoding = Encoding;
+ Object = Object;
+
+ constructor(public dialogRef: MatDialogRef<EncodingDialogComponent>)
+ {
+ this.selectedEncodingType = Encoding.Label;
+ }
+
+ ngOnInit(): void {
+ }
+
+ onNoClick() {
+ this.dialogRef.close();
+ }
+}
diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.css b/frontend/src/app/_modals/login-modal/login-modal.component.css
index e69de29b..f8ffe797 100644
--- a/frontend/src/app/_modals/login-modal/login-modal.component.css
+++ b/frontend/src/app/_modals/login-modal/login-modal.component.css
@@ -0,0 +1,37 @@
+.modal-content {
+ text-align: center;
+ width: 80%;
+ margin: auto;
+}
+
+.modal-footer {
+ text-align: center;
+}
+
+#loginButton {
+ color: white;
+ background-color: #003459;
+ margin-right: 10px;
+}
+
+#loginButton,
+#doNotLoginButton {
+ padding: 2% 6%;
+}
+
+.close-button {
+ margin: 2%;
+}
+
+#link {
+ text-decoration: underline;
+}
+
+#link:hover {
+ color: var(--offwhite);
+ font-size: 110%;
+}
+
+#wrong-creds {
+ color: var(--ns-warn);
+} \ No newline at end of file
diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.html b/frontend/src/app/_modals/login-modal/login-modal.component.html
index 03048155..cea6bf39 100644
--- a/frontend/src/app/_modals/login-modal/login-modal.component.html
+++ b/frontend/src/app/_modals/login-modal/login-modal.component.html
@@ -1,42 +1,49 @@
<!-- Modal -->
<div class="modal fade" id="modalForLogin" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
- <div class="modal-dialog modal-dialog-centered">
- <div class="modal-content">
- <div class="modal-header" style="background-color: #003459;">
- <button #closeButton type="button" class="btn-close" style="background-color:white;" data-bs-dismiss="modal" aria-label="Close" (click)="resetData()"></button>
- </div>
- <div class="modal-body px-5" style="color:#003459">
- <h1 class="text-center mt-2 mb-4">Prijavite se</h1>
- <form>
- <!-- Korisnicko ime -->
- <div class="form-outline mb-3">
- <label class="form-label" for="username">Korisničko ime</label>
- <input [(ngModel)]="username" name="username" type="text" id="username"
- class="form-control form-control" placeholder="Unesite korisničko ime..." />
- </div>
- <!-- Lozinka -->
- <div class="form-outline mb-3">
- <label class="form-label" for="password">Lozinka</label>
- <input [(ngModel)]="password" name="password" type="password" id="password"
- class="form-control form-control" placeholder="Unesite lozinku..." />
+ <div class="modal-dialog modal-dialog-centered">
+ <div class="modal-content bg-alt text-offwhite">
+ <button #closeButton type="button" class="close-button btn-clear" data-bs-dismiss="modal" aria-label="Close" (click)="resetData()">
+ <mat-icon>close</mat-icon>
+ </button>
+ <h1 class="login-heading mt-5 mb-5">Prijava</h1>
+ <form>
+ <!-- Korisnicko ime -->
+ <div class="mb-3">
+ <mat-form-field appearance="fill">
+ <mat-label>Korisničko ime</mat-label>
+ <input type="text" matInput [(ngModel)]="username" name="username" id="username">
+ <mat-icon matSuffix></mat-icon>
+ </mat-form-field>
+ </div>
+ <!-- Lozinka -->
+ <div class="mb-4">
+ <mat-form-field appearance="fill">
+ <mat-label>Lozinka</mat-label>
+ <input type="password" matInput [(ngModel)]="password" name="password" id="pass" #pass>
+ <ng-container matSuffix *ngIf="!passwordShown">
+ <mat-icon (click)="togglePasswordShown()">visibility_off</mat-icon>
+ </ng-container>
+ <ng-container matSuffix *ngIf="passwordShown">
+ <mat-icon (click)="togglePasswordShown()">visibility</mat-icon>
+ </ng-container>
+ </mat-form-field>
+ </div>
+ </form>
+
+ <div class="text-lg-start">
+ <p *ngIf="wrongCreds" class="small text-center" id="wrong-creds">Unesite ispravno korisničko ime i lozinku.</p>
</div>
- </form>
-
- <div class="text-center text-lg-start mt-5">
- <p *ngIf="wrongCreds" class="small fw-bold text-danger text-center">Unesite ispravan e-mail i lozinku.</p>
- </div>
- <div class="col-md-12 d-flex justify-content-center">
- <button type="button" class="btn btn-lg" style="color:white; background-color: #003459; margin-right: 10px;" (click)="doLogin()">Prijavite se</button>
- <button type="button" class="btn btn-lg btn-outline-secondary" data-bs-dismiss="modal" (click)="resetData()">Odustanite</button>
- </div>
- <br>
- </div>
- <div class="modal-footer justify-content-center">
- <p class="small fw-bold">Još uvek nemate nalog?
- <a data-bs-toggle="modal" data-bs-target="#modalForRegister" class="link-danger">Registrujte se</a>
- </p>
- </div>
+ <!--mat-raised-button-->
+ <div class="d-flex justify-content-center">
+ <button mat-raised-button id="loginButton" (click)="doLogin()">Prijavite se</button>
+ <button mat-stroked-button id="doNotloginButton" data-bs-dismiss="modal" (click)="resetData()">Odustanite</button>
+ </div>
+ <div class="modal-footer justify-content-center mt-5">
+ <p class="small mt-1">Nemate nalog?
+ <a data-bs-toggle="modal" data-bs-target="#modalForRegister"><span id="link" (click)="cleanWarnings()">Registrujte se</span></a>
+ </p>
+ </div>
+ </div>
</div>
- </div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.ts b/frontend/src/app/_modals/login-modal/login-modal.component.ts
index b28d9799..ccd78509 100644
--- a/frontend/src/app/_modals/login-modal/login-modal.component.ts
+++ b/frontend/src/app/_modals/login-modal/login-modal.component.ts
@@ -14,10 +14,13 @@ import {AfterViewInit, ElementRef} from '@angular/core';
export class LoginModalComponent implements OnInit {
@ViewChild('closeButton') closeButton?: ElementRef;
+ @ViewChild('pass') passwordInput!: ElementRef;
username: string = '';
password: string = '';
+ passwordShown: boolean = false;
+
wrongCreds: boolean = false;
constructor(
@@ -37,14 +40,19 @@ export class LoginModalComponent implements OnInit {
if (response == "Username doesn't exist" || response == "Wrong password") {
this.wrongCreds = true;
this.password = '';
+ this.passwordShown = false;
+ this.passwordInput.nativeElement.type = "password";
}
else {
+ this.wrongCreds = false;
this.authService.authenticate(response);
(<HTMLSelectElement>this.closeButton?.nativeElement).click();
this.userInfoService.getUserInfo().subscribe((response) => {
shared.photoId = response.photoId;
});
+ location.reload();
}
+
});
}
else {
@@ -57,4 +65,17 @@ export class LoginModalComponent implements OnInit {
this.username = '';
this.password = '';
}
+
+ togglePasswordShown() {
+ this.passwordShown = !this.passwordShown;
+
+ if (this.passwordShown)
+ this.passwordInput.nativeElement.type = "text";
+ else
+ this.passwordInput.nativeElement.type = "password";
+ }
+
+ cleanWarnings() {
+ this.wrongCreds = false;
+ }
}
diff --git a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.css b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.css
diff --git a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.html b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.html
new file mode 100644
index 00000000..81aec5f8
--- /dev/null
+++ b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.html
@@ -0,0 +1,13 @@
+<h1 mat-dialog-title>Popunjavanje nedostajućih vrednosti</h1>
+<div mat-dialog-content>
+ <p>Želim da:</p>
+ <mat-radio-group [(ngModel)]="selectedMissingValuesOption">
+ <mat-radio-button [value]="NullValueOptions.DeleteColumns" checked>obrišem sve kolone koje sadrže nedostajuće vrednosti</mat-radio-button>
+ <mat-radio-button [value]="NullValueOptions.DeleteRows">obrišem sve redove koji sadrže nedostajuće vrednosti</mat-radio-button>
+ </mat-radio-group>
+ <p>Da li ste sigurni u izbor?</p>
+</div>
+<div mat-dialog-actions>
+ <button mat-button [mat-dialog-close]="selectedMissingValuesOption" cdkFocusInitial>Da</button>
+ <button mat-button (click)="onNoClick()">Odustani</button>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.spec.ts b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.spec.ts
new file mode 100644
index 00000000..958925f4
--- /dev/null
+++ b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MissingvaluesDialogComponent } from './missingvalues-dialog.component';
+
+describe('MissingvaluesDialogComponent', () => {
+ let component: MissingvaluesDialogComponent;
+ let fixture: ComponentFixture<MissingvaluesDialogComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ MissingvaluesDialogComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MissingvaluesDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.ts b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.ts
new file mode 100644
index 00000000..908edd9e
--- /dev/null
+++ b/frontend/src/app/_modals/missingvalues-dialog/missingvalues-dialog.component.ts
@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+import { MatDialogRef } from '@angular/material/dialog';
+import { NullValueOptions } from 'src/app/_data/Experiment';
+
+@Component({
+ selector: 'app-missingvalues-dialog',
+ templateUrl: './missingvalues-dialog.component.html',
+ styleUrls: ['./missingvalues-dialog.component.css']
+})
+export class MissingvaluesDialogComponent implements OnInit {
+
+ selectedMissingValuesOption?: NullValueOptions;
+
+ NullValueOptions = NullValueOptions;
+
+ constructor(public dialogRef: MatDialogRef<MissingvaluesDialogComponent>)
+ {
+ this.selectedMissingValuesOption = NullValueOptions.DeleteColumns;
+ }
+
+ ngOnInit(): void {
+ }
+
+ onNoClick() {
+ this.dialogRef.close();
+ }
+
+}
diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.css b/frontend/src/app/_modals/register-modal/register-modal.component.css
index e69de29b..f8496973 100644
--- a/frontend/src/app/_modals/register-modal/register-modal.component.css
+++ b/frontend/src/app/_modals/register-modal/register-modal.component.css
@@ -0,0 +1,44 @@
+.modal-content {
+ text-align: center;
+ margin: auto;
+}
+
+.modal-footer {
+ text-align: center;
+}
+
+#registerButton {
+ color: white;
+ background-color: #003459;
+ margin-right: 10px;
+}
+
+#registerButton,
+#doNotRegisterButton {
+ padding: 2% 7%;
+}
+
+.close-button {
+ margin: 2%;
+}
+
+#link {
+ text-decoration: underline;
+}
+
+#link:hover {
+ color: var(--offwhite);
+ font-size: 110%;
+}
+
+.mat-form-field {
+ width: 80%;
+}
+
+.wrong-creds {
+ color: var(--ns-warn);
+}
+
+p {
+ font-size: 11px;
+} \ No newline at end of file
diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.html b/frontend/src/app/_modals/register-modal/register-modal.component.html
index 68025a46..d76af4d6 100644
--- a/frontend/src/app/_modals/register-modal/register-modal.component.html
+++ b/frontend/src/app/_modals/register-modal/register-modal.component.html
@@ -1,86 +1,85 @@
<!-- Modal -->
-<div class="modal fade" id="modalForRegister" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
- aria-labelledby="staticBackdropLabel" aria-hidden="true">
- <div class="modal-dialog modal-dialog-centered modal-dialog modal-lg">
- <div class="modal-content">
- <div class="modal-header" style="background-color: #003459;">
- <button id="closeButtonReg" type="button" class="btn-close" data-bs-dismiss="modal" style="background-color: white;"
- aria-label="Close" (click)="resetData()"></button>
- </div>
- <div class="modal-body" style="color:#003459">
- <h1 class="text-center mt-2 mb-4">Registracija</h1>
-
- <form class="mx-5">
- <!--Ime-->
- <div class="row">
- <div class="col-6 px-3 py-3">
- <label class="form-label" for="firstName">Ime</label>
- <input type="text" id="firstName" class="form-control" [(ngModel)]="firstName"
- name="firstName" placeholder="Unesite ime...">
- <small *ngIf="wrongFirstNameBool" class="form-text text-danger">Unesite ispravno
- ime.</small>
- </div>
- <!--Prezime-->
- <div class="col-6 px-3 py-3">
- <label class="form-label" for="lastName">Prezime</label>
- <input type="text" id="lastName" class="form-control" [(ngModel)]="lastName" name="lastName"
- placeholder="Unesite prezime..." />
- <small *ngIf="wrongLastNameBool" class="form-text text-danger">Unesite ispravno
- prezime.</small>
- </div>
- </div>
- <div class="row">
- <!--Korisnicko ime-->
- <div class="col-12 px-3 py-3">
- <label class="form-label" for="username-register">Korisničko ime</label>
- <input type="text" id="username-register" class="form-control" [(ngModel)]="username"
- name="username-register" placeholder="Unesite korisničko ime..." />
- <small *ngIf="wrongUsernameBool" class="form-text text-danger">Unesite ispravno korisničko
- ime.</small>
- </div>
- </div>
- <div class="row">
- <!--Email-->
- <div class="col-12 px-3 py-3">
- <label class="form-label" for="email">E-mail adresa</label>
- <input type="email" id="email" class="form-control" [(ngModel)]="email" name="email"
- placeholder="Unesite email adresu..." />
- <small *ngIf="wrongEmailBool" class="form-text text-danger">Unesite ispravno e-mail
- adresu.</small>
- </div>
- </div>
- <div class="row">
- <!-- Lozinka 1. -->
- <div class="col-6 px-3 py-3">
- <label class="form-label" for="pass1">Lozinka</label>
- <input type="password" id="pass1" class="form-control" [(ngModel)]="pass1" name="pass1"
- placeholder="Unesite lozinku..." />
- <small *ngIf="wrongPass1Bool" class="form-text text-danger">Lozinka se mora sastojati od
- najmanje 6 karaktera.</small>
- </div>
- <!-- Lozinka 2. -->
- <div class="col-6 px-3 py-3">
- <label class="form-label" for="pass2">Potvrdite lozinku</label>
- <input type="password" id="pass2" class="form-control" [(ngModel)]="pass2" name="pass2"
- placeholder="Ponovite lozinku..." />
- <small *ngIf="wrongPass2Bool" class="form-text text-danger">Lozinke se ne
- podudaraju.</small>
- </div>
- </div>
- </form>
- <div class="col-md-12 d-flex justify-content-center mt-5">
- <button type="button" class="btn btn-lg"
- style="color:white; background-color: #003459; margin-right: 10px;"
- (click)="doRegister()">Registrujte se</button>
- <button type="button" class="btn btn-lg btn-outline-secondary" style="margin-left: 15px;"
- data-bs-dismiss="modal" (click)="resetData()">Odustanite</button>
+<div class="modal fade" id="modalForRegister" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
+ <div class="modal-dialog modal-dialog-centered">
+ <div class="modal-content bg-alt text-offwhite">
+ <button #closeButton type="button" class="close-button btn-clear" data-bs-dismiss="modal" aria-label="Close" (click)="resetData()">
+ <mat-icon>close</mat-icon>
+ </button>
+ <h1 class="mt-5 mb-4">Registracija</h1>
+ <form class="mx-4">
+ <!--Ime-->
+ <div>
+ <mat-form-field appearance="fill">
+ <mat-label>Ime</mat-label>
+ <input type="text" matInput [(ngModel)]="firstName" name="firstName" id="firstName">
+ <mat-icon matSuffix></mat-icon>
+ </mat-form-field>
+ <p *ngIf="wrongFirstNameBool" class="wrong-creds">Unesite ispravno ime.</p>
+ </div>
+ <!--Prezime-->
+ <div>
+ <mat-form-field appearance="fill">
+ <mat-label>Prezime</mat-label>
+ <input type="text" matInput [(ngModel)]="lastName" name="lastName" id="lastName">
+ <mat-icon matSuffix></mat-icon>
+ </mat-form-field>
+ <p *ngIf="wrongLastNameBool" class="wrong-creds">Unesite ispravno prezime.</p>
+ </div>
+ <!--Korisnicko ime-->
+ <div>
+ <mat-form-field appearance="fill">
+ <mat-label>Korisničko ime</mat-label>
+ <input type="text" matInput [(ngModel)]="username" name="username-register" id="username-register">
+ <mat-icon matSuffix></mat-icon>
+ </mat-form-field>
+ <p *ngIf="wrongUsernameBool" class="wrong-creds">Unesite ispravno korisničko ime.</p>
+ </div>
+ <!--Email-->
+ <div>
+ <mat-form-field appearance="fill">
+ <mat-label>E-mail adresa</mat-label>
+ <input type="email" matInput [(ngModel)]="email" name="email" id="email">
+ <mat-icon matSuffix></mat-icon>
+ </mat-form-field>
+ <p *ngIf="wrongEmailBool" class="wrong-creds">Unesite ispravno e-mail adresu.</p>
+ </div>
+ <!-- Lozinka 1. -->
+ <div>
+ <mat-form-field appearance="fill">
+ <mat-label>Lozinka</mat-label>
+ <input type="password" matInput [(ngModel)]="pass1" name="pass1" id="pass1" #password1>
+ <ng-container matSuffix *ngIf="!password1Shown">
+ <mat-icon (click)="togglePasswordShown(1)">visibility_off</mat-icon>
+ </ng-container>
+ <ng-container matSuffix *ngIf="password1Shown">
+ <mat-icon (click)="togglePasswordShown(1)">visibility</mat-icon>
+ </ng-container>
+ </mat-form-field>
+ <p *ngIf="wrongPass1Bool" class="wrong-creds">Lozinka se mora sastojati od najmanje 6 karaktera.</p>
+ </div>
+ <!-- Lozinka 2. -->
+ <div>
+ <mat-form-field appearance="fill">
+ <mat-label>Potvrdite lozinku</mat-label>
+ <input type="password" matInput [(ngModel)]="pass2" name="pass2" id="pass2" #password2>
+ <ng-container matSuffix *ngIf="!password2Shown">
+ <mat-icon (click)="togglePasswordShown(2)">visibility_off</mat-icon>
+ </ng-container>
+ <ng-container matSuffix *ngIf="password2Shown">
+ <mat-icon (click)="togglePasswordShown(2)">visibility</mat-icon>
+ </ng-container>
+ </mat-form-field>
+ <p *ngIf="wrongPass2Bool" class="wrong-creds">Lozinke se ne podudaraju.</p>
</div>
- <br>
+ </form>
+ <div class="d-flex justify-content-center mt-2">
+ <button mat-raised-button id="registerButton" (click)="doRegister()">Registrujte se</button>
+ <button mat-stroked-button id="doNotRegisterButton" data-bs-dismiss="modal" (click)="resetData()">Odustanite</button>
</div>
- <div class="modal-footer justify-content-center">
- <p class="small fw-bold">Već imate kreiran nalog?
- <a id="linkToLoginModal" data-bs-toggle="modal" data-bs-target="#modalForLogin"
- class="link-danger">Prijavite se</a>
+ <br>
+ <div class="modal-footer justify-content-center mt-3">
+ <p class="small">Imate kreiran nalog?
+ <a data-bs-toggle="modal" data-bs-target="#modalForLogin"><span id="link" (click)="cleanWarnings()">Prijavite se</span></a>
</p>
</div>
</div>
diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.ts b/frontend/src/app/_modals/register-modal/register-modal.component.ts
index 11f6727d..9da5484d 100644
--- a/frontend/src/app/_modals/register-modal/register-modal.component.ts
+++ b/frontend/src/app/_modals/register-modal/register-modal.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { AuthService } from 'src/app/_services/auth.service';
import User from 'src/app/_data/User';
import { DOCUMENT } from '@angular/common';
@@ -34,6 +34,13 @@ export class RegisterModalComponent implements OnInit {
shared = shared;
+ password1Shown: boolean = false;
+ password2Shown: boolean = false;
+ @ViewChild('password1') password1Input!: ElementRef;
+ @ViewChild('password2') password2Input!: ElementRef;
+ @ViewChild('closeButton') closeButton!: ElementRef;
+
+
constructor(
private authService: AuthService,
@Inject(DOCUMENT) document: Document
@@ -48,6 +55,8 @@ export class RegisterModalComponent implements OnInit {
resetData() {
this.firstName = this.lastName = this.username = this.email = this.pass1 = this.pass2 = '';
this.wrongFirstNameBool = this.wrongLastNameBool = this.wrongUsernameBool = this.wrongEmailBool = this.wrongPass1Bool = this.wrongPass2Bool = false;
+ this.password1Shown = false;
+ this.password2Shown = false;
}
isCorrectName(element: string): boolean {
@@ -76,7 +85,7 @@ export class RegisterModalComponent implements OnInit {
this.wrongFirstNameBool = false;
return;
}
- (<HTMLSelectElement>document.getElementById('firstName')).focus();
+ //(<HTMLSelectElement>document.getElementById('firstName')).focus();
this.wrongFirstNameBool = true;
}
lastNameValidation() {
@@ -84,7 +93,7 @@ export class RegisterModalComponent implements OnInit {
this.wrongLastNameBool = false;
return;
}
- (<HTMLSelectElement>document.getElementById('lastName')).focus();
+ //(<HTMLSelectElement>document.getElementById('lastName')).focus();
this.wrongLastNameBool = true;
}
usernameValidation() {
@@ -92,7 +101,7 @@ export class RegisterModalComponent implements OnInit {
this.wrongUsernameBool = false;
return;
}
- (<HTMLSelectElement>document.getElementById('username-register')).focus();
+ //(<HTMLSelectElement>document.getElementById('username-register')).focus();
this.wrongUsernameBool = true;
}
emailValidation() {
@@ -100,7 +109,7 @@ export class RegisterModalComponent implements OnInit {
this.wrongEmailBool = false;
return;
}
- (<HTMLSelectElement>document.getElementById('email')).focus();
+ //(<HTMLSelectElement>document.getElementById('email')).focus();
this.wrongEmailBool = true;
}
passwordValidation() {
@@ -111,7 +120,11 @@ export class RegisterModalComponent implements OnInit {
}
this.pass1 = ''; //brisi obe ukucane lozinke
this.pass2 = '';
- (<HTMLSelectElement>document.getElementById('pass1')).focus();
+ this.password1Shown = false;
+ this.password2Shown = false;
+ this.password1Input.nativeElement.type = "password";
+ this.password2Input.nativeElement.type = "password";
+ //(<HTMLSelectElement>document.getElementById('pass1')).focus();
this.wrongPass1Bool = true;
this.wrongPass2Bool = true;
}
@@ -148,7 +161,8 @@ export class RegisterModalComponent implements OnInit {
this.authService.login(this.username, this.pass1).subscribe((response) => {
this.authService.authenticate(response);
- (<HTMLSelectElement>document.getElementById('closeButtonReg')).click();
+ this.closeButton.nativeElement.click();
+ location.reload();
//(<HTMLSelectElement>document.getElementById('linkToLoginModal')).click();
}, (error) => console.warn(error));
}
@@ -165,5 +179,25 @@ export class RegisterModalComponent implements OnInit {
}
}
+ togglePasswordShown(whichPassword: number) {
+ if (whichPassword == 1) {
+ this.password1Shown = !this.password1Shown;
+
+ if (this.password1Shown)
+ this.password1Input.nativeElement.type = "text";
+ else
+ this.password1Input.nativeElement.type = "password";
+ }
+ else {
+ this.password2Shown = !this.password2Shown;
+ if (this.password2Shown)
+ this.password2Input.nativeElement.type = "text";
+ else
+ this.password2Input.nativeElement.type = "password";
+ }
+ }
+ cleanWarnings() {
+ this.resetData();
+ }
}
diff --git a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.css b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.css
diff --git a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.html b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.html
new file mode 100644
index 00000000..bac73e0a
--- /dev/null
+++ b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.html
@@ -0,0 +1,13 @@
+<h1 mat-dialog-title>Čuvanje eksperimenta</h1>
+<div mat-dialog-content>
+ <span>Unesi naziv eksperimenta:</span>
+ <mat-form-field>
+ <input type="text" matInput [(ngModel)]="selectedName">
+ </mat-form-field>
+ <br><br>
+ <p>Sačuvaj eksperiment:</p>
+</div>
+<div mat-dialog-actions>
+ <button mat-button [mat-dialog-close]="selectedName" cdkFocusInitial>Da</button>
+ <button mat-button (click)="onNoClick()">Odustani</button>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.spec.ts b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.spec.ts
new file mode 100644
index 00000000..5fd6cb71
--- /dev/null
+++ b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SaveExperimentDialogComponent } from './save-experiment-dialog.component';
+
+describe('SaveExperimentDialogComponent', () => {
+ let component: SaveExperimentDialogComponent;
+ let fixture: ComponentFixture<SaveExperimentDialogComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ SaveExperimentDialogComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SaveExperimentDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.ts b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.ts
new file mode 100644
index 00000000..ca01f57e
--- /dev/null
+++ b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.ts
@@ -0,0 +1,21 @@
+import { Component, OnInit } from '@angular/core';
+import { MatDialogRef } from '@angular/material/dialog';
+
+@Component({
+ selector: 'app-save-experiment-dialog',
+ templateUrl: './save-experiment-dialog.component.html',
+ styleUrls: ['./save-experiment-dialog.component.css']
+})
+export class SaveExperimentDialogComponent implements OnInit {
+
+ selectedName: string = '';
+
+ constructor(public dialogRef: MatDialogRef<SaveExperimentDialogComponent>) { }
+
+ ngOnInit(): void {
+ }
+
+ onNoClick() {
+ this.dialogRef.close();
+ }
+}
diff --git a/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html
index 572e8c4c..06e74093 100644
--- a/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html
+++ b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html
@@ -1,8 +1,8 @@
<h2 mat-dialog-title class="text-muted">{{data.title}}</h2>
<div mat-dialog-content class="mt-4" style="color: rgb(81, 76, 76);">
- {{data.message}}
+ {{data.message}}
</div>
<div mat-dialog-actions class="d-flex justify-content-center mt-4">
- <button mat-button cdkFocusInitial (click)="onYesClick()" class="btn-lg" style="background-color: #003459; color:white;">Da</button>
- <button mat-button cdkFocusInitial (click)="onNoClick()" class="btn-lg" style="background-color: lightgray;">Ne</button>
+ <button mat-button cdkFocusInitial (click)="onYesClick()" style="background-color: lightgray;">Da</button>
+ <button mat-button cdkFocusInitial (click)="onNoClick()" style="background-color: lightgray;">Ne</button>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/archive/archive.component.css b/frontend/src/app/_pages/archive/archive.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_pages/archive/archive.component.css
diff --git a/frontend/src/app/_pages/archive/archive.component.html b/frontend/src/app/_pages/archive/archive.component.html
new file mode 100644
index 00000000..fc3c4763
--- /dev/null
+++ b/frontend/src/app/_pages/archive/archive.component.html
@@ -0,0 +1,49 @@
+<div class="d-flex flex-column align-items-center my-5">
+ <app-folder></app-folder>
+
+ <!--<div class="my-5" style="height: fit-content;">
+ <app-playlist [datasets]="publicDatasets"></app-playlist>
+ </div>-->
+
+ <!--<div id="cards" class="row align-items-view align-items-stretch justify-content-center">
+ <div class="card shadowed bg-light text-light col-3 m-3" style="width: 18rem;">
+ <div class="card-body">
+ <mat-icon width="48px" height="48px" style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">model_training
+ </mat-icon>
+ <h3 class="card-title my-2">Moji eksperimenti</h3>
+ <p class="card-text">
+ <a class="stretched-link" routerLink="my-models">Pregledajte</a> vaše modele, menjajte ih, napravite nove modele, ili ih obrišite.
+ </p>
+ </div>
+ </div>
+ <div class="card shadowed bg-light text-light col-3 m-3" style="width: 18rem;">
+ <div class="card-body">
+ <mat-icon width="48px" height="48px" style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">storage</mat-icon>
+ <h3 class="card-title my-2">Izvori podataka</h3>
+ <p class="card-text">
+ <a class="stretched-link" routerLink="my-datasets">Preuredite</a> vaše izvore podataka, ili dodajte novi.
+ </p>
+ </div>
+ </div>
+ <div class="card shadowed bg-light text-light col-3 m-3" style="width: 18rem;">
+ <div class="card-body">
+ <mat-icon width="48px" height="48px" style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">model_training
+ </mat-icon>
+ <h3 class="card-title my-2">Modeli</h3>
+ <p class="card-text">
+ <a class="stretched-link" routerLink="my-models">Pregledajte</a> vaše modele, menjajte ih, napravite nove modele, ili ih obrišite.
+ </p>
+ </div>
+ </div>
+ <div class="card shadowed bg-light text-light col-3 m-3" style="width: 18rem;">
+ <div class="card-body">
+ <mat-icon width="48px" height="48px" style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">batch_prediction
+ </mat-icon>
+ <h3 class="card-title my-2">Rezultati treniranja</h3>
+ <p class="card-text">
+ <a class="stretched-link" routerLink="my-predictors">Pregledajte</a> sve vaše trenirane modele, koristite ih da predvidite vrednosti za red ili skup podataka, ili ih obrišite.
+ </p>
+ </div>
+ </div>
+ </div>-->
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/archive/archive.component.spec.ts b/frontend/src/app/_pages/archive/archive.component.spec.ts
new file mode 100644
index 00000000..41fc8e77
--- /dev/null
+++ b/frontend/src/app/_pages/archive/archive.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ArchiveComponent } from './archive.component';
+
+describe('ArchiveComponent', () => {
+ let component: ArchiveComponent;
+ let fixture: ComponentFixture<ArchiveComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ ArchiveComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ArchiveComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_pages/archive/archive.component.ts b/frontend/src/app/_pages/archive/archive.component.ts
new file mode 100644
index 00000000..47f96218
--- /dev/null
+++ b/frontend/src/app/_pages/archive/archive.component.ts
@@ -0,0 +1,25 @@
+import { Component, OnInit } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+
+@Component({
+ selector: 'app-archive',
+ templateUrl: './archive.component.html',
+ styleUrls: ['./archive.component.css']
+})
+export class ArchiveComponent implements OnInit {
+
+ publicDatasets: Dataset[] = [];
+
+ constructor(private datasetsService: DatasetsService) { }
+
+ ngOnInit(): void {
+ this.datasetsService.getPublicDatasets().subscribe((datasets) => {
+ this.publicDatasets = datasets;
+ this.publicDatasets.forEach((element, index) => {
+ this.publicDatasets[index] = (<Dataset>element);
+ })
+ });
+ }
+
+}
diff --git a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.html b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.html
deleted file mode 100644
index fa38a1bc..00000000
--- a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.html
+++ /dev/null
@@ -1 +0,0 @@
-<p>browse-datasets works!</p>
diff --git a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.spec.ts b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.spec.ts
deleted file mode 100644
index fda74dbe..00000000
--- a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { BrowseDatasetsComponent } from './browse-datasets.component';
-
-describe('BrowseDatasetsComponent', () => {
- let component: BrowseDatasetsComponent;
- let fixture: ComponentFixture<BrowseDatasetsComponent>;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ BrowseDatasetsComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(BrowseDatasetsComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.ts b/frontend/src/app/_pages/browse-datasets/browse-datasets.component.ts
deleted file mode 100644
index dba6c25e..00000000
--- a/frontend/src/app/_pages/browse-datasets/browse-datasets.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-browse-datasets',
- templateUrl: './browse-datasets.component.html',
- styleUrls: ['./browse-datasets.component.css']
-})
-export class BrowseDatasetsComponent implements OnInit {
-
- constructor() { }
-
- ngOnInit(): void {
- }
-
-}
diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css
deleted file mode 100644
index b4ac9669..00000000
--- a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.css
+++ /dev/null
@@ -1,7 +0,0 @@
-#container {
- border-radius: 8px;
-}
-
-#wrapper {
- color: #003459;
-} \ No newline at end of file
diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html
deleted file mode 100644
index 27e06884..00000000
--- a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html
+++ /dev/null
@@ -1,40 +0,0 @@
-
-<div id="wrapper">
-
- <div id="container" class="container p-5" style="background-color: white; min-height: 100%;">
- <div class="row mt-3 mb-2 d-flex justify-content-center">
-
- <div class="col-sm-6" style="margin-bottom: 10px;">
- <p class="glyphicon glyphicon-search"></p>
- <input type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term">
-
- </div>
-
- <div class="row">
- <div class="col-sm-4" style="margin-bottom: 10px;" *ngFor="let predictor of publicPredictors | filter:term">
- <div class="card h-100">
- <div class="card-body">
- <h3 class="card-title"><b>{{predictor.name}}</b></h3>
- <p class="card-text">{{predictor.description}}</p>
- <a class="btn btn-primary" (click)="openPredictor(predictor._id)">Iskoristi</a>
- </div>
- <div class="card-footer text-muted">
- Kreirao: {{predictor.username}} <br>
- Datum kreiranja: {{predictor.dateCreated |date}}
- </div>
- </div>
- </div>
-
-
- </div>
- <div class="text-center"*ngIf="( publicPredictors != undefined && publicPredictors|filter:term).length === 0">
- <h2>Nema rezultata</h2>
- </div>
- </div>
-
- </div>
-
-
-
-
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.spec.ts b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.spec.ts
deleted file mode 100644
index 6d13fedf..00000000
--- a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { BrowsePredictorsComponent } from './browse-predictors.component';
-
-describe('BrowsePredictorsComponent', () => {
- let component: BrowsePredictorsComponent;
- let fixture: ComponentFixture<BrowsePredictorsComponent>;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ BrowsePredictorsComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(BrowsePredictorsComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts
deleted file mode 100644
index 891b3cab..00000000
--- a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { PredictorsService } from 'src/app/_services/predictors.service';
-import Predictor from 'src/app/_data/Predictor';
-import {Router} from '@angular/router'
-@Component({
- selector: 'app-browse-predictors',
- templateUrl: './browse-predictors.component.html',
- styleUrls: ['./browse-predictors.component.css']
-})
-export class BrowsePredictorsComponent implements OnInit {
-
- publicPredictors? :Predictor[];
- term: string="";
- constructor(private predictors: PredictorsService,private router:Router) {
- this.predictors.getPublicPredictors().subscribe((predictors) => {
- this.publicPredictors = predictors;
- });
- }
-
- ngOnInit(): void {
- }
- openPredictor(id:string):void{
- this.router.navigate(['predict/'+id]);
- };
-
-}
diff --git a/frontend/src/app/_pages/experiment/experiment.component.css b/frontend/src/app/_pages/experiment/experiment.component.css
new file mode 100644
index 00000000..36c35484
--- /dev/null
+++ b/frontend/src/app/_pages/experiment/experiment.component.css
@@ -0,0 +1,55 @@
+ul {
+ list-style: none;
+}
+
+.holder {
+ display: flex;
+ flex-direction: row;
+ align-items: stretch;
+}
+
+.sidenav {
+ width: 250px;
+ background-color: var(--ns-bg-dark-50);
+}
+
+@media only screen and (max-width: 400px) {
+ .sidenav {
+ width: 100%;
+ background-color: var(--ns-bg-dark-100);
+ }
+ .holder {
+ flex-direction: column;
+ }
+}
+
+mat-stepper {
+ background-color: transparent;
+}
+
+.label {
+ color: white;
+}
+
+.steps-container {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ overflow-y: auto;
+}
+
+.step-content {
+ position: relative;
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+}
+
+.step-content-inside {
+ width: 98%;
+ height: 98%;
+ overflow-y: auto;
+} \ 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
new file mode 100644
index 00000000..2b32db81
--- /dev/null
+++ b/frontend/src/app/_pages/experiment/experiment.component.html
@@ -0,0 +1,49 @@
+<div class="container-fluid p-0 text-offwhite holder" style="height: calc(100vh - 64px); text-align: center;">
+ <div class="d-flex flex-colum align-items-center sidenav">
+ <mat-stepper orientation="vertical" (selectionChange)="changePage($event)">
+ <mat-step>
+ <!--editable="false"-->
+ <ng-template matStepLabel><span class="label">Izvor podataka</span></ng-template>
+ <ng-template matStepContent>
+ <p>Izaberite vas izvor podataka</p>
+ </ng-template>
+ </mat-step>
+ <mat-step>
+ <ng-template matStepLabel> <span class="label">Odabir kolona</span></ng-template>
+ <ng-template matStepContent>
+ <p>Pripremite podatke i izaberite izlazne kolone</p>
+ </ng-template>
+ </mat-step>
+ <mat-step>
+ <ng-template matStepLabel><span class="label">Treniranje</span></ng-template>
+ <p>Odaberite parametre i trenirajte model</p>
+ </mat-step>
+ <mat-step>
+ <ng-template matStepLabel><span class="label">Pregled rezultata treniranja</span></ng-template>
+ <p>Pregledajte tok treniranja i grafički prikaz rezultata</p>
+ </mat-step>
+ </mat-stepper>
+ </div>
+ <div #stepsContainer class="steps-container">
+ <div #steps id="step_1" class="step-content">
+ <div class="step-content-inside">
+ <app-folder #folderDataset [type]="FolderType.Dataset" [forExperiment]="experiment" [startingTab]="TabType.NewFile" [tabsToShow]="[TabType.MyDatasets, TabType.PublicDatasets]" (okPressed)="goToPage(1)" (selectedFileChanged)="setDataset($event)"></app-folder>
+ </div>
+ </div>
+ <div #steps id="step_2" class="step-content">
+ <div class="step-content-inside">
+ <app-column-table (okPressed)="goToPage(2); experiment._columnsSelected = true;" (columnTableChanged)="columnTableChangedEvent()" [experiment]="experiment" [dataset]="dataset"></app-column-table>
+ </div>
+ </div>
+ <div #steps id="step_3" class="step-content">
+ <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>
+ </div>
+ <div #steps id="step_4" class="step-content">
+ <div class="step-content-inside">
+ <app-metric-view #metricView></app-metric-view>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/experiment/experiment.component.spec.ts b/frontend/src/app/_pages/experiment/experiment.component.spec.ts
index fd2bbd30..fd2bbd30 100644
--- a/frontend/src/app/experiment/experiment.component.spec.ts
+++ b/frontend/src/app/_pages/experiment/experiment.component.spec.ts
diff --git a/frontend/src/app/_pages/experiment/experiment.component.ts b/frontend/src/app/_pages/experiment/experiment.component.ts
new file mode 100644
index 00000000..1dc18a78
--- /dev/null
+++ b/frontend/src/app/_pages/experiment/experiment.component.ts
@@ -0,0 +1,164 @@
+import { AfterViewInit, Component, ElementRef, ViewChild, ViewChildren, Input } from '@angular/core';
+import { StepperSelectionEvent } from '@angular/cdk/stepper';
+import { MatStepper } from '@angular/material/stepper';
+import Shared from 'src/app/Shared';
+import { FolderFile, FolderType } from 'src/app/_data/FolderFile';
+import { FolderComponent, TabType } from 'src/app/_elements/folder/folder.component';
+import Experiment from 'src/app/_data/Experiment';
+import { ExperimentsService } from 'src/app/_services/experiments.service';
+import { ModelsService } from 'src/app/_services/models.service';
+import Model from 'src/app/_data/Model';
+import Dataset from 'src/app/_data/Dataset';
+import { ColumnTableComponent } from 'src/app/_elements/column-table/column-table.component';
+import { SignalRService } from 'src/app/_services/signal-r.service';
+import { MetricViewComponent } from 'src/app/_elements/metric-view/metric-view.component';
+
+@Component({
+ selector: 'app-experiment',
+ templateUrl: './experiment.component.html',
+ styleUrls: ['./experiment.component.css']
+})
+export class ExperimentComponent implements AfterViewInit {
+
+ @ViewChild(MatStepper) stepper!: MatStepper;
+ @ViewChild('stepsContainer') stepsContainer!: ElementRef;
+ @ViewChildren('steps') steps!: ElementRef[];
+
+ event: number = 0;
+ experiment: Experiment;
+ dataset?: Dataset;
+ @ViewChild("folderDataset") folderDataset!: FolderComponent;
+ @ViewChild(ColumnTableComponent) columnTable!: ColumnTableComponent;
+ @ViewChild("folderModel") folderModel!: FolderComponent;
+ @ViewChild("metricView") metricView!: MetricViewComponent;
+
+ constructor(private experimentsService: ExperimentsService, private modelsService: ModelsService, private signalRService: SignalRService) {
+ this.experiment = new Experiment("exp1");
+ }
+
+ /*updateExperiment(){
+
+ }*/
+
+ addNewExperiment() {
+ this.experimentsService.addExperiment(this.experiment).subscribe(() => { console.log("new Experiment") });
+ }
+
+ trainModel() {
+ if (!this.modelToTrain) {
+ 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") });
+ }
+ }
+
+ stepHeight = this.calcStepHeight();
+
+ calcStepHeight() {
+ return window.innerHeight - 64;
+ }
+
+ ngAfterViewInit(): void {
+ window.addEventListener('resize', () => {
+ this.updatePageHeight();
+ })
+ this.updatePageHeight();
+
+ setInterval(() => {
+ this.updatePageIfScrolled();
+ }, 100);
+
+ this.stepsContainer.nativeElement.addEventListener('scroll', (event: Event) => {
+ Shared.emitBGScrollEvent(this.stepsContainer.nativeElement.scrollTop);
+ });
+
+ if (this.signalRService.hubConnection) {
+ this.signalRService.hubConnection.on("NotifyEpoch", (mName: string, mId: string, stat: string, totalEpochs: number, currentEpoch: number) => {
+ console.log(this.modelToTrain?._id, mId);
+ if (currentEpoch == 0) {
+ this.history = [];
+ }
+ if (this.modelToTrain?._id == mId) {
+ stat = stat.replace(/'/g, '"');
+ //console.log('JSON', this.trainingResult);
+ this.history.push(JSON.parse(stat));
+ this.metricView.update(this.history);
+ }
+ });
+
+ }
+ }
+
+ history: any[] = [];
+
+ updatePageIfScrolled() {
+ if (this.scrolling) return;
+ const currentPage = Math.round(this.stepsContainer.nativeElement.scrollTop / this.stepHeight)
+ this.stepper.selectedIndex = currentPage;
+ }
+
+ updatePageHeight() {
+ this.stepHeight = this.calcStepHeight();
+ const stepHeight = `${this.stepHeight}px`;
+ this.stepsContainer.nativeElement.style.minHeight = stepHeight;
+ this.steps.forEach(step => {
+ step.nativeElement.style.minHeight = stepHeight;
+ })
+ }
+
+ changePage(event: StepperSelectionEvent) {
+ this.updatePage(<number>event.selectedIndex)
+ }
+
+ goToPage(pageNum: number) {
+ this.stepper.selectedIndex = pageNum;
+ this.updatePage(pageNum);
+ }
+
+ scrollTimeout: any;
+
+ updatePage(pageNum: number) {
+ this.scrolling = true;
+ this.event = pageNum;
+ let scrollAmount = 0;
+ this.steps.forEach((step, index) => {
+ if (index == pageNum) {
+ scrollAmount = step.nativeElement.offsetTop;
+ }
+ })
+ clearTimeout(this.scrollTimeout);
+ this.scrollTimeout = setTimeout(() => {
+ this.scrolling = false;
+ }, 800);
+ this.stepsContainer.nativeElement.scroll({
+ top: scrollAmount,
+ behavior: 'smooth' //auto, smooth, initial, inherit
+ });
+ }
+
+ scrolling: boolean = false;
+
+ FolderType = FolderType;
+
+ TabType = TabType;
+
+ columnTableChangedEvent() {
+ //sta se desi kad se nesto promeni u column-table komponenti...
+ //console.log("promenio se column-table");
+ }
+
+ setDataset(dataset: FolderFile) {
+ const d = <Dataset>dataset;
+ this.experiment.datasetId = d._id;
+ this.dataset = d;
+
+ this.columnTable.loadDataset(this.dataset);
+ }
+
+ modelToTrain?: Model;
+
+ setModel(model: FolderFile) {
+ const m = <Model>model;
+ this.modelToTrain = m;
+ }
+}
diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.html b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.html
deleted file mode 100644
index 84f5ebaf..00000000
--- a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.html
+++ /dev/null
@@ -1,38 +0,0 @@
-
-<div id="wrapper">
-
- <div id="container" class="container p-5" style="background-color: white; min-height: 100%;">
- <div class="row mt-3 mb-2 d-flex justify-content-center">
-
- <div class="col-sm-6" style="margin-bottom: 10px;">
- <input type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term">
- </div>
-
- <div class="row">
- <div class="col-sm-4" style="margin-bottom: 10px;" *ngFor="let dataset of publicDatasets | filter:term">
- <div class="card h-100">
- <div class="card-body">
- <h3 class="card-title"><b>{{dataset.name}}</b></h3>
- <p class="card-text">{{dataset.description}}</p>
- <a class="btn btn-primary" (click)="addDataset(dataset)">Dodaj dataset</a>
- </div>
- <div class="card-footer text-muted">
- Kreirao: {{dataset.username}} <br>
- Datum kreiranja: {{dataset.dateCreated |date}}
- </div>
- </div>
- </div>
-
-
- </div>
- <div class="text-center"*ngIf="( publicDatasets != undefined && publicDatasets|filter:term).length === 0">
- <h2>Nema rezultata</h2>
- </div>
- </div>
-
- </div>
-
-
-
-
-</div>
diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.spec.ts b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.spec.ts
deleted file mode 100644
index 6ab894fd..00000000
--- a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { FilterDatasetsComponent } from './filter-datasets.component';
-
-describe('FilterDatasetsComponent', () => {
- let component: FilterDatasetsComponent;
- let fixture: ComponentFixture<FilterDatasetsComponent>;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ FilterDatasetsComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(FilterDatasetsComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend/src/app/_pages/home/home.component.css b/frontend/src/app/_pages/home/home.component.css
index e69de29b..906f5728 100644
--- a/frontend/src/app/_pages/home/home.component.css
+++ b/frontend/src/app/_pages/home/home.component.css
@@ -0,0 +1,20 @@
+.logo {
+ margin: 0 !important;
+}
+
+#title {
+ color: var(--offwhite);
+}
+
+h1 {
+ font-size: 64px !important;
+ font-weight: 900 !important;
+ margin-top: 1rem;
+ margin-bottom: 2.5rem;
+}
+
+.card {
+ margin: 2.5rem !important;
+ padding: 1.5rem;
+ width: 26rem !important;
+} \ No newline at end of file
diff --git a/frontend/src/app/_pages/home/home.component.html b/frontend/src/app/_pages/home/home.component.html
index f5e94d27..2825b3bf 100644
--- a/frontend/src/app/_pages/home/home.component.html
+++ b/frontend/src/app/_pages/home/home.component.html
@@ -1,57 +1,33 @@
-<div class="d-flex flex-column align-items-center bg-light">
- <img src="../../../assets/svg/logo.svg" class="bi me-2" width="256" height="256" role="img">
- <div *ngIf="shared.loggedIn" class="d-flex flex-column align-items-center">
- <h2 class="my-4">Započnite sa treniranjem!</h2>
- <div id="cards" class="row align-items-stretch justify-content-center">
- <div class="card shadow col-3 m-1" style="width: 18rem;">
- <div class="card-body">
- <mat-icon width="48px" height="48px"
- style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">storage</mat-icon>
- <h3 class="card-title my-2">Moji izvori podataka</h3>
- <p class="card-text">
- <a class="stretched-link" routerLink="my-datasets">Preuredite</a> vaše izvore
- podataka, ili
- dodajte novi.
- </p>
- </div>
- </div>
- <div class="card shadow col-3 m-1" style="width: 18rem;">
- <div class="card-body">
- <mat-icon width="48px" height="48px"
- style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">model_training
- </mat-icon>
- <h3 class="card-title my-2">Moji modeli</h3>
- <p class="card-text">
- <a class="stretched-link" routerLink="my-models">Pregledajte</a> vaše modele, menjajte ih,
- napravite nove modele, ili
- ih obrišite.
- </p>
- </div>
+<div class="d-flex flex-column align-items-center">
+ <div class="logo">
+ <img src="../../../assets/images/logo.png" class="bi me-2" width="256" height="256" role="img">
+ </div>
+
+ <div id="title">
+ <h1>Igr<span class="highlight">ann</span>onica</h1>
+ </div>
+
+ <div id="cards" class="row align-items-view align-items-stretch justify-content-center">
+ <div class="card shadowed bg-light text-light col-3 m-3" style="width: 18rem;">
+ <div class="card-body">
+ <mat-icon width="48px" height="48px" style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">model_training</mat-icon>
+ <h3 class="card-title my-2">Experimentiši</h3>
+ <p class="card-text">
+ U tri koraka <a class="stretched-link" routerLink="experiment">napravite novu neuronsku mrežu</a>. Koristite postojeće izvore podataka, modele, itd.
+ </p>
</div>
- <div class="card shadow col-3 m-1" style="width: 18rem;">
- <div class="card-body">
- <mat-icon width="48px" height="48px"
- style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">batch_prediction
- </mat-icon>
- <h3 class="card-title my-2">Rezultati treniranja</h3>
- <p class="card-text">
- <a class="stretched-link" routerLink="my-predictors">Pregledajte</a> sve vaše trenirane
- modele,
- koristite ih da predvidite vrednosti za red ili skup podataka, ili ih obrišite.
- </p>
- </div>
+ </div>
+ <div class="card shadowed bg-light text-light col-3 m-3" style="width: 18rem;">
+ <div class="card-body">
+ <mat-icon width="48px" height="48px" style="font-size: 48px; margin-left: 50%; transform: translateX(-100%);">storage
+ </mat-icon>
+ <h3 class="card-title my-2">Arhiva</h3>
+ <p class="card-text">
+ <a class="stretched-link" routerLink="archive">Upravljajte</a> izvorima podataka, eksperimentima, modelima, i rezultatima treniranja. Pogledajte podatke koje su podelili drugi korisnici.
+ </p>
</div>
</div>
- </div>
- <h2 class="my-4">Pogledajte javne izvore podataka!</h2>
- <app-carousel [items]="publicDatasets" [type]="'Dataset'">
-
- </app-carousel>
- <h3><a routerLink="browse-datasets">Pogledaj sve javne izvore podataka...</a></h3>
- <h2 class="my-4">Iskoristite već trenirane modele!</h2>
- <app-carousel [items]="publicPredictors" [type]="'Predictor'">
- </app-carousel>
- <h3><a routerLink="browse-predictors">Pogledaj sve javne trenirane modele...</a></h3>
+ </div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/home/home.component.ts b/frontend/src/app/_pages/home/home.component.ts
index 0575c4c0..28ba2cbb 100644
--- a/frontend/src/app/_pages/home/home.component.ts
+++ b/frontend/src/app/_pages/home/home.component.ts
@@ -1,7 +1,6 @@
import { Component, OnInit } from '@angular/core';
import Dataset from 'src/app/_data/Dataset';
import Predictor from 'src/app/_data/Predictor';
-import { ItemDatasetComponent } from 'src/app/_elements/item-dataset/item-dataset.component';
import shared from 'src/app/Shared';
import { DatasetsService } from 'src/app/_services/datasets.service';
import { PredictorsService } from 'src/app/_services/predictors.service';
@@ -14,7 +13,6 @@ import { PredictorsService } from 'src/app/_services/predictors.service';
export class HomeComponent implements OnInit {
publicDatasets: Dataset[] = [];
- publicPredictors: Predictor[] = [];
shared = shared;
@@ -25,9 +23,6 @@ export class HomeComponent implements OnInit {
this.publicDatasets[index] = (<Dataset>element);
})
});
- this.predictorsService.getPublicPredictors().subscribe((predictors) => {
- this.publicPredictors = predictors;
- });
}
ngOnInit(): void {
diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.css b/frontend/src/app/_pages/my-datasets/my-datasets.component.css
deleted file mode 100644
index 57889937..00000000
--- a/frontend/src/app/_pages/my-datasets/my-datasets.component.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#header {
- background-color: #003459;
- padding-top: 20px;
- padding-bottom: 15px;
- text-align: center;
- color: white;
- border-radius: 5px;
-} \ No newline at end of file
diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.html b/frontend/src/app/_pages/my-datasets/my-datasets.component.html
deleted file mode 100644
index 0c83dc85..00000000
--- a/frontend/src/app/_pages/my-datasets/my-datasets.component.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<div id="header">
- <h1>Moji setovi podataka</h1>
-</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 dataset of myDatasets">
- <app-item-dataset [dataset]="dataset"></app-item-dataset>
-
- <div class="panel-footer row"><!-- panel-footer -->
- <div class="col-xs-6 text-center">
- <div>
- <div>
- <button (click)="deleteThisDataset(dataset)" mat-raised-button color="warn" style="min-width: 10rem;float: right" ><mat-icon>delete</mat-icon></button>
- </div>
-
- </div>
- </div>
- </div><!-- end panel-footer -->
- </div>
- </div>
- <div class="text-center" *ngIf="this.myDatasets.length == 0" >
- <h2>Nema rezultata</h2>
- </div>
- </div>
-
-
- </div>
-
-
-
-
-
- </div>
diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.spec.ts b/frontend/src/app/_pages/my-datasets/my-datasets.component.spec.ts
deleted file mode 100644
index fc1fc3f3..00000000
--- a/frontend/src/app/_pages/my-datasets/my-datasets.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { MyDatasetsComponent } from './my-datasets.component';
-
-describe('MyDatasetsComponent', () => {
- let component: MyDatasetsComponent;
- let fixture: ComponentFixture<MyDatasetsComponent>;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ MyDatasetsComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(MyDatasetsComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.ts b/frontend/src/app/_pages/my-datasets/my-datasets.component.ts
deleted file mode 100644
index 8857e371..00000000
--- a/frontend/src/app/_pages/my-datasets/my-datasets.component.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import {Router} from '@angular/router';
-import { DatasetsService } from 'src/app/_services/datasets.service';
-import Dataset from 'src/app/_data/Dataset';
-import { JwtHelperService } from '@auth0/angular-jwt';
-import { CookieService } from 'ngx-cookie-service';
-import shared from 'src/app/Shared';
-import { share } from 'rxjs';
-
-@Component({
- selector: 'app-my-datasets',
- templateUrl: './my-datasets.component.html',
- styleUrls: ['./my-datasets.component.css']
-})
-export class MyDatasetsComponent implements OnInit {
- myDatasets: Dataset[] = [];
-
- constructor(private datasetsS : DatasetsService) {
-
-
-
- }
-
- ngOnInit(): void {
-
- this.datasetsS.getMyDatasets().subscribe((response) => {
- this.myDatasets = response;
- }, (error) => {
- if (error.error == "Dataset with...") {
- shared.openDialog("Greska", "Niste dobro uneli nesto");
- }
- });
- }
-
-/*
- editModel(): void{
- this.modelsS.editModel().subscribe(m => {
- this.myModel = m;
-
- })
- }
-*/
-
-deleteThisDataset(dataset: Dataset): void{
- shared.openYesNoDialog('Brisanje seta podataka','Da li ste sigurni da želite da obrišete ovaj set podataka?',() => {
- this.datasetsS.deleteDataset(dataset).subscribe((response) => {
- this.getAllMyDatasets();
- }, (error) =>{
- if (error.error == "Dataset with name = {name} deleted") {
- shared.openDialog("Greška","Greška pri brisanju dataseta!");
- }
- });
- });
-}
-
- getAllMyDatasets(): void{
- this.datasetsS.getMyDatasets().subscribe(m => {
- this.myDatasets = m;
- });
- }
-
-
-}
diff --git a/frontend/src/app/_pages/my-models/my-models.component.css b/frontend/src/app/_pages/my-models/my-models.component.css
deleted file mode 100644
index 19d29595..00000000
--- a/frontend/src/app/_pages/my-models/my-models.component.css
+++ /dev/null
@@ -1,12 +0,0 @@
-button{
- margin-left: 5%;
- margin-right: 5%;
-}
-#header {
- background-color: #003459;
- padding-top: 20px;
- padding-bottom: 15px;
- text-align: center;
- color: white;
- border-radius: 5px;
-} \ No newline at end of file
diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.css b/frontend/src/app/_pages/my-predictors/my-predictors.component.css
deleted file mode 100644
index ccb9fb7b..00000000
--- a/frontend/src/app/_pages/my-predictors/my-predictors.component.css
+++ /dev/null
@@ -1,13 +0,0 @@
-#header {
- background-color: #003459;
- padding-top: 20px;
- padding-bottom: 15px;
- text-align: center;
- color: white;
- border-radius: 5px;
-}
-
-.row{
- margin-top: 10px;
- margin-bottom: 30px;
-} \ No newline at end of file
diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.html b/frontend/src/app/_pages/my-predictors/my-predictors.component.html
deleted file mode 100644
index 31fa786c..00000000
--- a/frontend/src/app/_pages/my-predictors/my-predictors.component.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<div id="header">
- <h1>Trenirani modeli</h1>
-</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.spec.ts b/frontend/src/app/_pages/my-predictors/my-predictors.component.spec.ts
deleted file mode 100644
index 37dddf6d..00000000
--- a/frontend/src/app/_pages/my-predictors/my-predictors.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { MyPredictorsComponent } from './my-predictors.component';
-
-describe('MyPredictorsComponent', () => {
- let component: MyPredictorsComponent;
- let fixture: ComponentFixture<MyPredictorsComponent>;
-
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [ MyPredictorsComponent ]
- })
- .compileComponents();
- });
-
- beforeEach(() => {
- fixture = TestBed.createComponent(MyPredictorsComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.ts b/frontend/src/app/_pages/my-predictors/my-predictors.component.ts
deleted file mode 100644
index 4dc5300d..00000000
--- a/frontend/src/app/_pages/my-predictors/my-predictors.component.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-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',
- styleUrls: ['./my-predictors.component.css']
-})
-export class MyPredictorsComponent implements OnInit {
- predictors: Predictor[] = [];
- constructor(private predictorsS : PredictorsService) {
- }
- ngOnInit(): void {
- this.predictorsS.getMyPredictors().subscribe((response) => {
- this.predictors = response;
- }, (error) => {
- if (error.error == "Predictor with...") {
- shared.openDialog("Greska", "Greska");
- }
- });
- }
-
- 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(p => {
- this.predictors = p;
- });
- }
-
-
-}
diff --git a/frontend/src/app/_pages/playground/playground.component.css b/frontend/src/app/_pages/playground/playground.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_pages/playground/playground.component.css
diff --git a/frontend/src/app/_pages/playground/playground.component.html b/frontend/src/app/_pages/playground/playground.component.html
new file mode 100644
index 00000000..1dd7e331
--- /dev/null
+++ b/frontend/src/app/_pages/playground/playground.component.html
@@ -0,0 +1,18 @@
+<div class="position-fixed d-flex flex-col align-items-center justify-content-center" style="top: 50%; left: 50%; transform: translateX(-50%);">
+ <div class="d-flex flex-row align-items-center justify-content-center mt-5">
+ <h2 class="text-light my-2">
+ Broj tačaka:
+ </h2>
+ <mat-slider class="mx-3" [(ngModel)]="backgroundFill" min="0" max="1" step="0.01" (input)="updateFillPref($event)">
+ </mat-slider>
+
+ </div>
+ <div class="d-flex flex-row align-items-center justify-content-center mt-5">
+ <h2 class="text-light my-2">
+ Animacija: </h2>
+ <mat-slide-toggle class="mx-3" [(ngModel)]="animateBackground" (change)="updateAnimPref()"></mat-slide-toggle>
+
+ </div>
+</div>
+<div style="height: 5000px;">
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-model/item-model.component.spec.ts b/frontend/src/app/_pages/playground/playground.component.spec.ts
index f696a160..bf66b27e 100644
--- a/frontend/src/app/_elements/item-model/item-model.component.spec.ts
+++ b/frontend/src/app/_pages/playground/playground.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemModelComponent } from './item-model.component';
+import { PlaygroundComponent } from './playground.component';
-describe('ItemModelComponent', () => {
- let component: ItemModelComponent;
- let fixture: ComponentFixture<ItemModelComponent>;
+describe('PlaygroundComponent', () => {
+ let component: PlaygroundComponent;
+ let fixture: ComponentFixture<PlaygroundComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemModelComponent ]
+ declarations: [ PlaygroundComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemModelComponent);
+ fixture = TestBed.createComponent(PlaygroundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_pages/playground/playground.component.ts b/frontend/src/app/_pages/playground/playground.component.ts
new file mode 100644
index 00000000..831132a4
--- /dev/null
+++ b/frontend/src/app/_pages/playground/playground.component.ts
@@ -0,0 +1,35 @@
+import { Component, OnInit } from '@angular/core';
+import { MatSliderChange } from '@angular/material/slider';
+import { CookieService } from 'ngx-cookie-service';
+
+@Component({
+ selector: 'app-playground',
+ templateUrl: './playground.component.html',
+ styleUrls: ['./playground.component.css']
+})
+export class PlaygroundComponent implements OnInit {
+
+ animateBackground = true;
+ backgroundFill = 1.0;
+
+ constructor(private cookie: CookieService) { }
+
+ updateFillPref(event: MatSliderChange) {
+ this.backgroundFill = event.value!;
+ this.cookie.set('backgroundFill', "" + this.backgroundFill);
+ }
+
+ updateAnimPref() {
+ this.cookie.set('animateBackground', "" + this.animateBackground);
+ }
+
+ ngOnInit(): void {
+ if (this.cookie.check('animateBackground')) {
+ this.animateBackground = this.cookie.get('animateBackground') == 'true';
+ }
+ if (this.cookie.check('backgroundFill')) {
+ this.backgroundFill = parseFloat(this.cookie.get('backgroundFill'));
+ }
+ }
+
+}
diff --git a/frontend/src/app/_pages/predict/predict.component.css b/frontend/src/app/_pages/predict/predict.component.css
deleted file mode 100644
index dab059a5..00000000
--- a/frontend/src/app/_pages/predict/predict.component.css
+++ /dev/null
@@ -1,3 +0,0 @@
-#wrapper {
- color: #003459;
-} \ No newline at end of file
diff --git a/frontend/src/app/_pages/predict/predict.component.html b/frontend/src/app/_pages/predict/predict.component.html
deleted file mode 100644
index 13afa8e4..00000000
--- a/frontend/src/app/_pages/predict/predict.component.html
+++ /dev/null
@@ -1,73 +0,0 @@
-
-<div id="wrapper">
- <br>
- <div id="container" class="container p-5" style="background-color: white; min-height: 100%;">
-
- <div id="header">
- <h1>Iskoristite prediktor</h1>
- </div>
-
- <br>
-
- <div class="form-group row mt-3 mb-2 d-flex justify-content-left">
- <!--justify-content-center-->
- <h2> Izabrani prediktor: </h2>
- <div class="col-10">
- <label for="output" class="col-sm-5 col-form-label">Naziv prediktora: <b>{{predictor.name}}</b></label>
- </div>
- <div>
- <label for="output" class="col-sm-5 col-form-label">Opis prediktora: <b>{{predictor.description}}</b></label>
- </div>
-
-
- </div>
- <br>
- <label for="type" class="form-check-label" ><b>Informacije o prediktoru</b></label>
- <div class="col-5 mt-2">
- <label for="type" class="form-check-label" >Prediktor {{predictor.isPublic?"je":"nije"}} javni.</label>
- </div>
- <div class="col-5 mt-2">
- <label for="type" class="form-check-label" >Prediktor {{predictor.accessibleByLink?"je":"nije"}} dostupan za deljenje.</label>
- </div>
- <br>
- <div class="col-2">
- <label for="dateCreated" class="col-form-label">Datum:</label>
- <input type="text" class="form-control-plaintext" id="dateCreated" placeholder="--/--/--"
- value="{{predictor.dateCreated | date: 'dd/MM/yyyy'}}" readonly>
- </div>
-
-
- <br>
- <div >
- <!--input -->
- <h3>Popunite ulazne kolone:</h3>
- <div id="divInputs" class="form-check mt-2">
- <div *ngIf="predictor" class="form-group row mt-3 mb-2 d-flex justify-content-left">
- <div *ngFor="let input of predictor.inputs; let i = index">
- <label for="{{input}}" class="col-sm-2 col-form-label"><b>{{input}}</b></label>
- <input name="{{input}}" type="text" [(ngModel)]="inputs[i].value" >
-
- </div>
-
- </div>
- </div>
-
- <br>
-
- </div>
- <div>
- <label for="output" class="col-sm-2 col-form-label">Izlaz: <b>{{predictor.output}}</b></label>
- </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)="usePredictor();">Iskoristi prediktor</button>
- <div class="col"></div>
-
- </div>
-
-
-
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/predict/predict.component.ts b/frontend/src/app/_pages/predict/predict.component.ts
deleted file mode 100644
index 39dec0ae..00000000
--- a/frontend/src/app/_pages/predict/predict.component.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
-import Predictor from 'src/app/_data/Predictor';
-import { PredictorsService } from 'src/app/_services/predictors.service';
-import shared from 'src/app/Shared';
-
-@Component({
- selector: 'app-predict',
- templateUrl: './predict.component.html',
- styleUrls: ['./predict.component.css']
-})
-export class PredictComponent implements OnInit {
-
- inputs : Column[] = [];
-
-
- predictor:Predictor;
- constructor(private predictS : PredictorsService, private route: ActivatedRoute) {
- this.predictor = new Predictor();
- }
-
- ngOnInit(): void {
- this.route.params.subscribe(url => {
- this.predictS.getPredictor(url["id"]).subscribe(p => {
-
- this.predictor = p;
- this.predictor.inputs.forEach((p,index)=> this.inputs[index] = new Column(p, ""));
- })
- });
- }
-
- usePredictor(): void{
- this.predictS.usePredictor(this.predictor, this.inputs).subscribe(p => {
- shared.openDialog("Obaveštenje", "Prediktor je uspešno poslat na probu."); //pisalo je "na treniranje" ??
- })
- }
-}
-
-
-export class Column {
- constructor(
- public name : string,
- public value : (number | string)){
- }
-} \ No newline at end of file
diff --git a/frontend/src/app/_pages/profile/profile.component.css b/frontend/src/app/_pages/profile/profile.component.css
index 5565d105..428870da 100644
--- a/frontend/src/app/_pages/profile/profile.component.css
+++ b/frontend/src/app/_pages/profile/profile.component.css
@@ -1,44 +1,21 @@
-body{margin-top:20px;
-background-color:#f2f6fc;
-color:#69707a;
+.card{
+ background-color: transparent;
+ color:var(--offwhite)
}
-.img-account-profile {
- height: 10rem;
- border: 1px solid lightgray;
-}
-.rounded-circle {
- border-radius: 50% !important;
-}
-.card .card-header {
- font-weight: 500;
-}
-.card-header:first-child {
- border-radius: 0.35rem 0.35rem 0 0;
+
+.card-header{
+ background-color: var(--ns-primary-50);
+ color:var(--offwhite)
}
-.card-header {
- padding: 1rem 1.35rem;
- margin-bottom: 0;
- background-color: rgba(33, 40, 50, 0.03);
- border-bottom: 1px solid rgba(33, 40, 50, 0.125);
+.card-body{
+ background-color: var(--ns-bg-dark-50);
}
-.form-control, .dataTable-input {
- display: block;
+
+mat-form-field{
width: 100%;
- padding: 0.875rem 1.125rem;
- font-size: 0.875rem;
- font-weight: 400;
- line-height: 1;
- color: #69707a;
- background-color: #fff;
- background-clip: padding-box;
- border: 1px solid #c5ccd6;
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- border-radius: 0.35rem;
- transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
-.selectedPicture {
- border: 2px solid darkgray;
+.danger-Text{
+ color:var(--ns-warn)
}
+
diff --git a/frontend/src/app/_pages/profile/profile.component.html b/frontend/src/app/_pages/profile/profile.component.html
index 557d69fd..37df4f14 100644
--- a/frontend/src/app/_pages/profile/profile.component.html
+++ b/frontend/src/app/_pages/profile/profile.component.html
@@ -27,17 +27,21 @@
<div class="row gx-3 mb-3">
<!-- Form Group (password)-->
<div class="col-md-6">
- <label class="small mb-1" for="inputPassword">Važeća lozinka</label>
- <input class="form-control" id="inputPassword" name="inputPassword" type="password" [(ngModel)]="this.oldPass" placeholder="Trenutna lozinka">
- <small *ngIf="wrongPassBool" class="form-text text-danger">Neispravna lozinka.</small>
- <small *ngIf="wrongOldPassBool" class="form-text text-danger">Pogrešan format.</small>
+ <small *ngIf="wrongPassBool" class="form-text danger-Text">Neispravna lozinka.</small>
+ <mat-form-field appearance="fill">
+ <mat-label>Važeća lozinka</mat-label>
+ <input matInput id="inputPassword" name="inputPassword" type="password" placeholder="" [(ngModel)]="this.oldPass">
+ </mat-form-field>
+ <small *ngIf="wrongOldPassBool" class="form-text danger-Text">Pogrešan format.</small>
</div>
<!-- Form Group (new password)-->
<div class="col-md-6">
- <label class="small mb-1" for="inputNewPassword">Nova lozinka</label>
- <input class="form-control" id="inputNewPassword" name="inputNewPassword" type="password" [(ngModel)]="this.newPass1" placeholder="Ukucaj novu lozinku">
- <small *ngIf="wrongNewPassBool" class="form-text text-danger">Lozinke se ne podudaraju.</small>
- <small *ngIf="wrongNewPass1Bool" class="form-text text-danger">Pogrešan format.</small>
+ <mat-form-field appearance="fill">
+ <mat-label>Nova lozinka</mat-label>
+ <input matInput id="inputNewPassword" name="inputNewPassword" type="password" placeholder="" [(ngModel)]="this.newPass1">
+ </mat-form-field>
+ <small *ngIf="wrongNewPassBool" class="form-text danger-Text">Lozinke se ne podudaraju.</small>
+ <small *ngIf="wrongNewPass1Bool" class="form-text danger-Text">Pogrešan format.</small>
</div>
</div>
@@ -46,15 +50,17 @@
<div class="col-md-6">
<div class="col text-center">
<!-- Save changes button-->
- <button class="btn btn-primary text-center mt-4" type="button" (click)="savePasswordChanges()">Promeni lozinku</button>
+ <button mat-raised-button color="primary" (click)="savePasswordChanges()">Promeni lozinku</button>
</div>
</div>
<!-- Form Group (new password again)-->
<div class="col-md-6">
- <label class="small mb-1" for="inputNewPasswordAgain">Ponovo nova lozinka</label>
- <input class="form-control" id="inputNewPasswordAgain" name="inputNewPasswordAgain" type="password" [(ngModel)]="this.newPass2" placeholder="Ukucaj novu lozinku">
- <small *ngIf="wrongNewPassBool" class="form-text text-danger">Lozinke se ne podudaraju.</small>
- <small *ngIf="wrongNewPass2Bool" class="form-text text-danger">Pogrešan format.</small>
+ <mat-form-field appearance="fill">
+ <mat-label>Ponovo nova lozinka</mat-label>
+ <input matInput id="inputNewPasswordAgain" name="inputNewPasswordAgain" placeholder="" type="password" [(ngModel)]="this.newPass2">
+ </mat-form-field>
+ <small *ngIf="wrongNewPassBool" class="form-text danger-Text">Lozinke se ne podudaraju.</small>
+ <small *ngIf="wrongNewPass2Bool" class="form-text danger-Text">Pogrešan format.</small>
</div>
</div>
</div>
@@ -74,15 +80,19 @@
<div class="row gx-3 mb-3">
<!-- Form Group (username)-->
<div class="col-md-6">
- <label class="small mb-1" for="inputUsername">Korisničko ime (kako će ostali korisnici videti tvoje ime)</label>
- <input class="form-control" id="inputUsername" name="inputUsername" type="text" [(ngModel)]="this.username">
- <small *ngIf="wrongUsernameBool" class="form-text text-danger">Pogrešan format.</small>
+ <mat-form-field appearance="fill">
+ <mat-label>Korisničko ime (kako će ostali korisnici videti tvoje ime)</mat-label>
+ <input matInput id="inputUsername" name="inputUsername" type="text" [(ngModel)]="this.username">
+ </mat-form-field>
+ <small *ngIf="wrongUsernameBool" class="form-text danger-Text">Pogrešan format.</small>
</div>
<!-- Form Group (email address)-->
<div class="col-md-6">
- <label class="small mb-1" for="inputEmailAddress">Email adresa</label>
- <input class="form-control" id="inputEmailAddress" name="inputEmailAddress" type="email" [(ngModel)]="this.email">
- <small *ngIf="wrongEmailBool" class="form-text text-danger">Pogrešan format.</small>
+ <mat-form-field appearance="fill">
+ <mat-label>Email adresa</mat-label>
+ <input matInput id="inputEmailAddress" name="inputEmailAddress" type="email" [(ngModel)]="this.email">
+ </mat-form-field>
+ <small *ngIf="wrongEmailBool" class="form-text danger-Text">Pogrešan format.</small>
</div>
</div>
@@ -90,15 +100,19 @@
<div class="row gx-3 mb-3">
<!-- Form Group (first name)-->
<div class="col-md-6">
- <label class="small mb-1" for="inputFirstName">Ime</label>
- <input class="form-control" id="inputFirstName" name="inputFirstName" type="text" [(ngModel)]="this.firstName">
- <small *ngIf="wrongFirstNameBool" class="form-text text-danger">Pogrešan format.</small>
+ <mat-form-field appearance="fill">
+ <mat-label>Ime</mat-label>
+ <input matInput id="inputFirstName" name="inputFirstName" type="text" [(ngModel)]="this.firstName">
+ </mat-form-field>
+ <small *ngIf="wrongFirstNameBool" class="form-text danger-Text">Pogrešan format.</small>
</div>
<!-- Form Group (last name)-->
<div class="col-md-6">
- <label class="small mb-1" for="inputLastName">Prezime</label>
- <input class="form-control" id="inputLastName" name="inputLastName" type="text" [(ngModel)]="this.lastName">
- <small *ngIf="wrongLastNameBool" class="form-text text-danger">Pogrešan format.</small>
+ <mat-form-field appearance="fill">
+ <mat-label>Prezime</mat-label>
+ <input matInput id="inputLastName" name="inputLastName" type="text" [(ngModel)]="this.lastName">
+ </mat-form-field>
+ <small *ngIf="wrongLastNameBool" class="form-text danger-Text">Pogrešan format.</small>
</div>
</div>
@@ -121,7 +135,7 @@
<div class="row mt-5">
<div class="col text-center">
<!-- Save changes button-->
- <button class="btn btn-primary text-center" type="button" (click)="saveInfoChanges()">Sačuvaj izmene</button>
+ <button mat-raised-button color="primary" (click)="saveInfoChanges()" >Sačuvaj izmene</button>
</div>
</div>
</form>
diff --git a/frontend/src/app/_pages/test/test.component.css b/frontend/src/app/_pages/test/test.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_pages/test/test.component.css
diff --git a/frontend/src/app/_pages/test/test.component.html b/frontend/src/app/_pages/test/test.component.html
new file mode 100644
index 00000000..94679055
--- /dev/null
+++ b/frontend/src/app/_pages/test/test.component.html
@@ -0,0 +1,5 @@
+<app-pie-chart></app-pie-chart>
+<app-doughnut-chart></app-doughnut-chart>
+<app-barchart></app-barchart>
+<app-box-plot></app-box-plot>
+<app-heatmap></app-heatmap> \ No newline at end of file
diff --git a/frontend/src/app/_pages/test/test.component.spec.ts b/frontend/src/app/_pages/test/test.component.spec.ts
new file mode 100644
index 00000000..e0f9bcc9
--- /dev/null
+++ b/frontend/src/app/_pages/test/test.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TestComponent } from './test.component';
+
+describe('TestComponent', () => {
+ let component: TestComponent;
+ let fixture: ComponentFixture<TestComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ TestComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TestComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_pages/test/test.component.ts b/frontend/src/app/_pages/test/test.component.ts
new file mode 100644
index 00000000..b3c0d8cf
--- /dev/null
+++ b/frontend/src/app/_pages/test/test.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-test',
+ templateUrl: './test.component.html',
+ styleUrls: ['./test.component.css']
+})
+export class TestComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(): void {
+ }
+
+}
diff --git a/frontend/src/app/_services/auth.service.ts b/frontend/src/app/_services/auth.service.ts
index 9e3f9f2f..cc5ad688 100644
--- a/frontend/src/app/_services/auth.service.ts
+++ b/frontend/src/app/_services/auth.service.ts
@@ -3,7 +3,7 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
import { JwtHelperService } from '@auth0/angular-jwt';
import { CookieService } from 'ngx-cookie-service';
import shared from '../Shared';
-import { Configuration } from '../configuration.service';
+import { Configuration } from './configuration.service';
const jwtHelper = new JwtHelperService();
@@ -12,9 +12,8 @@ const jwtHelper = new JwtHelperService();
})
export class AuthService {
- public loggedInEvent: EventEmitter<boolean> = new EventEmitter();
-
shared = shared;
+ public loggedInEvent: EventEmitter<boolean> = new EventEmitter();
constructor(private http: HttpClient, private cookie: CookieService) { }
@@ -23,7 +22,7 @@ export class AuthService {
}
register(user: any) {
- return this.http.post(`${Configuration.settings.apiURL}/auth/register`, { ...user }, { responseType: 'text' });
+ return this.http.post(`${Configuration.settings.apiURL}/auth/register`, { ...user },{ headers: this.authHeader() , responseType: 'text' });
}
getGuestToken() {
@@ -53,26 +52,19 @@ export class AuthService {
}
var property = jwtHelper.decodeToken(this.cookie.get('token'));
var username = property['name'];
- if (username != "") {
this.refresher = setTimeout(() => {
this.http.post(`${Configuration.settings.apiURL}/auth/renewJwt`, {}, { headers: this.authHeader(), responseType: 'text' }).subscribe((response) => {
this.authenticate(response);
});
}, exp.getTime() - new Date().getTime() - 60000);
- }
- else {
- this.refresher = setTimeout(() => {
- this.getGuestToken().subscribe((response) => {
- this.authenticate(response);
- });
- }, exp.getTime() - new Date().getTime() - 60000);
- }
+
}
addGuestToken() {
this.getGuestToken().subscribe((token) => {
this.authenticate(token);
+ location.reload();
});
}
@@ -83,7 +75,6 @@ export class AuthService {
}
this.cookie.set('token', token, exp);
this.updateUser();
- this.loggedInEvent.emit(true);
}
updateUser() {
@@ -108,4 +99,15 @@ export class AuthService {
authHeader() {
return new HttpHeaders().set("Authorization", "Bearer " + this.cookie.get('token'));
}
+ alreadyGuest(){
+ if(this.cookie.check('token')){
+ const token = this.cookie.get('token');
+ const decodedToken = jwtHelper.decodeToken(token);
+ if(decodedToken.role=="Guest")
+ return true;
+ }
+ return false;
+ }
+
+
}
diff --git a/frontend/src/app/configuration.service.spec.ts b/frontend/src/app/_services/configuration.service.spec.ts
index ec51dfa5..4b9322b5 100644
--- a/frontend/src/app/configuration.service.spec.ts
+++ b/frontend/src/app/_services/configuration.service.spec.ts
@@ -1,6 +1,6 @@
import { TestBed } from '@angular/core/testing';
-import { ConfigurationService } from './configuration.service';
+import { Configuration as ConfigurationService } from './configuration.service';
describe('ConfigurationService', () => {
let service: ConfigurationService;
diff --git a/frontend/src/app/configuration.service.ts b/frontend/src/app/_services/configuration.service.ts
index 4d2b0987..cda1091a 100644
--- a/frontend/src/app/configuration.service.ts
+++ b/frontend/src/app/_services/configuration.service.ts
@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
-import { IConfig } from '../app/_data/IConfig'
+import { IConfig } from '../_data/IConfig'
@Injectable()
export class Configuration {
diff --git a/frontend/src/app/_services/csv-parse.service.ts b/frontend/src/app/_services/csv-parse.service.ts
index 4a05535a..aae10193 100644
--- a/frontend/src/app/_services/csv-parse.service.ts
+++ b/frontend/src/app/_services/csv-parse.service.ts
@@ -1,4 +1,10 @@
import { Injectable } from "@angular/core";
+import * as FileSaver from 'file-saver';
+import * as XLSX from 'xlsx';
+
+const EXCEL_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8';
+const EXCEL_EXTENSION = '.xlsx';
+
@Injectable({ providedIn: 'root' })
export class CsvParseService {
@@ -47,10 +53,42 @@ export class CsvParseService {
if (strMatchedValue.length > 0)
arrData[arrData.length - 1].push(strMatchedValue);
- else
+ else
arrData[arrData.length - 1].push(null);
}
return (arrData);
}
+
+ ConvertJSONToCSV(objArray: string, headerList: { [x: string]: any; }) {
+ let array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
+ let str = '';
+ let row = 'S.No,';
+ for (let index in headerList) {
+ row += headerList[index] + ',';
+ }
+ row = row.slice(0, -1);
+ str += row + '\r\n';
+ for (let i = 0; i < array.length; i++) {
+ let line = (i + 1) + '';
+ for (let index in headerList) {
+ let head = headerList[index];
+ line += ',' + array[i][head];
+ }
+ str += line + '\r\n';
+ }
+ return str;
+ }
+
+ public exportAsExcelFile(json: any[], excelFileName: string): void {
+ const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json);
+ const workbook: XLSX.WorkBook = { Sheets: { 'data': worksheet }, SheetNames: ['data'] };
+ const excelBuffer: any = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
+ this.saveAsExcelFile(excelBuffer, excelFileName);
+ }
+
+ private saveAsExcelFile(buffer: any, fileName: string): void {
+ const data: Blob = new Blob([buffer], { type: EXCEL_TYPE });
+ FileSaver.saveAs(data, fileName + '_export_' + new Date().getTime() + EXCEL_EXTENSION);
+ }
} \ No newline at end of file
diff --git a/frontend/src/app/_services/datasets.service.ts b/frontend/src/app/_services/datasets.service.ts
index b2272f0a..2211996f 100644
--- a/frontend/src/app/_services/datasets.service.ts
+++ b/frontend/src/app/_services/datasets.service.ts
@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
-import { Configuration } from '../configuration.service';
+import { Configuration } from './configuration.service';
import Dataset from '../_data/Dataset';
import { AuthService } from './auth.service';
@@ -25,7 +25,10 @@ export class DatasetsService {
}
getDatasetFile(fileId: any): any {
- return this.http.get(`${Configuration.settings.apiURL}/file/csvRead/true/${fileId}`, { headers: this.authService.authHeader(), responseType: 'text' });
+ return this.http.get(`${Configuration.settings.apiURL}/file/csvRead/${fileId}/-1/11`, { headers: this.authService.authHeader(), responseType: 'text' });
+ }
+ getDatasetFilePartial(fileId: any, startRow: number, rowNum: number): Observable<any> {
+ return this.http.get(`${Configuration.settings.apiURL}/file/csvRead/${fileId}/${startRow}/${rowNum}`, { headers: this.authService.authHeader(), responseType: 'text' });
}
editDataset(dataset: Dataset): Observable<Dataset> {
diff --git a/frontend/src/app/_services/experiments.service.ts b/frontend/src/app/_services/experiments.service.ts
index 0d0d372b..29569fca 100644
--- a/frontend/src/app/_services/experiments.service.ts
+++ b/frontend/src/app/_services/experiments.service.ts
@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
-import { Configuration } from '../configuration.service';
+import { Configuration } from './configuration.service';
import Experiment from '../_data/Experiment';
import { AuthService } from './auth.service';
@@ -19,4 +19,8 @@ export class ExperimentsService {
getMyExperiments(): Observable<Experiment[]> {
return this.http.get<Experiment[]>(`${Configuration.settings.apiURL}/experiment/getmyexperiments`, { headers: this.authService.authHeader() });
}
+
+ updateExperiment(experiment: Experiment): Observable<Experiment> {
+ return this.http.put<Experiment>(`${Configuration.settings.apiURL}/experiment/` + experiment._id, experiment, { headers: this.authService.authHeader() });
+ }
}
diff --git a/frontend/src/app/_services/models.service.ts b/frontend/src/app/_services/models.service.ts
index 1130b12c..fc888556 100644
--- a/frontend/src/app/_services/models.service.ts
+++ b/frontend/src/app/_services/models.service.ts
@@ -1,10 +1,10 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
-import Model, { ProblemType } from '../_data/Model';
+import Model from '../_data/Model';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs';
import Dataset from '../_data/Dataset';
-import { Configuration } from '../configuration.service';
+import { Configuration } from '../_services/configuration.service';
@Injectable({
providedIn: 'root'
@@ -31,16 +31,20 @@ export class ModelsService {
addModel(model: Model): Observable<any> {
return this.http.post(`${Configuration.settings.apiURL}/model/add`, model, { headers: this.authService.authHeader() });
}
+ addDataset(dataset: Dataset): Observable<any> {
+ return this.http.post(`${Configuration.settings.apiURL}/dataset/add`, dataset, { headers: this.authService.authHeader() });
+ }
trainModel(modelId: string, experimentId: string): Observable<any> {
return this.http.post(`${Configuration.settings.apiURL}/model/trainmodel`, { ModelId: modelId, ExperimentId: experimentId }, { headers: this.authService.authHeader(), responseType: 'text' });
}
+ getMyDatasets(): Observable<Dataset[]> {
+ return this.http.get<Dataset[]>(`${Configuration.settings.apiURL}/dataset/mydatasets`, { headers: this.authService.authHeader() });
+ }
+
getMyModels(): Observable<Model[]> {
return this.http.get<Model[]>(`${Configuration.settings.apiURL}/model/mymodels`, { headers: this.authService.authHeader() });
}
- getMyModelsByType(problemType: ProblemType): Observable<Model[]> {
- return this.http.get<Model[]>(`${Configuration.settings.apiURL}/model/mymodelsbytype/` + problemType, { headers: this.authService.authHeader() });
- }
editModel(model: Model): Observable<Model> {
return this.http.put<Model>(`${Configuration.settings.apiURL}/model/`, model, { headers: this.authService.authHeader() });
@@ -49,5 +53,9 @@ export class ModelsService {
deleteModel(model: Model) {
return this.http.delete(`${Configuration.settings.apiURL}/model/` + model.name, { headers: this.authService.authHeader(), responseType: "text" });
}
-
+
+ getPublicModels(): Observable<Model[]> {
+ return this.http.get<Model[]>(`${Configuration.settings.apiURL}/model/publicmodels`, { headers: this.authService.authHeader() });
+ }
+
}
diff --git a/frontend/src/app/_services/predictors.service.ts b/frontend/src/app/_services/predictors.service.ts
index a24ee708..e2033e3e 100644
--- a/frontend/src/app/_services/predictors.service.ts
+++ b/frontend/src/app/_services/predictors.service.ts
@@ -1,9 +1,8 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
-import { Configuration } from '../configuration.service';
+import { Configuration } from './configuration.service';
import Predictor from '../_data/Predictor';
-import { Column } from '../_pages/predict/predict.component';
import { AuthService } from './auth.service';
@Injectable({
@@ -31,3 +30,10 @@ export class PredictorsService {
return this.http.get<Predictor[]>(`${Configuration.settings.apiURL}/predictor/mypredictors`, { headers: this.authService.authHeader() });
}
}
+
+export class Column {
+ constructor(
+ public name: string,
+ public value: (number | string)) {
+ }
+} \ No newline at end of file
diff --git a/frontend/src/app/_services/signal-r.service.ts b/frontend/src/app/_services/signal-r.service.ts
index 6a2e61e9..234636fe 100644
--- a/frontend/src/app/_services/signal-r.service.ts
+++ b/frontend/src/app/_services/signal-r.service.ts
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import * as signalR from "@microsoft/signalr";
import { CookieService } from 'ngx-cookie-service';
-import { Configuration } from '../configuration.service';
+import { Configuration } from './configuration.service';
@Injectable({
providedIn: 'root'
})
@@ -16,18 +16,13 @@ export class SignalRService {
}).build();
this.hubConnection.on("Notify", (message: string) => {
- //console.log(" " + message);
+ console.log(" " + message);
});
this.hubConnection
.start()
- .then(() => {})
- .catch(err => {})
+ .then(() => console.log("con Started"))
+ .catch(err => console.log("Error" + err))
}
-
- public stopConnection = () => {
- this.hubConnection?.stop();
- }
-
constructor(private cookie: CookieService) { }
}
diff --git a/frontend/src/app/_services/user-info.service.ts b/frontend/src/app/_services/user-info.service.ts
index 5d3394f6..8efeb903 100644
--- a/frontend/src/app/_services/user-info.service.ts
+++ b/frontend/src/app/_services/user-info.service.ts
@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
-import { Configuration } from '../configuration.service';
+import { Configuration } from './configuration.service';
import User from '../_data/User';
import { AuthService } from './auth.service';
diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts
index a0952d21..f5f1ccae 100644
--- a/frontend/src/app/app-routing.module.ts
+++ b/frontend/src/app/app-routing.module.ts
@@ -3,33 +3,21 @@ import { RouterModule, Routes } from '@angular/router';
import { AuthGuardService } from './_services/auth-guard.service';
import { HomeComponent } from './_pages/home/home.component';
-import { MyDatasetsComponent } from './_pages/my-datasets/my-datasets.component';
-import { MyModelsComponent } from './_pages/my-models/my-models.component';
-import { MyPredictorsComponent } from './_pages/my-predictors/my-predictors.component';
-import { BrowsePredictorsComponent } from './_pages/browse-predictors/browse-predictors.component';
-import { BrowseDatasetsComponent } from './_pages/browse-datasets/browse-datasets.component';
-import { SettingsComponent } from './_pages/settings/settings.component';
import { ProfileComponent } from './_pages/profile/profile.component';
-import { PredictComponent } from './_pages/predict/predict.component';
-import { FilterDatasetsComponent } from './_pages/filter-datasets/filter-datasets.component';
-import { ExperimentComponent } from './experiment/experiment.component';
-import { TrainingComponent } from './training/training.component';
+import { PlaygroundComponent } from './_pages/playground/playground.component';
+import { ExperimentComponent } from './_pages/experiment/experiment.component';
+import { ArchiveComponent } from './_pages/archive/archive.component';
+import { ColumnTableComponent } from './_elements/column-table/column-table.component';
+import { TestComponent } from './_pages/test/test.component';
const routes: Routes = [
{ path: '', component: HomeComponent, data: { title: 'Početna strana' } },
- /*{ path: 'add-model', component: AddModelComponent, data: { title: 'Dodaj model' } },*/
- { path: 'experiment', component: ExperimentComponent, data: { title: 'Dodaj eksperiment' } },
- { path: 'training/:id', component: TrainingComponent, data: { title: 'Treniraj model' } },
- { path: 'training', 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' } },
- { path: 'settings', component: SettingsComponent, canActivate: [AuthGuardService], data: { title: 'Podešavanja' } },
+ { path: 'experiment', component: ExperimentComponent, data: { title: 'Eksperiment' } },
+ { path: 'archive', component: ArchiveComponent, data: { title: 'Arhiva' } },
{ path: 'profile', component: ProfileComponent, canActivate: [AuthGuardService], data: { title: 'Profil' } },
- { 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' } },
-
+ { path: 'playground', component: PlaygroundComponent, data: { title: 'Zabava' } },
+ { path: 'sonja', component: ColumnTableComponent },
+ { path: 'test', component: TestComponent, data: { title: 'Test' } }
];
@NgModule({
diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html
index daf93cc1..d15793e7 100644
--- a/frontend/src/app/app.component.html
+++ b/frontend/src/app/app.component.html
@@ -1,10 +1,13 @@
-<app-reactive-background [bgColor]="'#003459'" [lineColor]="'#00a8e8'" [pointColor]="'#cfeffb'"
- [cursorLineColor]="'#888888'" [numPoints]="300">
+<app-gradient-background colorHorizontal1="rgba(0, 8, 45, 0.5)" colorHorizontal2="rgba(0, 52, 89, 0.5)" colorVertical1="rgba(0, 52, 89, 0.5)" colorVertical2="rgba(0, 152, 189, 0.5)"></app-gradient-background>
+<app-reactive-background [speed]="0.0005" [scrollSpeed]="0.25" [minDistance]="0.1" [maxSize]="3" [cursorDistance]="0.17" lineColor="#00a8e8" pointColor="rgba(0, 188, 252, 0.33)" cursorLineColor="#00a8e8" [numPoints]="300">
+</app-reactive-background>
+<app-reactive-background [speed]="0.0008" [scrollSpeed]="0.5" [minDistance]="0.12" [maxSize]="4" [cursorDistance]="0.19" lineColor="#00a8e8" pointColor="rgba(0, 188, 252, 0.66)" cursorLineColor="#00a8e8" [numPoints]="200">
+</app-reactive-background>
+<app-reactive-background [speed]="0.001" [scrollSpeed]="1" [minDistance]="0.14" [maxSize]="5" [cursorDistance]="0.22" lineColor="#00a8e8" pointColor="rgba(0, 188, 252, 1)" cursorLineColor="#00a8e8" [numPoints]="100">
</app-reactive-background>
<app-navbar></app-navbar>
-<div class="container h-100">
- <router-outlet></router-outlet>
- <!--<app-barchart></app-barchart>
- <app-scatterchart></app-scatterchart>-->
-</div>
+<a class="bg-controls" style="z-index: 1000;" routerLink="playground">
+ <mat-icon color="accent">settings_suggest</mat-icon>
+</a>
+<router-outlet></router-outlet>
<app-notifications></app-notifications> \ No newline at end of file
diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts
index 54c18bec..e301b46f 100644
--- a/frontend/src/app/app.component.ts
+++ b/frontend/src/app/app.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { AfterViewInit, Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { Router, NavigationEnd, ActivatedRoute } from '@angular/router';
import { filter, map } from 'rxjs';
@@ -11,9 +11,12 @@ import Shared from './Shared';
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
-export class AppComponent implements OnInit {
+export class AppComponent implements OnInit, AfterViewInit {
+ constructor(private router: Router, private titleService: Title, private authService: AuthService, private signalRService: SignalRService, private http: HttpClient) {
- constructor(private router: Router, private titleService: Title,private authService:AuthService,private signalRService:SignalRService,private http:HttpClient) { }
+ }
+ ngAfterViewInit(): void {
+ }
ngOnInit() {
this.router.events
@@ -36,22 +39,11 @@ export class AppComponent implements OnInit {
this.titleService.setTitle(`${title} - Igrannonica`);
}
});
- if(!this.authService.isAuthenticated())
- {
+ if (!this.authService.isAuthenticated()) {
+ if(!this.authService.alreadyGuest())
this.authService.addGuestToken();
- }
- this.signalRService.startConnection();
- //this.startHttpRequest();
-
-
-
-
- }
- private startHttpRequest = () => {
- this.http.get('http://localhost:5283/chatHub')
- .subscribe(res => {
- })
+ }
+ this.signalRService.startConnection();
+ //this.startHttpRequest();
}
-
-
}
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index 51374bd4..d44bf6ad 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -8,97 +8,90 @@ import { MatIconModule } from '@angular/material/icon';
import { NgMultiSelectDropDownModule } from 'ng-multiselect-dropdown';
import { NgChartsModule } from 'ng2-charts';
import { Ng2SearchPipeModule } from 'ng2-search-filter';
-import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
-import { DatasetLoadComponent } from './_elements/dataset-load/dataset-load.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-import { LoginModalComponent } from './_modals/login-modal/login-modal.component';
import { ReactiveFormsModule } from '@angular/forms';
-import { RegisterModalComponent } from './_modals/register-modal/register-modal.component';
-
+import { AppComponent } from './app.component';
+// Modules and modals
+import { Configuration } from './_services/configuration.service';
import { MaterialModule } from './material.module';
+import { LoginModalComponent } from './_modals/login-modal/login-modal.component';
+import { RegisterModalComponent } from './_modals/register-modal/register-modal.component';
+import { AlertDialogComponent } from './_modals/alert-dialog/alert-dialog.component';
+import { YesNoDialogComponent } from './_modals/yes-no-dialog/yes-no-dialog.component';
+import { EncodingDialogComponent } from './_modals/encoding-dialog/encoding-dialog.component';
+import { MissingvaluesDialogComponent } from './_modals/missingvalues-dialog/missingvalues-dialog.component';
+import { SaveExperimentDialogComponent } from './_modals/save-experiment-dialog/save-experiment-dialog.component';
+// Pages
import { HomeComponent } from './_pages/home/home.component';
-import { NavbarComponent } from './_elements/navbar/navbar.component';
-import { ItemPredictorComponent } from './_elements/item-predictor/item-predictor.component';
-import { ItemDatasetComponent } from './_elements/item-dataset/item-dataset.component';
-import { CarouselComponent } from './_elements/carousel/carousel.component';
-import { SettingsComponent } from './_pages/settings/settings.component';
import { ProfileComponent } from './_pages/profile/profile.component';
-import { MyPredictorsComponent } from './_pages/my-predictors/my-predictors.component';
-import { MyDatasetsComponent } from './_pages/my-datasets/my-datasets.component';
-import { MyModelsComponent } from './_pages/my-models/my-models.component';
-import { BrowseDatasetsComponent } from './_pages/browse-datasets/browse-datasets.component';
-import { BrowsePredictorsComponent } from './_pages/browse-predictors/browse-predictors.component';
-import { PredictComponent } from './_pages/predict/predict.component';
-import { ScatterchartComponent } from './scatterchart/scatterchart.component';
-import { BarchartComponent } from './barchart/barchart.component';
+import { ExperimentComponent } from './_pages/experiment/experiment.component';
+import { PlaygroundComponent } from './_pages/playground/playground.component';
+import { ArchiveComponent } from './_pages/archive/archive.component';
+// Charts
+import { ScatterchartComponent } from './_elements/_charts/scatterchart/scatterchart.component';
+import { BarchartComponent } from './_elements/_charts/barchart/barchart.component';
+import { PieChartComponent } from './_elements/_charts/pie-chart/pie-chart.component';
+import { BoxPlotComponent } from './_elements/_charts/box-plot/box-plot.component';
+import {LineChartComponent} from './_elements/_charts/line-chart/line-chart.component';
+// Elements
+import { NavbarComponent } from './_elements/navbar/navbar.component';
import { NotificationsComponent } from './_elements/notifications/notifications.component';
import { DatatableComponent } from './_elements/datatable/datatable.component';
-import { FilterDatasetsComponent } from './_pages/filter-datasets/filter-datasets.component';
import { ReactiveBackgroundComponent } from './_elements/reactive-background/reactive-background.component';
-import { ItemModelComponent } from './_elements/item-model/item-model.component';
-import { AnnvisualComponent } from './_elements/annvisual/annvisual.component';
-import { ExperimentComponent } from './experiment/experiment.component';
import { LoadingComponent } from './_elements/loading/loading.component';
-import { ModelLoadComponent } from './_elements/model-load/model-load.component';
-import { AlertDialogComponent } from './_modals/alert-dialog/alert-dialog.component';
-import { AddNewDatasetComponent } from './_elements/add-new-dataset/add-new-dataset.component';
import { GraphComponent } from './_elements/graph/graph.component';
-import { TrainingComponent } from './training/training.component';
-import { ItemExperimentComponent } from './_elements/item-experiment/item-experiment.component';
-import { YesNoDialogComponent } from './_modals/yes-no-dialog/yes-no-dialog.component';
-import { Configuration } from './configuration.service';
-import { PointLinechartComponent } from './point-linechart/point-linechart.component';
-import { MixedChartComponent } from './mixed-chart/mixed-chart.component';
-
-import { LineChartComponent } from './_elements/line-chart/line-chart.component';
+import { GradientBackgroundComponent } from './_elements/gradient-background/gradient-background.component';
+import { PlaylistComponent } from './_elements/playlist/playlist.component';
+import { FormDatasetComponent } from './_elements/form-dataset/form-dataset.component';
+import { FormModelComponent } from './_elements/form-model/form-model.component';
+import { ColumnTableComponent } from './_elements/column-table/column-table.component';
+import { FolderComponent } from './_elements/folder/folder.component';
+import { TestComponent } from './_pages/test/test.component';
+import { DoughnutChartComponent } from './_elements/_charts/doughnut-chart/doughnut-chart.component';
+import { HeatmapComponent } from './_elements/_charts/heatmap/heatmap.component';
+import { HeatMapAllModule } from '@syncfusion/ej2-angular-heatmap';
import { MetricViewComponent } from './_elements/metric-view/metric-view.component';
+
export function initializeApp(appConfig: Configuration) {
return () => appConfig.load();
}
@NgModule({
declarations: [
AppComponent,
- DatasetLoadComponent,
LoginModalComponent,
RegisterModalComponent,
HomeComponent,
NavbarComponent,
- ItemPredictorComponent,
- ItemDatasetComponent,
- CarouselComponent,
- SettingsComponent,
ProfileComponent,
- MyPredictorsComponent,
- MyDatasetsComponent,
- MyModelsComponent,
- BrowseDatasetsComponent,
- BrowsePredictorsComponent,
- PredictComponent,
ScatterchartComponent,
BarchartComponent,
NotificationsComponent,
DatatableComponent,
- FilterDatasetsComponent,
ReactiveBackgroundComponent,
- ItemModelComponent,
- AnnvisualComponent,
ExperimentComponent,
LoadingComponent,
- ModelLoadComponent,
AlertDialogComponent,
- AddNewDatasetComponent,
GraphComponent,
- TrainingComponent,
- ItemExperimentComponent,
YesNoDialogComponent,
- LineChartComponent,
- PointLinechartComponent,
- MixedChartComponent,
- LineChartComponent,
+ PlaygroundComponent,
+ GradientBackgroundComponent,
+ PlaylistComponent,
+ ArchiveComponent,
+ FormDatasetComponent,
+ FormModelComponent,
+ ColumnTableComponent,
+ PieChartComponent,
+ BoxPlotComponent,
+ FolderComponent,
+ EncodingDialogComponent,
+ MissingvaluesDialogComponent,
+ TestComponent,
+ DoughnutChartComponent,
+ HeatmapComponent,
MetricViewComponent,
-
-
+ LineChartComponent,
+ SaveExperimentDialogComponent
],
imports: [
BrowserModule,
@@ -113,6 +106,7 @@ export function initializeApp(appConfig: Configuration) {
MatIconModule,
NgChartsModule,
Ng2SearchPipeModule,
+ HeatMapAllModule
],
providers: [
Configuration,
diff --git a/frontend/src/app/experiment/experiment.component.css b/frontend/src/app/experiment/experiment.component.css
deleted file mode 100644
index d84a897e..00000000
--- a/frontend/src/app/experiment/experiment.component.css
+++ /dev/null
@@ -1,48 +0,0 @@
-#header {
- background-color: #003459;
- padding-top: 30px;
- padding-bottom: 20px;
-}
-
-#header h1 {
- font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
- text-align: center;
- color: white;
-}
-
-#container {
- border-radius: 8px;
-}
-
-#wrapper {
- color: #003459;
-}
-
-.btnType1 {
- background-color: #003459;
- color: white;
-}
-
-.btnType2 {
- background-color: white;
- color: #003459;
- border-color: #003459;
-}
-
-.selectedDatasetClass {
- /*border-color: 2px solid #003459;*/
- background-color: lightblue;
-}
-
-ul li:hover {
- background-color: lightblue;
-}
-
-h2 {
- color: #003459;
-}
-
-.boldClass {
- font-weight: bold;
- font-size: 125%;
-} \ No newline at end of file
diff --git a/frontend/src/app/experiment/experiment.component.html b/frontend/src/app/experiment/experiment.component.html
index 4bdd79b2..62236cce 100644
--- a/frontend/src/app/experiment/experiment.component.html
+++ b/frontend/src/app/experiment/experiment.component.html
@@ -4,15 +4,15 @@
<div id="wrapper">
<div id="container" class="container p-5" style="background-color: white; min-height: 100%;">
- <div class="d-flex flex-row justify-content-center align-items-center my-3 links">
- <a id="firstStep" href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="0" (click)="updateCarouselIndex(0);" [ngClass]="{'boldClass' : carouselIndex == 0}" style="text-decoration: none">Izvor podataka</a>
+ <div class="d-flex flex-row justify-content-center align-items-center my-3">
+ <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="0">Izvor podataka</a>
<mat-icon>arrow_forward</mat-icon>
- <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="1" (click)="updateCarouselIndex(1)" [ngClass]="{'boldClass' : carouselIndex == 1}" style="text-decoration: none">Preprocesiranje</a>
+ <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="1">Preprocesiranje</a>
<mat-icon>arrow_forward</mat-icon>
- <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="2" (click)="updateCarouselIndex(2)" [ngClass]="{'boldClass' : carouselIndex == 2}" style="text-decoration: none">Dodaj eksperiment</a>
+ <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="2">Dodaj eksperiment</a>
</div>
- <div id="carouselExampleControls" class="carousel px-5 mt-5" data-bs-wrap="false" data-bs-ride="carousel" data-bs-interval="false">
+ <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>
@@ -63,10 +63,8 @@
<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 *ngIf="selectedColumnsInfoArray.length == selectedDataset.columnInfo.length" for="delRows" class="form-check-label ">Obriši sve
- redove sa nedostajućim vrednostima ({{selectedDataset.nullRows}} od {{selectedDataset.rowCount}})</label>
- <label *ngIf="selectedColumnsInfoArray.length != selectedDataset.columnInfo.length" for="delRows" class="form-check-label ">Obriši sve
- redove sa nedostajućim vrednostima</label><br>
+ <label for="delRows" class="form-check-label ">Obriši sve
+ redove sa nedostajućim vrednostima ({{selectedDataset.nullRows}} od {{selectedDataset.rowCount}})</label><br>
<input type="radio" [(ngModel)]="experiment.nullValues " [value]="NullValueOptions.DeleteColumns " class="form-check-input" value="deleteCols" name="fillMissing " id="delCols" data-bs-toggle="collapse" data-bs-target="#fillMissingCustom.show">
<label for="delCols" class="form-check-label ">Obriši sve
kolone sa nedostajućim vrednostima ({{countSelectedNullCols()}} od {{selectedDataset.columnInfo.length}})</label><br>
@@ -123,17 +121,13 @@
</div>
<div class="flex-shrink-1 mx-3">
- <div class="input-group" *ngIf="column.columnName != this.experiment.outputColumn">
+ <div class="input-group">
<label class="form-control" [for]="'delCol_'+column.columnName">Izbriši
- kolonu
- <input type="radio" [id]="'delCol_'+column.columnName"
+ kolonu
+ <input type="radio" [id]="'delCol_'+column.columnName"
[name]="'delOp_'+column.columnName"
- (change)="emptyFillTextInput(column.columnName)">
- </label>
+ (change)="emptyFillTextInput(column.columnName)"></label>
</div>
- <label class="form-control text-secondary" role="alert" *ngIf="column.columnName == this.experiment.outputColumn">
- (Izlazna kolona)
- </label>
</div>
<div class="flex-shrink-1 mx-3">
@@ -213,7 +207,7 @@
</div>
<div class="carousel-item mt-2">
- <h2 class="mb-4">3. Dodaj eksperiment</h2>
+ <h2 class="mb-4">3. Dodaj eskperiment</h2>
<div class="row">
<div class="col"></div>
@@ -248,11 +242,11 @@
</div>
- <div class="m-3 d-flex flex-row align-items-center" style=" margin-left: auto;">
- <button class="me-auto" *ngIf="carouselIndex != 0" mat-fab color="primary" data-bs-target="#carouselExampleControls" data-bs-slide="prev" (click)="updateCarouselIndex(carouselIndex - 1)">
+ <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 class="ms-auto" *ngIf="carouselIndex != 2" mat-fab color="primary" data-bs-target="#carouselExampleControls" data-bs-slide="next" (click)="updateCarouselIndex(carouselIndex + 1)">
+ <button mat-fab color="primary" data-bs-target="#carouselExampleControls" data-bs-slide="next">
<mat-icon>arrow_forward</mat-icon>
</button>
</div>
diff --git a/frontend/src/app/experiment/experiment.component.ts b/frontend/src/app/experiment/experiment.component.ts
index 64adce3f..2d0f6ec5 100644
--- a/frontend/src/app/experiment/experiment.component.ts
+++ b/frontend/src/app/experiment/experiment.component.ts
@@ -6,9 +6,6 @@ import { ModelsService } from '../_services/models.service';
import Shared from '../Shared';
import { ExperimentsService } from '../_services/experiments.service';
import { ColumnEncoding } from '../_data/Experiment';
-import { Router } from '@angular/router';
-import { TrainingComponent } from '../training/training.component';
-import { NEVER, retryWhen } from 'rxjs';
@Component({
selector: 'app-experiment',
@@ -32,9 +29,8 @@ export class ExperimentComponent implements OnInit {
selectedNotNullColumnsArray: string[] = [];
tempTestSetDistribution = 90;
- carouselIndex: number = 0;
- constructor(private experimentsService: ExperimentsService, private router: Router) {
+ constructor(private modelsService: ModelsService, private experimentsService: ExperimentsService) {
}
ngOnInit(): void {
@@ -47,6 +43,7 @@ export class ExperimentComponent implements OnInit {
this.experiment.outputColumn = this.selectedDataset.columnInfo[this.selectedDataset.columnInfo.length - 1].columnName;
this.resetColumnEncodings();
+ console.log(this.experiment.encodings);
}
resetColumnEncodings() {
@@ -169,7 +166,7 @@ export class ExperimentComponent implements OnInit {
}
saveExperiment() {
- if (this.selectedDataset == undefined) {
+ if (this.selectedDataset == undefined) {
Shared.openDialog("Greška", "Izvor podataka nije izabran!");
return;
}
@@ -195,14 +192,16 @@ 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;
- Shared.openYesNoDialog("Obaveštenje", "Eksperiment je uspešno kreiran. Da li želite da pređete na treniranje modela?", () => {
- this.router.navigate(['/training', this.experiment._id]);
- });
+ this.selectedColumnsInfoArray = [];
+ this.selectedNotNullColumnsArray = [];
+ this.experiment.encodings = [];
+
+ Shared.openDialog("Obaveštenje", "Eksperiment je uspešno kreiran.");
}, (error) => {
if (error.error == "Experiment with this name exists") {
Shared.openDialog("Greška", "Eksperiment sa unetim nazivom već postoji u Vašoj kolekciji. Unesite neki drugi naziv.");
@@ -220,23 +219,4 @@ export class ExperimentComponent implements OnInit {
}
return counter;
}
-
- updateCarouselIndex(newIndex: number) {
- if (newIndex > 2)
- newIndex = 2;
- else if (newIndex < 0)
- newIndex = 0;
-
- if (this.carouselIndex == 0 && (newIndex == 1 || newIndex == 2))
- this.checkRequiredData();
- this.carouselIndex = newIndex;
- }
-
- checkRequiredData() {
- if (this.selectedDataset == undefined) {
- (<HTMLAnchorElement>document.getElementById("firstStep")).click();
- Shared.openDialog("Pažnja", "Potrebno je da dodate ili izabere izvor podataka kako biste prešli na naredni korak (preprocesiranje).");
- return;
- }
- }
}
diff --git a/frontend/src/app/material.module.ts b/frontend/src/app/material.module.ts
index d16cef3d..baaae58b 100644
--- a/frontend/src/app/material.module.ts
+++ b/frontend/src/app/material.module.ts
@@ -1,21 +1,122 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
-import { MatDialogModule } from '@angular/material/dialog';
-import { MatButtonModule } from '@angular/material/button';
+// Material Form Controls
+import { MatAutocompleteModule } from '@angular/material/autocomplete';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
-import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatRadioModule } from '@angular/material/radio';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSliderModule } from '@angular/material/slider';
+import { MatSlideToggleModule } from '@angular/material/slide-toggle';
+// Material Navigation
+import { MatMenuModule } from '@angular/material/menu';
+import { MatSidenavModule } from '@angular/material/sidenav';
+import { MatToolbarModule } from '@angular/material/toolbar';
+// Material Layout
+import { MatCardModule } from '@angular/material/card';
+import { MatDividerModule } from '@angular/material/divider';
+import { MatExpansionModule } from '@angular/material/expansion';
+import { MatGridListModule } from '@angular/material/grid-list';
+import { MatListModule } from '@angular/material/list';
+import { MatStepperModule } from '@angular/material/stepper';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatTreeModule } from '@angular/material/tree';
+// Material Buttons & Indicators
+import { MatButtonModule } from '@angular/material/button';
+import { MatButtonToggleModule } from '@angular/material/button-toggle';
+import { MatBadgeModule } from '@angular/material/badge';
+import { MatChipsModule } from '@angular/material/chips';
import { MatIconModule } from '@angular/material/icon';
-
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { MatProgressBarModule } from '@angular/material/progress-bar';
+import { MatRippleModule } from '@angular/material/core';
+// Material Popups & Modals
+import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
+import { MatTooltipModule } from '@angular/material/tooltip';
+// Material Data tables
+import { MatPaginatorModule } from '@angular/material/paginator';
+import { MatSortModule } from '@angular/material/sort';
+import { MatTableModule } from '@angular/material/table';
@NgModule({
- exports: [
+ declarations: [],
+ imports: [
CommonModule,
- MatDialogModule,
- MatButtonModule,
+ MatAutocompleteModule,
+ MatCheckboxModule,
+ MatDatepickerModule,
MatFormFieldModule,
MatInputModule,
+ MatRadioModule,
+ MatSelectModule,
+ MatSliderModule,
+ MatSlideToggleModule,
+ MatMenuModule,
+ MatSidenavModule,
+ MatToolbarModule,
+ MatCardModule,
+ MatDividerModule,
+ MatExpansionModule,
+ MatGridListModule,
+ MatListModule,
+ MatStepperModule,
+ MatTabsModule,
+ MatTreeModule,
+ MatButtonModule,
+ MatButtonToggleModule,
+ MatBadgeModule,
+ MatChipsModule,
+ MatIconModule,
+ MatProgressSpinnerModule,
+ MatProgressBarModule,
+ MatRippleModule,
+ MatBottomSheetModule,
+ MatDialogModule,
+ MatSnackBarModule,
+ MatTooltipModule,
+ MatPaginatorModule,
+ MatSortModule,
+ MatTableModule
+ ],
+ exports: [
+ MatAutocompleteModule,
MatCheckboxModule,
- MatIconModule
+ MatDatepickerModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatRadioModule,
+ MatSelectModule,
+ MatSliderModule,
+ MatSlideToggleModule,
+ MatMenuModule,
+ MatSidenavModule,
+ MatToolbarModule,
+ MatCardModule,
+ MatDividerModule,
+ MatExpansionModule,
+ MatGridListModule,
+ MatListModule,
+ MatStepperModule,
+ MatTabsModule,
+ MatTreeModule,
+ MatButtonModule,
+ MatButtonToggleModule,
+ MatBadgeModule,
+ MatChipsModule,
+ MatIconModule,
+ MatProgressSpinnerModule,
+ MatProgressBarModule,
+ MatRippleModule,
+ MatBottomSheetModule,
+ MatDialogModule,
+ MatSnackBarModule,
+ MatTooltipModule,
+ MatPaginatorModule,
+ MatSortModule,
+ MatTableModule
]
})
-export class MaterialModule {} \ No newline at end of file
+export class MaterialModule { } \ No newline at end of file
diff --git a/frontend/src/app/scatterchart/scatterchart.component.css b/frontend/src/app/scatterchart/scatterchart.component.css
deleted file mode 100644
index 5735217e..00000000
--- a/frontend/src/app/scatterchart/scatterchart.component.css
+++ /dev/null
@@ -1,6 +0,0 @@
-#divScatterChart{
- background-color: beige;
- display: block;
- width: 400px;
- height: 200px;
-} \ No newline at end of file
diff --git a/frontend/src/app/scatterchart/scatterchart.component.html b/frontend/src/app/scatterchart/scatterchart.component.html
deleted file mode 100644
index 2b30fe1f..00000000
--- a/frontend/src/app/scatterchart/scatterchart.component.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<p>Scatter chart:</p>
-<div id="divScatterChart">
- <canvas id="ScatterCharts"> </canvas>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/training/training.component.css b/frontend/src/app/training/training.component.css
deleted file mode 100644
index 490c56b5..00000000
--- a/frontend/src/app/training/training.component.css
+++ /dev/null
@@ -1,39 +0,0 @@
-#header {
- background-color: #003459;
- padding-top: 30px;
- padding-bottom: 20px;
-}
-
-#header h1 {
- font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
- text-align: center;
- color: white;
-}
-
-#container {
- border-radius: 8px;
-}
-
-#wrapper {
- color: #003459;
-}
-
-.btnType1 {
- background-color: #003459;
- color: white;
-}
-
-.btnType2 {
- background-color: white;
- color: #003459;
- border-color: #003459;
-}
-
-.selectedExperimentClass {
- /*border-color: 2px solid #003459;*/
- background-color: lightblue;
-}
-
-ul li:hover {
- background-color: lightblue;
-} \ No newline at end of file
diff --git a/frontend/src/app/training/training.component.html b/frontend/src/app/training/training.component.html
index deac88cc..672e75fb 100644
--- a/frontend/src/app/training/training.component.html
+++ b/frontend/src/app/training/training.component.html
@@ -2,40 +2,45 @@
<h1>Trenirajte veštačku neuronsku mrežu</h1>
</div>
<div id="wrapper" class="mb-4">
- <div id="container" class="container p-5" style="background-color: white; min-height: 100%;">
- <h2>1. Izaberite eksperiment iz kolekcije</h2>
- <div class="px-5 mt-5 mb-3">
- <input type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term">
- </div>
- <div class="overflow-auto px-5" style="max-height: 500px;">
- <ul class="list-group">
- <li class="list-group-item p-3" *ngFor="let experiment of myExperiments|filter:term" [ngClass]="{'selectedExperimentClass': this.selectedExperiment == experiment}">
- <app-item-experiment [experiment]="experiment" (click)="selectThisExperiment(experiment);"></app-item-experiment>
- </li>
- </ul>
- </div>
+ <div id="container" class="container p-5 row" style="background-color: white; min-height: 100%;">
+ <div class="col"></div>
- <h2 class="mt-5 mb-2">2. Izaberite model</h2>
- <app-model-load *ngIf="selectedExperiment" (selectedModelChangeEvent)="selectModel($event)" [forExperiment]="selectedExperiment"></app-model-load>
- <h3 *ngIf="!selectedExperiment">Morate prvo izabrati eksperiment.</h3>
+ <div class="col-10">
- <h2 class="my-5">3. Treniranje modela</h2>
+ <h2>1. Izaberite eksperiment iz kolekcije</h2>
+ <div class="px-5 mt-5 mb-3">
+ <input type="text" class="form-control" placeholder="Pretraga"
+ [(ngModel)]="term">
+ </div>
+ <div class="overflow-auto px-5" style="max-height: 500px;">
+ <ul class="list-group">
+ <li class="list-group-item p-3" *ngFor="let experiment of myExperiments|filter:term"
+ [ngClass]="{'selectedExperimentClass': this.selectedExperiment == experiment}">
+ <app-item-experiment [experiment]="experiment"
+ (click)="selectThisExperiment(experiment);"></app-item-experiment>
+ </li>
+ </ul>
+ </div>
+
+ <h2 class="mt-5 mb-2">2. Izaberite model</h2>
+ <app-model-load (selectedModelChangeEvent)="selectModel($event)" [forExperiment]="selectedExperiment"></app-model-load>
+
+ <h2 class="my-5">3. Treniranje modela</h2>
- <div class="d-flex flex-row justify-content-center align-items-center my-3">
- <button #trainButton class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="trainModel();">Treniraj
+ <div class="d-flex flex-row justify-content-center align-items-center my-3">
+ <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="trainModel();">Treniraj
model</button>
+ </div>
+
+ <h2 class="mt-5">Rezultati treniranja</h2>
+ <div class="m-3" *ngIf="trainingResult">
+ <h2 class="my-2">Rezultati treniranja:</h2>
+ <p>
+ {{trainingResult}}
+ </p>
+ </div>
</div>
- <h2 class="mt-5">Rezultati treniranja</h2>
- <div class="m-3" *ngIf="trainingResult">
- <h2 class="my-2">Rezultati treniranja:</h2>
- <app-metric-view *ngIf="trainingResult" [metrics]="trainingResult"></app-metric-view>
- </div>
- </div>
-
- <h2 class="mt-5">Rezultati treniranja</h2>
- <div class="m-3" *ngIf="trainingResult">
- <h2 class="my-2">Rezultati treniranja:</h2>
- <app-metric-view *ngIf="trainingResult" [metrics]="trainingResult"></app-metric-view>
+ <div class="col"></div>
</div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/training/training.component.ts b/frontend/src/app/training/training.component.ts
deleted file mode 100644
index a0bec8e2..00000000
--- a/frontend/src/app/training/training.component.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
-import { ActivatedRoute } from '@angular/router';
-import Shared from '../Shared';
-import Experiment from '../_data/Experiment';
-import Model, { ProblemType } from '../_data/Model';
-import { MetricViewComponent } from '../_elements/metric-view/metric-view.component';
-import { ModelLoadComponent } from '../_elements/model-load/model-load.component';
-import { AuthService } from '../_services/auth.service';
-import { ExperimentsService } from '../_services/experiments.service';
-import { ModelsService } from '../_services/models.service';
-import { SignalRService } from '../_services/signal-r.service';
-
-@Component({
- selector: 'app-training',
- templateUrl: './training.component.html',
- styleUrls: ['./training.component.css']
-})
-export class TrainingComponent implements OnInit {
-
- @ViewChild(ModelLoadComponent) modelLoadComponent?: ModelLoadComponent;
- @ViewChild("trainButton") trainButtonRef!: ElementRef;
-
- @ViewChild(MetricViewComponent) metricViewComponent!: MetricViewComponent;
-
- myExperiments?: Experiment[];
- selectedExperiment?: Experiment;
- selectedModel?: Model;
-
- trainingResult: any;
-
- history: any[] = [];
-
- term: string = "";
-
- constructor(private modelsService: ModelsService, private route: ActivatedRoute, private experimentsService: ExperimentsService, private authService: AuthService, private signalRService: SignalRService) {
- }
-
- ngOnInit(): void {
- this.route.queryParams.subscribe(params => {
- let experimentId = this.route.snapshot.paramMap.get("id");
-
- this.fetchExperiments(experimentId);
-
- this.authService.loggedInEvent.subscribe(_ => {
- this.fetchExperiments(experimentId);
-
- this.signalRService.startConnection();
- });
-
- console.log(this.signalRService.hubConnection);
- if (this.signalRService.hubConnection) {
- this.signalRService.hubConnection.on("NotifyEpoch", (mName: string, mId: string, stat: string, totalEpochs: number, currentEpoch: number) => {
- console.log(this.selectedModel?._id, mId);
- if (this.selectedModel?._id == mId) {
- stat = stat.replace(/'/g, '"');
- this.trainingResult = JSON.parse(stat);
- //console.log('JSON', this.trainingResult);
- this.history.push(this.trainingResult);
- this.metricViewComponent.update(this.history);
- }
- });
- }
- });
- }
-
- fetchExperiments(andSelectWithId: string | null = '') {
- this.experimentsService.getMyExperiments().subscribe((experiments) => {
- this.myExperiments = experiments.reverse();
-
- this.selectedExperiment = this.myExperiments.filter(x => x._id == andSelectWithId)[0];
- if (this.modelLoadComponent)
- this.modelLoadComponent.newModel.type = this.selectedExperiment.type;
- });
- }
-
- selectThisExperiment(experiment: Experiment) {
- this.selectedExperiment = experiment;
- if (this.modelLoadComponent)
- this.modelLoadComponent.newModel.type = this.selectedExperiment.type;
- }
-
- selectModel(model: Model) {
- this.selectedModel = model;
- }
-
- trainModel() {
- this.trainingResult = undefined;
-
- if (this.selectedExperiment == undefined) {
- Shared.openDialog("Greška", "Molimo Vas da izaberete eksperiment iz kolekcije.");
- return;
- }
- if (this.selectedModel == undefined) {
- Shared.openDialog("Greška", "Molimo Vas da izaberete model.");
- return;
- }
- this.trainButtonRef.nativeElement.disabled = true;
- this.modelsService.trainModel(this.selectedModel._id, this.selectedExperiment._id).subscribe((response: any) => {
- //console.log('Train model complete!', response);
- this.trainButtonRef.nativeElement.disabled = false;
- Shared.openDialog("Obaveštenje", "Treniranje modela je počelo!");
- this.trainingResult = response;
- });
- }
-}
diff --git a/frontend/src/assets/images/add_model_background.jpg b/frontend/src/assets/images/add_model_background.jpg
deleted file mode 100644
index d86f0566..00000000
--- a/frontend/src/assets/images/add_model_background.jpg
+++ /dev/null
Binary files differ
diff --git a/frontend/src/assets/images/logo.png b/frontend/src/assets/images/logo.png
index 2e15550a..dc8830de 100644
--- a/frontend/src/assets/images/logo.png
+++ b/frontend/src/assets/images/logo.png
Binary files differ
diff --git a/frontend/src/assets/images/logo_igrannonica_temp - Copy.png b/frontend/src/assets/images/logo_igrannonica_temp - Copy.png
new file mode 100644
index 00000000..a9d7d396
--- /dev/null
+++ b/frontend/src/assets/images/logo_igrannonica_temp - Copy.png
Binary files differ
diff --git a/frontend/src/assets/images/logo_igrannonica_temp.png b/frontend/src/assets/images/logo_igrannonica_temp.png
new file mode 100644
index 00000000..9e8e8855
--- /dev/null
+++ b/frontend/src/assets/images/logo_igrannonica_temp.png
Binary files differ
diff --git a/frontend/src/custom-theme.scss b/frontend/src/custom-theme.scss
index a6538c37..e8626080 100644
--- a/frontend/src/custom-theme.scss
+++ b/frontend/src/custom-theme.scss
@@ -1,35 +1,250 @@
+/**
+* Generated theme by Material Theme Generator
+* https://materialtheme.arcsine.dev
+* Fork at: https://materialtheme.arcsine.dev/?c=YHBhbGV0dGU$YHByaW1hcnk$YF48IzAwYThlOCIsIj9lcjwjYjNlNWY4IiwiO2VyPCMwMDhkZGV$LCIlPmBePCMwMDYzYWIiLCI~ZXI8I2IzZDBlNiIsIjtlcjwjMDA0Nzkxfiwid2Fybj5gXjwjZjliN2I3IiwiP2VyPCNmZGU5ZTkiLCI7ZXI8I2Y2OWY5Zn4sIj9UZXh0PCNkZmQ3ZDciLCI~PTwjMDAzNDU5IiwiO1RleHQ8I2RmZDdkNyIsIjs9PCMwMDM0NTl$LCJmb250cz5bYEA8KC00fixgQDwoLTN$LGBAPCgtMn4sYEA8KC0xfixgQDxoZWFkbGluZX4sYEA8dGl0bGV$LGBAPHN1YiktMn4sYEA8c3ViKS0xfixgQDxib2R5LTJ$LGBAPGJvZHktMX4sYEA8YnV0dG9ufixgQDxjYXB0aW9ufixgQDxpbnB1dCIsInNpemU$bnVsbH1dLCJpY29uczxGaWxsZWQiLCI~bmVzcz5mYWxzZSwidmVyc2lvbj4xM30=
+*/
-// Custom Theming for Angular Material
-// For more information: https://material.angular.io/guide/theming
@use '@angular/material' as mat;
-// Plus imports for other components in your app.
-
// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
-// Be sure that you only ever include this mixin once!
-@include mat.core();
+// Fonts
+@import 'https://fonts.googleapis.com/icon?family=Material+Icons';
+@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap');
+$fontConfig: ( display-4: mat.define-typography-level(112px, 112px, 300, 'Roboto', -0.0134em),
+display-3: mat.define-typography-level(56px, 56px, 400, 'Roboto', -0.0089em),
+display-2: mat.define-typography-level(45px, 48px, 400, 'Roboto', 0.0000em),
+display-1: mat.define-typography-level(34px, 40px, 400, 'Roboto', 0.0074em),
+headline: mat.define-typography-level(24px, 32px, 400, 'Roboto', 0.0000em),
+title: mat.define-typography-level(20px, 32px, 500, 'Roboto', 0.0075em),
+subheading-2: mat.define-typography-level(16px, 28px, 400, 'Roboto', 0.0094em),
+subheading-1: mat.define-typography-level(15px, 24px, 500, 'Roboto', 0.0067em),
+body-2: mat.define-typography-level(14px, 24px, 500, 'Roboto', 0.0179em),
+body-1: mat.define-typography-level(14px, 20px, 400, 'Roboto', 0.0179em),
+button: mat.define-typography-level(14px, 14px, 500, 'Roboto', 0.0893em),
+caption: mat.define-typography-level(12px, 20px, 400, 'Roboto', 0.0333em),
+input: mat.define-typography-level(inherit, 1.125, 400, 'Roboto', 1.5px));
+// Foreground Elements
+// Light Theme Text
+$dark-text: #dfd7d7;
+$dark-primary-text: rgba($dark-text,
+0.87);
+$dark-accent-text: rgba($dark-primary-text,
+0.54);
+$dark-disabled-text: rgba($dark-primary-text,
+0.38);
+$dark-dividers: rgba($dark-primary-text,
+0.12);
+$dark-focused: rgba($dark-primary-text,
+0.12);
+$mat-light-theme-foreground: ( base : black,
+divider : $dark-dividers,
+dividers : $dark-dividers,
+disabled : $dark-disabled-text,
+disabled-button : rgba($dark-text, 0.26),
+disabled-text : $dark-disabled-text,
+elevation : black,
+secondary-text : $dark-accent-text,
+hint-text : $dark-disabled-text,
+accent-text : $dark-accent-text,
+icon : $dark-accent-text,
+icons : $dark-accent-text,
+text : $dark-primary-text,
+slider-min : $dark-primary-text,
+slider-off : rgba($dark-text, 0.26),
+slider-off-active: $dark-disabled-text,
+);
+// Dark Theme text
+$light-text: #dfd7d7;
+$light-primary-text: $light-text;
+$light-accent-text: rgba($light-primary-text,
+0.7);
+$light-disabled-text: rgba($light-primary-text,
+0.5);
+$light-dividers: rgba($light-primary-text,
+0.12);
+$light-focused: rgba($light-primary-text,
+0.12);
+$mat-dark-theme-foreground: ( base : $light-text,
+divider : $light-dividers,
+dividers : $light-dividers,
+disabled : $light-disabled-text,
+disabled-button : rgba($light-text, 0.3),
+disabled-text : $light-disabled-text,
+elevation : black,
+hint-text : $light-disabled-text,
+secondary-text : $light-accent-text,
+accent-text : $light-accent-text,
+icon : $light-text,
+icons : $light-text,
+text : $light-text,
+slider-min : $light-text,
+slider-off : rgba($light-text, 0.3),
+slider-off-active: rgba($light-text, 0.3),
+);
+// Background config
+// Light bg
+$light-background : #003459;
+$light-bg-darker-5 : darken($light-background,
+5%);
+$light-bg-darker-10 : darken($light-background,
+10%);
+$light-bg-darker-20 : darken($light-background,
+20%);
+$light-bg-darker-30 : darken($light-background,
+30%);
+$light-bg-lighter-5 : lighten($light-background,
+5%);
+$dark-bg-tooltip : lighten(#003459,
+20%);
+$dark-bg-alpha-4 : rgba(#003459,
+0.04);
+$dark-bg-alpha-12 : rgba(#003459,
+0.12);
+$mat-light-theme-background: ( background : $light-background,
+status-bar : $light-bg-darker-20,
+app-bar : $light-bg-darker-5,
+hover : $dark-bg-alpha-4,
+card : $light-bg-lighter-5,
+dialog : $light-bg-lighter-5,
+tooltip : $dark-bg-tooltip,
+disabled-button : $dark-bg-alpha-12,
+raised-button : $light-bg-lighter-5,
+focused-button : $dark-focused,
+selected-button : $light-bg-darker-20,
+selected-disabled-button: $light-bg-darker-30,
+disabled-button-toggle : $light-bg-darker-10,
+unselected-chip : $light-bg-darker-10,
+disabled-list-option : $light-bg-darker-10,
+);
+// Dark bg
+$dark-background : #003459;
+$dark-bg-lighter-5 : lighten($dark-background,
+5%);
+$dark-bg-lighter-10 : lighten($dark-background,
+10%);
+$dark-bg-lighter-20 : lighten($dark-background,
+20%);
+$dark-bg-lighter-30 : lighten($dark-background,
+30%);
+$light-bg-alpha-4 : rgba(#003459,
+0.04);
+$light-bg-alpha-12 : rgba(#003459,
+0.12);
+// Background palette for dark themes.
+$mat-dark-theme-background: ( background : $dark-background,
+status-bar : $dark-bg-lighter-20,
+app-bar : $dark-bg-lighter-5,
+hover : $light-bg-alpha-4,
+card : $dark-bg-lighter-5,
+dialog : $dark-bg-lighter-5,
+tooltip : $dark-bg-lighter-20,
+disabled-button : $light-bg-alpha-12,
+raised-button : $dark-bg-lighter-5,
+focused-button : $light-focused,
+selected-button : $dark-bg-lighter-20,
+selected-disabled-button: $dark-bg-lighter-30,
+disabled-button-toggle : $dark-bg-lighter-10,
+unselected-chip : $dark-bg-lighter-20,
+disabled-list-option : $dark-bg-lighter-10,
+);
+// Compute font config
+@include mat.core($fontConfig);
+// Theme Config
+body {
+ --primary-color: #00a8e8;
+ --primary-lighter-color: #b3e5f8;
+ --primary-darker-color: #008dde;
+ --text-primary-color: #{$light-primary-text};
+ --text-primary-lighter-color: #{$dark-primary-text};
+ --text-primary-darker-color: #{$light-primary-text};
+}
+
+$mat-primary: ( main: #00a8e8,
+lighter: #b3e5f8,
+darker: #008dde,
+200: #00a8e8, // For slide toggle,
+contrast: ( main: $light-primary-text, lighter: $dark-primary-text, darker: $light-primary-text, ));
+$theme-primary: mat.define-palette($mat-primary,
+main,
+lighter,
+darker);
+body {
+ --accent-color: #0063ab;
+ --accent-lighter-color: #b3d0e6;
+ --accent-darker-color: #004791;
+ --text-accent-color: #{$light-primary-text};
+ --text-accent-lighter-color: #{$dark-primary-text};
+ --text-accent-darker-color: #{$light-primary-text};
+}
-// Define the palettes for your theme using the Material Design palettes available in palette.scss
-// (imported above). For each palette, you can optionally specify a default, lighter, and darker
-// hue. Available color palettes: https://material.io/design/color/
-$frontend-primary: mat.define-palette(mat.$indigo-palette);
-$frontend-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400);
+$mat-accent: ( main: #0063ab,
+lighter: #b3d0e6,
+darker: #004791,
+200: #0063ab, // For slide toggle,
+contrast: ( main: $light-primary-text, lighter: $dark-primary-text, darker: $light-primary-text, ));
+$theme-accent: mat.define-palette($mat-accent,
+main,
+lighter,
+darker);
+body {
+ --warn-color: #f9b7b7;
+ --warn-lighter-color: #fde9e9;
+ --warn-darker-color: #f69f9f;
+ --text-warn-color: #{$dark-primary-text};
+ --text-warn-lighter-color: #{$dark-primary-text};
+ --text-warn-darker-color: #{$dark-primary-text};
+}
-// The warn palette is optional (defaults to red).
-$frontend-warn: mat.define-palette(mat.$red-palette);
+$mat-warn: ( main: #f9b7b7,
+lighter: #fde9e9,
+darker: #f69f9f,
+200: #f9b7b7, // For slide toggle,
+contrast: ( main: $dark-primary-text, lighter: $dark-primary-text, darker: $dark-primary-text, ));
+$theme-warn: mat.define-palette($mat-warn,
+main,
+lighter,
+darker);
+;
+$theme: ( primary: $theme-primary,
+accent: $theme-accent,
+warn: $theme-warn,
+is-dark: true,
+foreground: $mat-dark-theme-foreground,
+background: $mat-dark-theme-background,
+);
+$altTheme: ( primary: $theme-primary,
+accent: $theme-accent,
+warn: $theme-warn,
+is-dark: false,
+foreground: $mat-light-theme-foreground,
+background: $mat-light-theme-background,
+);
+// Theme Init
+@include mat.all-component-themes($theme);
+.theme-alternate {
+ @include mat.all-component-themes($altTheme);
+}
-// Create the theme object. A theme consists of configurations for individual
-// theming systems such as "color" or "typography".
-$frontend-theme: mat.define-light-theme((
- color: (
- primary: $frontend-primary,
- accent: $frontend-accent,
- warn: $frontend-warn,
- )
-));
+// Specific component overrides, pieces that are not in line with the general theming
+// Handle buttons appropriately, with respect to line-height
+.mat-raised-button,
+.mat-stroked-button,
+.mat-flat-button {
+ padding: 0 1.15em;
+ margin: 0 .65em;
+ min-width: 3em;
+ line-height: 36.4px
+}
-// Include theme styles for core and each component used in your app.
-// Alternatively, you can import and @include the theme mixins for each component
-// that you are using.
-@include mat.all-component-themes($frontend-theme);
+.mat-standard-chip {
+ padding: .5em .85em;
+ min-height: 2.5em;
+}
+.material-icons {
+ font-size: 24px;
+ font-family: 'Material Icons', 'Material Icons';
+ .mat-badge-content {
+ font-family: 'Roboto';
+ }
+} \ No newline at end of file
diff --git a/frontend/src/styles.css b/frontend/src/styles.css
index 802b8bd0..50986ea4 100644
--- a/frontend/src/styles.css
+++ b/frontend/src/styles.css
@@ -1,5 +1,8 @@
@import '~bootstrap/dist/css/bootstrap.min.css';
-body {
- /*background-image: url('/assets/images/add_model_background.jpg');*/
- background-color: #003459;
-} \ No newline at end of file
+@import './styles/theme.css';
+@import './styles/layout.css';
+@import './styles/helper.css';
+@import './styles/scrollbar.css';
+@import './styles/fx.css';
+@import './styles/font.css';
+@import 'material-icons/iconfont/material-icons.css'; \ No newline at end of file
diff --git a/frontend/src/styles/font.css b/frontend/src/styles/font.css
new file mode 100644
index 00000000..a4d876f5
--- /dev/null
+++ b/frontend/src/styles/font.css
@@ -0,0 +1,6 @@
+/*p,
+a {
+ font-family: Helvetica, sans-serif;
+ letter-spacing: 2px;
+ font-weight: 100;
+}*/ \ No newline at end of file
diff --git a/frontend/src/styles/fx.css b/frontend/src/styles/fx.css
new file mode 100644
index 00000000..9c29ad58
--- /dev/null
+++ b/frontend/src/styles/fx.css
@@ -0,0 +1,24 @@
+.shadowed:hover {
+ box-shadow: rgba(0, 152, 189, 0.4) 0px 5px, rgba(0, 152, 189, 0.3) 0px 10px, rgba(0, 152, 189, 0.2) 0px 15px, rgba(0, 152, 189, 0.1) 0px 20px, rgba(0, 152, 189, 0.05) 0px 25px;
+}
+
+.shadowed:first-child:hover {
+ box-shadow: rgba(0, 152, 189, 0.4) -5px 5px, rgba(0, 152, 189, 0.3) -10px 10px, rgba(0, 152, 189, 0.2) -15px 15px, rgba(0, 152, 189, 0.1) -20px 20px, rgba(0, 152, 189, 0.05) -25px 25px;
+}
+
+.shadowed:last-child:hover {
+ box-shadow: rgba(0, 152, 189, 0.4) 5px 5px, rgba(0, 152, 189, 0.3) 10px 10px, rgba(0, 152, 189, 0.2) 15px 15px, rgba(0, 152, 189, 0.1) 20px 20px, rgba(0, 152, 189, 0.05) 25px 25px;
+}
+
+.shadow-accent:hover {
+ box-shadow: rgba(0, 152, 189, 0.4) 0px 5px, rgba(0, 152, 189, 0.3) 0px 10px, rgba(0, 152, 189, 0.2) 0px 15px, rgba(0, 152, 189, 0.1) 0px 20px, rgba(0, 152, 189, 0.05) 0px 25px;
+ animation-name: holo-hover;
+ animation-duration: 300ms;
+ transform: perspective(100em) rotateX(10deg);
+}
+
+@keyframes holo-hover {
+ to {
+ transform: perspective(100em) rotateX(10deg);
+ }
+} \ No newline at end of file
diff --git a/frontend/src/styles/helper.css b/frontend/src/styles/helper.css
new file mode 100644
index 00000000..1c172058
--- /dev/null
+++ b/frontend/src/styles/helper.css
@@ -0,0 +1,144 @@
+@keyframes logo-animation {
+ from {
+ transform: perspective(100em) rotateX(50deg) rotateZ(0deg);
+ }
+ to {
+ transform: perspective(100em) rotateX(50deg) rotateZ(360deg);
+ }
+}
+
+.ann-logo {
+ animation-name: logo-animation;
+ animation-duration: 3140ms;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+}
+
+.bg-controls {
+ position: fixed;
+ bottom: 10px;
+ right: 10px;
+}
+
+.center-horizontal {
+ margin-top: 50%;
+ margin-left: auto;
+ transform: translateX(-50%);
+}
+
+.footer-center {
+ position: relative;
+ height: 1rem;
+ text-align: center;
+}
+
+.row-height {
+ white-space: nowrap;
+ height: 1rem;
+}
+
+.icon-double>*:nth-child(2) {
+ margin-left: -1rem;
+}
+
+.no-wrap {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.btn-clear {
+ border: unset;
+ background-color: unset;
+ outline: unset;
+ position: relative;
+}
+
+.input-icon {
+ color: var(--offwhite);
+ transform: translateY(25%);
+}
+
+.input-icon:hover {
+ color: var(--ns-primary);
+}
+
+.f-row {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.icon-toggle {
+ color: var(--offwhite);
+ height: 100%;
+}
+
+.icon-toggle>* {
+ margin-top: 5px;
+}
+
+.icon-toggle:active {
+ background-color: var(--ns-primary);
+}
+
+.icon-toggle-on {
+ background-color: var(--ns-primary);
+}
+
+.icon-toggle-on>* {
+ transform: scale(1.3);
+}
+
+.force-link {
+ color: var(--offwhite) !important;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.text-primary {
+ color: var(--ns-primary) !important;
+}
+
+.btn-icon {
+ color: var(--offwhite) !important;
+ background-color: var(--ns-primary);
+ border-radius: 50%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ margin: 3px;
+ width: 28px;
+ height: 28px;
+}
+
+.bg-blur {
+ backdrop-filter: blur(2px);
+}
+
+.chart-wrapper {
+ width: 150px;
+ height: 150px;
+ padding-left: 2px;
+}
+
+.close-button {
+ background-color: none;
+ color: white;
+ position: absolute;
+ top: 5px;
+ right: 5px;
+}
+
+input:-webkit-autofill,
+textarea:-webkit-autofill,
+select:-webkit-autofill {
+ -webkit-box-shadow: 0 0 0 1000px var(--ns-bg) inset !important;
+ -webkit-text-fill-color: var(--ns-accent) !important;
+}
+
+a {
+ color: var(--ns-accent) !important;
+} \ No newline at end of file
diff --git a/frontend/src/styles/layout.css b/frontend/src/styles/layout.css
new file mode 100644
index 00000000..07c0bf34
--- /dev/null
+++ b/frontend/src/styles/layout.css
@@ -0,0 +1,69 @@
+/*Mora da se ispravi za media kada je ekran premali pa se poredjaju u kolonu*/
+
+html,
+body {
+ height: 100%;
+}
+
+.align-items-view>*:first-child {
+ transform: perspective(100em) rotateY(25deg) translateZ(1em);
+}
+
+.align-items-view>* {
+ transform: perspective(100em) translateZ(-2em);
+}
+
+.align-items-view>*:last-child {
+ transform: perspective(100em) rotateY(-25deg) translateZ(1em);
+}
+
+.ns-row {
+ width: 98%;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ margin: 0;
+ padding: 0;
+}
+
+.ns-col {
+ flex-grow: 1;
+ padding: 2px;
+ margin-bottom: 0;
+ padding-bottom: 0;
+ flex-grow: 1;
+ flex-shrink: 0;
+ flex-basis: 50%;
+}
+
+@media screen and (min-width: 1200px) {
+ .ns-col {
+ flex-basis: 25%;
+ }
+}
+
+@media screen and (min-width: 1600px) {
+ .ns-col {
+ flex-basis: 12.5%;
+ width: 10%;
+ }
+}
+
+
+/*.break-1,
+.break-2 {
+ height: 1px;
+ width: 100%;
+}
+
+@media screen and (min-width: 1200px) {
+ .break-1 {
+ display: none;
+ }
+}
+
+@media screen and (min-width: 1600px) {
+ .break-2 {
+ display: none;
+ }
+}*/ \ No newline at end of file
diff --git a/frontend/src/styles/scrollbar.css b/frontend/src/styles/scrollbar.css
new file mode 100644
index 00000000..7fb79329
--- /dev/null
+++ b/frontend/src/styles/scrollbar.css
@@ -0,0 +1,28 @@
+/* width */
+
+::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+
+
+/* Track */
+
+::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0);
+}
+
+
+/* Handle */
+
+::-webkit-scrollbar-thumb {
+ background: rgba(0, 188, 252, 0.6);
+ border-radius: 25px;
+}
+
+
+/* Handle on hover */
+
+::-webkit-scrollbar-thumb:hover {
+ background: rgba(0, 188, 252, 0.8);
+} \ No newline at end of file
diff --git a/frontend/src/styles/theme.css b/frontend/src/styles/theme.css
new file mode 100644
index 00000000..ee7a2e61
--- /dev/null
+++ b/frontend/src/styles/theme.css
@@ -0,0 +1,68 @@
+:root {
+ --ns-primary: #0063AB;
+ --ns-primary-25: rgba(0, 99, 171, 0.25);
+ --ns-primary-50: rgba(0, 99, 171, 0.5);
+ --ns-accent: #00a8e8;
+ --ns-bg: #003459;
+ --ns-bg-light-30: rgba(0, 152, 189, 0.3);
+ --ns-bg-dark-100: rgba(0, 65, 101, 1.0);
+ --ns-bg-dark-50: rgba(0, 65, 101, 0.5);
+ --offwhite: #dfd7d7;
+ --ns-warn: #f9b7b7;
+ --ns-alt: #002b49;
+}
+
+body {
+ /*background-image: url('/assets/images/add_model_background.jpg');*/
+ background-color: var(--ns-bg);
+}
+
+.bg-light {
+ background-color: var(--ns-bg-light-30) !important;
+}
+
+.bg-alt {
+ background-color: var(--ns-alt);
+}
+
+.ns-border-primary {
+ border: 1px solid var(--ns-primary);
+}
+
+.ns-bg-dark-50 {
+ background-color: var(--ns-bg-dark-50) !important;
+}
+
+.ns-bg-dark-100 {
+ background-color: var(--ns-bg-dark-100) !important;
+}
+
+a {
+ color: var(--ns-primary) !important;
+}
+
+.text-offwhite {
+ color: var(--offwhite) !important;
+}
+
+.highlight {
+ color: var(--ns-accent);
+}
+
+
+/* Ripple effect */
+
+.bubble {
+ background-position: center;
+ transition: background 0.2s;
+}
+
+.bubble:hover {
+ background: var(--ns-primary-50) radial-gradient(circle, transparent 1%, var(--ns-accent) 1%) center/15000%;
+}
+
+.bubble:active {
+ background-color: #6eb9f7;
+ background-size: 100%;
+ transition: background 0s;
+} \ No newline at end of file