aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/app/_elements
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/app/_elements')
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts124
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.css9
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.html5
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts69
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts41
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css12
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html4
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.html12
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.ts117
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.ts100
-rw-r--r--frontend/src/app/_elements/folder/folder.component.css9
-rw-r--r--frontend/src/app/_elements/folder/folder.component.html41
-rw-r--r--frontend/src/app/_elements/folder/folder.component.ts146
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.css22
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.html26
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.ts2
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.css2
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.html36
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.ts35
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.css25
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.html25
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.ts20
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.html215
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.ts114
24 files changed, 519 insertions, 692 deletions
diff --git a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts
index d6f4b6ec..bf5e3fd6 100644
--- a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts
@@ -16,16 +16,31 @@ Chart.register(BoxPlotController, BoxAndWiskers, LinearScale, CategoryScale);
})
export class BoxPlotComponent implements AfterViewInit {
- @Input()width?: number;
- @Input()height?: number;
-
+ @Input() width?: number;
+ @Input() height?: number;
+ @Input() mean?: number;
+ @Input() median?: number;
+ @Input() min?: number;
+ @Input() max?: number;
+ @Input() q1?: number;
+ @Input() q3?: number;
+
+ updateChart(min: number, max: number, q1: number, q3: number, median: number) {
+ if (this.myChart) {
+ this.boxplotData.datasets[0].data = [[min, q1, median, q3, max]]
+ this.myChart.update();
+ }
+ };
+
@ViewChild('boxplot') chartRef!: ElementRef;
- constructor() { }
+ constructor() {
+ //this.updateChart();
+ }
boxplotData = {
// define label tree
- labels: ['January'/*, 'February', 'March', 'April', 'May', 'June', 'July'*/],
- datasets: [{
+ //labels: ['January'/*, 'February', 'March', 'April', 'May', 'June', 'July'*/],
+ datasets: [{
label: 'Dataset 1',
backgroundColor: '#0063AB',
borderColor: '#dfd7d7',
@@ -35,68 +50,43 @@ export class BoxPlotComponent implements AfterViewInit {
padding: 10,
itemRadius: 0,
data: [
- randomValues(100, 0, 100),
- /*randomValues(100, 0, 20),
- randomValues(100, 20, 70),
- randomValues(100, 60, 100),
- randomValues(40, 50, 100),
- randomValues(100, 60, 120),
- randomValues(100, 80, 100)*/
- ]}/*, {
- label: 'Dataset 2',
- backgroundColor: 'rgba(0,0,255,0.5)',
- borderColor: 'blue',
- borderWidth: 1,
- outlierColor: '#999999',
- padding: 10,
- itemRadius: 0,
- data: [
- randomValues(100, 60, 100),
- randomValues(100, 0, 100),
- randomValues(100, 0, 20),
- randomValues(100, 20, 70),
- randomValues(40, 60, 120),
- randomValues(100, 20, 100),
- randomValues(100, 80, 100)
- ]
- }*/]
- };
+ randomValues(100, 0, 100),
+ ]
+ }]
+ };
ngAfterViewInit(): void {
- const myChart = new Chart(this.chartRef.nativeElement, {
- type: "boxplot",
- data: this.boxplotData,
- options: {
- /*title: {
- display: true,
- text: 'Predicted world population (millions) in 2050'
- }*/
- plugins:{
- legend: {
- display: false
- },
- },
- scales : {
- x: {
- ticks: {
- color: '#dfd7d7'
- },
- grid: {
- color: "rgba(0, 99, 171, 0.5)"
- }
- },
- y : {
- min: -50,
- max: 200,
- ticks: {
- color: '#dfd7d7'
- },
- grid: {
- color: "rgba(0, 99, 171, 0.5)"
- }
- }
+ this.myChart = new Chart(this.chartRef.nativeElement, {
+ type: "boxplot",
+ data: this.boxplotData,
+ options: {
+ plugins: {
+ legend: {
+ display: false
+ },
+ },
+ scales: {
+ x: {
+ ticks: {
+ color: '#dfd7d7'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ },
+ y: {
+ min: this.min,
+ max: this.max,
+ ticks: {
+ color: '#dfd7d7'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
}
- }
- });
-}
+ }
+ }
+ });
+ }
+ myChart?: Chart;
}
diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
index e69de29b..a190693a 100644
--- a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
@@ -0,0 +1,9 @@
+canvas{
+
+ width:100% !important;
+ height:90% !important;
+ border: 1px solid var(--ns-primary);
+ background-color: var(--ns-bg-dark-100);
+ border-radius: 5px;
+ margin: 10px;
+ } \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
index 7f18256a..5bb7aae6 100644
--- a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
@@ -1,3 +1,4 @@
- <canvas id="myChart" style="width: 100%; height: 530px;">
- </canvas> \ No newline at end of file
+ <canvas id="myChart" >
+ </canvas>
+ \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts
index 655db9ec..e873618c 100644
--- a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts
@@ -13,22 +13,18 @@ export class LineChartComponent implements AfterViewInit {
dataMAE: number[] = [];
dataMSE: number[] = [];
dataLOSS: number[] = [];
-
+ dataValAcc:number[]=[];
+ dataValMAE:number[]=[];
+ dataValMSE:number[]=[];
+ dataValLoss:number[]=[];
dataEpoch: number[] = [];
constructor() {
- /*let i = 0;
- setInterval(() => {
- this.dataAcc.push(0.5);
- this.dataEpoch.push(i);
- i++;
- this.update();
- }, 200);*/
}
myChart!: Chart;
- update(myEpochs: number[], myAcc: number[], myLoss: number[], myMae: number[], myMse: number[]) {
+ update(myEpochs: number[], myAcc: number[], myLoss: number[], myMae: number[], myMse: number[], myValAcc:number[],myValLoss:number[],myValMae:number[],myValMse:number[]) {
this.dataAcc.length = 0;
this.dataAcc.push(...myAcc);
@@ -42,6 +38,18 @@ export class LineChartComponent implements AfterViewInit {
this.dataLOSS.push(...myLoss);
this.dataMSE.length = 0;
+ this.dataMSE.push(...myValAcc);
+
+ this.dataMSE.length = 0;
+ this.dataMSE.push(...myValLoss);
+
+ this.dataMSE.length = 0;
+ this.dataMSE.push(...myValMae);
+
+ this.dataMSE.length = 0;
+ this.dataMSE.push(...myValMse);
+
+ this.dataMSE.length = 0;
this.dataMSE.push(...myMse);
this.myChart.update();
@@ -54,8 +62,15 @@ export class LineChartComponent implements AfterViewInit {
data: {
labels: this.dataEpoch,
datasets: [{
+
label: 'Accuracy',
data: this.dataAcc,
+ borderWidth: 1,
+
+ },
+ {
+ label: 'VAl_Accuracy',
+ data: this.dataMSE,
borderWidth: 1
},
{
@@ -64,18 +79,47 @@ export class LineChartComponent implements AfterViewInit {
borderWidth: 1
},
{
+ label: 'Val_Loss',
+ data: this.dataMSE,
+ borderWidth: 1
+ },
+ {
label: 'MAE',
data: this.dataMAE,
borderWidth: 1
},
{
+ label: 'Val_MAE',
+ data: this.dataMSE,
+ borderWidth: 1
+ },
+ {
label: 'MSE',
data: this.dataMSE,
borderWidth: 1
+ },
+ {
+ label: 'Val_MSE',
+ data: this.dataMSE,
+ borderWidth: 1
}
]
},
options: {
+ responsive: true,
+ maintainAspectRatio: true,
+
+ plugins: {
+ legend: {
+ labels: {
+ // This more specific font property overrides the global property
+ color:'white',
+ font: {
+ size: 10
+ }
+ }
+ }
+ },
scales: {
x: {
ticks: {
@@ -83,21 +127,24 @@ export class LineChartComponent implements AfterViewInit {
},
grid: {
color: "rgba(0, 99, 171, 0.5)"
- }
+ },
},
y: {
beginAtZero: true,
ticks: {
color: 'white'
+
},
grid: {
color: "rgba(0, 99, 171, 0.5)"
}
}
+ },
+ animation: {
+ duration: 0
}
-
}
}
);
diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts
index f141f522..c2bd3262 100644
--- a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts
@@ -10,21 +10,36 @@ export class PieChartComponent implements AfterViewInit {
@Input()width?: number;
@Input()height?: number;
+ @Input()uniqueValues?: string[] = [];
+ @Input()uniqueValuesPercent?: number[] = [];
+
+ updatePieChart(uniqueValues: string[], uniqueValuesPercent: number[]){
+ console.log(this.uniqueValues, this.uniqueValuesPercent);
+ const newPieChartData = {
+ datasets: [{
+ label: "Population (millions)",
+ backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
+ data: [2478,5267,734,784,433],
+ }]
+
+ }
+ };
@ViewChild('piechart') chartRef!: ElementRef;
constructor() { }
+ pieChartData = {
+ datasets: [{
+ label: "Population (millions)",
+ backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
+ data: [2478,5267,734,784,433]
+ }]
+}
+
ngAfterViewInit(): void {
const myChart = new Chart(this.chartRef.nativeElement, {
type: 'pie',
- data: {
- labels: ["Africa", "Asia", "Europe", "Latin America", "North America"],
- datasets: [{
- label: "Population (millions)",
- backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
- data: [2478,5267,734,784,433],
- }]
- },
+ data: this.pieChartData,
options: {
/*title: {
display: true,
@@ -36,11 +51,9 @@ export class PieChartComponent implements AfterViewInit {
},
},
layout: {
- padding: 15}
+ padding: 15
+ }
}
-});
-
- }
+ });}
-
-}
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
index 005cb692..3e91b926 100644
--- a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
@@ -1,4 +1,8 @@
-#divScatterChart{
-
- display: block;
-} \ No newline at end of file
+canvas{
+ width:95% !important;
+ height:95% !important;
+ border: 1px solid var(--ns-primary);
+ background-color: var(--ns-bg-dark-100);
+ border-radius: 5px;
+ margin: 10px;
+ } \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
index ef41775a..eedc6ade 100644
--- a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
@@ -1,4 +1,2 @@
-<div id="divScatterChart" style="width: 100%;height: 100%;">
- <canvas id="ScatterCharts" style="width: 100%;height: 280px;"> </canvas>
-</div> \ No newline at end of file
+ <canvas id="ScatterCharts"> </canvas>
diff --git a/frontend/src/app/_elements/column-table/column-table.component.html b/frontend/src/app/_elements/column-table/column-table.component.html
index efc093d2..d07d50b2 100644
--- a/frontend/src/app/_elements/column-table/column-table.component.html
+++ b/frontend/src/app/_elements/column-table/column-table.component.html
@@ -113,8 +113,8 @@
<tr class="graphics-row">
<th class="no-pad">Grafik</th>
<td class="no-pad" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'graphic-class' : !columnsChecked[i]}">
- <app-box-plot *ngIf="this.experiment.columnTypes[i] == ColumnType.numerical" [width]="150" [height]="150"></app-box-plot>
- <app-pie-chart *ngIf="this.experiment.columnTypes[i] == ColumnType.categorical" [width]="150" [height]="150"></app-pie-chart>
+ <app-box-plot *ngIf="this.experiment.columnTypes[i] == ColumnType.numerical" [width]="150" [height]="150" [mean]="colInfo.mean" [median]="colInfo.median" [min]="colInfo.min" [max]="colInfo.max" [q1]="colInfo.q1" [q3]="colInfo.q3"></app-box-plot>
+ <app-pie-chart *ngIf="this.experiment.columnTypes[i] == ColumnType.categorical" [width]="150" [height]="150" [uniqueValues]="colInfo.uniqueValues" [uniqueValuesPercent]="colInfo.uniqueValuesPercent"></app-pie-chart>
</td>
</tr>
<tr>
@@ -142,7 +142,10 @@
</th>
<td *ngFor="let colInfo of dataset.columnInfo; let i = index" class="pad-fix" [ngClass]="{'text-disabled' : !columnsChecked[i]}">
<mat-form-field>
- <mat-select matNativeControl [(value)]="experiment.encodings[i].encoding" [disabled]="!columnsChecked[i]" (selectionChange)="columnTableChangeDetected()">
+ <mat-select matNativeControl [(value)]="experiment.encodings[i].encoding" [disabled]="!columnsChecked[i] || experiment.columnTypes[i] == ColumnType.numerical || colInfo.columnName == experiment.outputColumn" (selectionChange)="columnTableChangeDetected()">
+ <mat-option [value]="Encoding.Label" *ngIf="experiment.columnTypes[i] == ColumnType.numerical || colInfo.columnName == experiment.outputColumn" [selected]="experiment.columnTypes[i] == ColumnType.numerical || colInfo.columnName == experiment.outputColumn">
+ Nema enkodiranja
+ </mat-option>
<mat-option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" [value]="option">
{{ optionName }}
</mat-option>
@@ -208,8 +211,9 @@
<div class="ns-col rounded">
<mat-form-field appearance="fill" class="align-items-center justify-content-center pt-3 w-100">
<mat-label>Izlazna kolona</mat-label>
- <mat-select [(value)]="experiment.outputColumn" (selectionChange)="changeOutputColumn(this.experiment.inputColumns[0])">
+ <mat-select [(value)]="experiment.outputColumn" (selectionChange)="changeProblemType()">
<mat-option *ngFor="let inputColumn of experiment.inputColumns" [value]="inputColumn">{{inputColumn}}</mat-option>
+ <mat-option *ngIf="experiment.inputColumns.length == 0" value="-">-</mat-option>
</mat-select>
</mat-form-field>
</div>
diff --git a/frontend/src/app/_elements/column-table/column-table.component.ts b/frontend/src/app/_elements/column-table/column-table.component.ts
index c200e674..217eda30 100644
--- a/frontend/src/app/_elements/column-table/column-table.component.ts
+++ b/frontend/src/app/_elements/column-table/column-table.component.ts
@@ -1,4 +1,4 @@
-import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChildren } from '@angular/core';
+import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren } from '@angular/core';
import Dataset from 'src/app/_data/Dataset';
import Experiment, { ColumnEncoding, Encoding, ColumnType, NullValueOptions } from 'src/app/_data/Experiment';
import { DatasetsService } from 'src/app/_services/datasets.service';
@@ -12,6 +12,8 @@ import { ExperimentsService } from 'src/app/_services/experiments.service';
import { SaveExperimentDialogComponent } from 'src/app/_modals/save-experiment-dialog/save-experiment-dialog.component';
import { AlertDialogComponent } from 'src/app/_modals/alert-dialog/alert-dialog.component';
import Shared from 'src/app/Shared';
+import { PieChartComponent } from '../_charts/pie-chart/pie-chart.component';
+import { BoxPlotComponent } from '../_charts/box-plot/box-plot.component';
@Component({
selector: 'app-column-table',
@@ -20,10 +22,13 @@ import Shared from 'src/app/Shared';
})
export class ColumnTableComponent implements AfterViewInit {
+ @ViewChildren(BoxPlotComponent) boxplotComp!: QueryList<BoxPlotComponent>;
+ @ViewChildren(PieChartComponent) piechartComp!: QueryList<PieChartComponent>;
@Input() dataset?: Dataset;
@Input() experiment!: Experiment;
@Output() okPressed: EventEmitter<string> = new EventEmitter();
@Output() columnTableChanged = new EventEmitter();
+ @Output() experimentChanged = new EventEmitter();
Object = Object;
Encoding = Encoding;
@@ -41,9 +46,35 @@ export class ColumnTableComponent implements AfterViewInit {
//ovo mi nece trebati jer primam dataset iz druge komponente
}
+ updateCharts() {
+ //min: number, max: number, q1: number, q3: number, median: number
+ let i = 0;
+ this.boxplotComp.changes.subscribe(() => {
+ const bps = this.boxplotComp.toArray();
+ this.dataset?.columnInfo.forEach(colInfo => {
+ if (this.experiment.columnTypes[i] == ColumnType.numerical) {
+ bps[i].updateChart(colInfo!.min, colInfo.max, colInfo.q1, colInfo.q3, colInfo.median);
+ i++;
+ }
+ });
+ });
+ }
+
+ updatePieChart() {
+ //min: number, max: number, q1: number, q3: number, median: number
+ let i = 0;
+ const pieChart = this.piechartComp.toArray();
+ this.dataset?.columnInfo.forEach(colInfo => {
+ if (this.experiment.columnTypes[i] == ColumnType.categorical) {
+ pieChart[i].updatePieChart(colInfo!.uniqueValues, colInfo.uniqueValuesPercent);
+ i++;
+ }
+ });
+ }
+
loadDataset(dataset: Dataset) {
+ console.log("LOADED DATASET");
this.dataset = dataset;
-
this.setColumnTypeInitial();
this.dataset.columnInfo.forEach(column => {
@@ -62,13 +93,17 @@ export class ColumnTableComponent implements AfterViewInit {
this.datasetService.getDatasetFilePartial(this.dataset.fileId, 0, 10).subscribe((response: string | undefined) => {
if (response && this.dataset != undefined) {
- this.tableData = this.csvParseService.csvToArray(response, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter.toString() == "") ? "," : this.dataset.delimiter);
+ this.tableData = this.csvParseService.csvToArray(response, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter);
}
});
this.loaded = true;
+
+ this.updateCharts();
+ this.updatePieChart();
}
ngAfterViewInit(): void {
+ console.log(this.dataset?.columnInfo);
}
@@ -89,7 +124,10 @@ export class ColumnTableComponent implements AfterViewInit {
}
}
resetOutputColumn() {
- this.experiment.outputColumn = this.experiment.inputColumns[0];
+ if (this.experiment.inputColumns.length > 0)
+ this.experiment.outputColumn = this.experiment.inputColumns[0];
+ else
+ this.experiment.outputColumn = '-';
}
setDeleteRowsForMissingValTreatment() {
@@ -112,7 +150,7 @@ export class ColumnTableComponent implements AfterViewInit {
columnTypeChanged(columnName: string) {
if (this.experiment.outputColumn == columnName)
- this.changeOutputColumn(columnName);
+ this.changeProblemType();
else
this.columnTableChangeDetected();
}
@@ -124,6 +162,8 @@ export class ColumnTableComponent implements AfterViewInit {
if (this.experiment.inputColumns.filter(x => x == columnName)[0] == undefined) {
this.experiment.inputColumns.push(columnName);
}
+ if (this.experiment.inputColumns.length == 1)
+ this.experiment.outputColumn = this.experiment.inputColumns[0];
}
else {
this.experiment.inputColumns = this.experiment.inputColumns.filter(x => x != columnName);
@@ -131,17 +171,21 @@ export class ColumnTableComponent implements AfterViewInit {
//TODO: da se zatamni kolona koja je unchecked
//this.experiment.encodings = this.experiment.encodings.filter(x => x.columnName != columnName); samo na kraju iz enkodinga skloni necekirane
this.experiment.nullValuesReplacers = this.experiment.nullValuesReplacers.filter(x => x.column != columnName);
- if (columnName == this.experiment.outputColumn)
- this.experiment.outputColumn = this.experiment.inputColumns[0];
+ if (columnName == this.experiment.outputColumn) {
+ if (this.experiment.inputColumns.length > 0)
+ this.experiment.outputColumn = this.experiment.inputColumns[0];
+ else
+ this.experiment.outputColumn = '-';
+ }
}
this.columnTableChangeDetected();
}
}
- changeOutputColumn(columnName: string) {
+ changeProblemType() {
if (this.experiment != undefined && this.dataset != undefined) {
let i = this.dataset.columnInfo.findIndex(x => x.columnName == this.experiment!.outputColumn);
- if (this.experiment.columnTypes[i] == ColumnType.numerical) {
+ if (i == -1 || this.experiment.columnTypes[i] == ColumnType.numerical) {
this.experiment.type = ProblemType.Regression;
}
else {
@@ -157,20 +201,30 @@ export class ColumnTableComponent implements AfterViewInit {
resetColumnEncodings(encodingType: Encoding) {
if (this.experiment != undefined && this.dataset != undefined) {
this.experiment.encodings = [];
- for (let i = 0; i < this.dataset?.columnInfo.length; i++) {
+ for (let i = 0; i < this.dataset.columnInfo.length; i++) {
this.experiment.encodings.push(new ColumnEncoding(this.dataset?.columnInfo[i].columnName, encodingType));
//console.log(this.experiment.encodings);
}
this.columnTableChangeDetected();
}
}
+ resetColumnEncodingsGlobalSetting(encodingType: Encoding) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+ for (let i = 0; i < this.dataset.columnInfo.length; i++) {
+ if (this.experiment.columnTypes[i] == ColumnType.categorical && this.dataset.columnInfo[i].columnName != this.experiment.outputColumn) //promeni
+ this.experiment.encodings[i].encoding = encodingType;
+ }
+ this.columnTableChangeDetected();
+ }
+ }
openEncodingDialog() {
const dialogRef = this.dialog.open(EncodingDialogComponent, {
- width: '300px'
+ width: '400px',
+ data: { experiment: this.experiment, dataset: this.dataset }
});
dialogRef.afterClosed().subscribe(selectedEncoding => {
if (selectedEncoding != undefined)
- this.resetColumnEncodings(selectedEncoding);
+ this.resetColumnEncodingsGlobalSetting(selectedEncoding);
});
}
@@ -217,21 +271,24 @@ export class ColumnTableComponent implements AfterViewInit {
openSaveExperimentDialog() {
const dialogRef = this.dialog.open(SaveExperimentDialogComponent, {
- width: '400px'
+ width: '400px',
+ data: { experiment: this.experiment }
});
- dialogRef.afterClosed().subscribe(selectedName => {
- this.experiment.name = selectedName;
- //napravi odvojene dugmice za save i update -> za update nece da se otvara dijalog za ime
- this.experimentService.addExperiment(this.experiment).subscribe((response) => {
- this.experiment._id = response._id;
- this.okPressed.emit();
- });
+ dialogRef.afterClosed().subscribe(experiment => {
+ if (experiment) {
+ Object.assign(this.experiment, experiment);
+ this.experiment._columnsSelected = true;
+ this.experimentChanged.emit();
+ console.log(this.experiment);
+ }
});
}
openUpdateExperimentDialog() {
this.experimentService.updateExperiment(this.experiment).subscribe((response) => {
- this.experiment = response;
+ Object.assign(this.experiment, response);
+ this.experiment._columnsSelected = true;
+ this.experimentChanged.emit();
Shared.openDialog("Izmena eksperimenta", "Uspešno ste izmenili podatke o eksperimentu.");
});
}
@@ -286,10 +343,20 @@ export class ColumnTableComponent implements AfterViewInit {
return '0';
}
saveExperiment() {
- this.openSaveExperimentDialog();
+ if (this.experiment.inputColumns.length == 0)
+ Shared.openDialog("Upozorenje", "Kako bi eksperiment bio uspešno izveden, neophodno je da izaberete barem dve kolone koje ćete koristiti.");
+ else if (this.experiment.inputColumns.length == 1)
+ Shared.openDialog("Upozorenje", "Kako bi eksperiment bio uspešno izveden, neophodno je da izaberete barem dve kolone koje ćete koristiti (mora postojati bar jedna ulazna i jedna izlazna kolona).");
+ else
+ this.openSaveExperimentDialog();
}
updateExperiment() {
- this.openUpdateExperimentDialog();
+ if (this.experiment.inputColumns.length == 0)
+ Shared.openDialog("Upozorenje", "Kako bi eksperiment bio uspešno izveden, neophodno je da izaberete barem dve kolone koje ćete koristiti.");
+ else if (this.experiment.inputColumns.length == 1)
+ Shared.openDialog("Upozorenje", "Kako bi eksperiment bio uspešno izveden, neophodno je da izaberete barem dve kolone koje ćete koristiti (mora postojati bar jedna ulazna i jedna izlazna kolona).");
+ else
+ this.openUpdateExperimentDialog();
}
@@ -312,10 +379,8 @@ export class ColumnTableComponent implements AfterViewInit {
hoverOverTab(index: number) {
if (index < 0) {
this.hoveringOverTab = null;
- this.tabToDisplay = this.selectedTab.value;
} else {
this.hoveringOverTab = this.tabs[index];
- this.tabToDisplay = this.tabs[index].value;
}
}
@@ -344,3 +409,5 @@ export class Tab {
public value: Table
) { }
}
+
+
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
deleted file mode 100644
index 73dbf2d2..00000000
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { Component, OnInit, ViewChild, ViewChildren } from '@angular/core';
-import { AddNewDatasetComponent } from '../add-new-dataset/add-new-dataset.component';
-import { ModelsService } from 'src/app/_services/models.service';
-import shared from 'src/app/Shared';
-import Dataset from 'src/app/_data/Dataset';
-import { DatatableComponent, TableData } from 'src/app/_elements/datatable/datatable.component';
-import { DatasetsService } from 'src/app/_services/datasets.service';
-import { CsvParseService } from 'src/app/_services/csv-parse.service';
-import { Output, EventEmitter } from '@angular/core';
-import { SignalRService } from 'src/app/_services/signal-r.service';
-
-@Component({
- selector: 'app-dataset-load',
- templateUrl: './dataset-load.component.html',
- styleUrls: ['./dataset-load.component.css']
-})
-export class DatasetLoadComponent implements OnInit {
-
- @Output() selectedDatasetChangeEvent = new EventEmitter<Dataset>();
-
- @ViewChild(AddNewDatasetComponent) addNewDatasetComponent!: AddNewDatasetComponent;
- @ViewChild(AddNewDatasetComponent) datatable!: DatatableComponent;
-
- datasetLoaded: boolean = false;
- selectedDatasetLoaded: boolean = false;
-
- showMyDatasets: boolean = true;
- myDatasets?: Dataset[];
- existingDatasetSelected: boolean = false;
- selectedDataset?: Dataset;
-
- tableData: TableData = new TableData();
-
- term: string = "";
-
- constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService) {
- this.datasets.getMyDatasets().subscribe((datasets) => {
- this.myDatasets = datasets;
- });
- }
-
- viewMyDatasetsForm() {
- this.showMyDatasets = true;
- if (this.selectedDataset != undefined)
- this.resetSelectedDataset();
- //this.resetCbsAndRbs(); //TREBA DA SE DESI
- }
- viewNewDatasetForm() {
- this.showMyDatasets = false;
- if (this.selectedDataset != undefined)
- this.resetSelectedDataset();
- //this.resetCbsAndRbs(); //TREBA DA SE DESI
- }
-
- refreshMyDatasets() {
- this.datasets.getMyDatasets().subscribe((datasets) => {
- this.myDatasets = datasets;
- this.showMyDatasets = true;
- });
- }
-
- selectThisDataset(dataset: Dataset) {
- this.selectedDataset = dataset;
- this.selectedDatasetLoaded = false;
- this.existingDatasetSelected = true;
- this.tableData.hasHeader = this.selectedDataset.hasHeader;
-
- this.tableData.hasInput = true;
- this.tableData.loaded = false;
-
- this.datasets.getDatasetFile(dataset.fileId).subscribe((file: string | undefined) => {
- if (file) {
- this.tableData.loaded = true;
- this.tableData.numRows = this.selectedDataset!.rowCount;
- this.tableData.numCols = this.selectedDataset!.columnInfo.length;
- this.tableData.data = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter == "") ? "," : dataset.delimiter);
- //this.resetCbsAndRbs(); //TREBA DA SE DESI
- //this.refreshThreeNullValueRadioOptions(); //TREBA DA SE DESI
- this.selectedDatasetLoaded = true;
-
- this.selectedDatasetChangeEvent.emit(this.selectedDataset);
- }
- });
- }
-
- resetSelectedDataset(): boolean {
- this.selectedDatasetChangeEvent.emit(this.selectedDataset);
- return true;
- }
-
- ngOnInit(): void {
- if (this.signalRService.hubConnection) {
- this.signalRService.hubConnection.on("NotifyDataset", _ => {
- this.refreshMyDatasets();
- });
- } else {
- console.warn("Dataset-Load: No connection!");
- }
- }
-}
diff --git a/frontend/src/app/_elements/folder/folder.component.css b/frontend/src/app/_elements/folder/folder.component.css
index 62324d62..ada2dba0 100644
--- a/frontend/src/app/_elements/folder/folder.component.css
+++ b/frontend/src/app/_elements/folder/folder.component.css
@@ -210,4 +210,13 @@
.form-hidden {
display: none;
+}
+
+.predictor {
+ text-decoration: underline;
+}
+
+.highlight-exp {
+ /*font-size: 16px;*/
+ font-weight: 700;
} \ No newline at end of file
diff --git a/frontend/src/app/_elements/folder/folder.component.html b/frontend/src/app/_elements/folder/folder.component.html
index bff066be..8896e7e5 100644
--- a/frontend/src/app/_elements/folder/folder.component.html
+++ b/frontend/src/app/_elements/folder/folder.component.html
@@ -67,17 +67,38 @@
<app-form-dataset [ngClass]="{'form-hidden': type != FolderType.Dataset}" [forExperiment]="forExperiment"></app-form-dataset>
</div>
<div [ngClass]="{'form-hidden' : !listView}" class="list-view">
- <div *ngFor="let file of filteredFiles; let i = index" class="list-item force-link" (click)="selectFile(file)">
- <div class="mx-2">
- {{file.name}}
+ <div *ngFor="let file of filteredFiles; let i = index">
+ <div class="list-item force-link" (click)="selectFile(file)">
+ <div class="mx-2" [ngClass]="{'highlight-exp' : selectedTab == TabType.MyExperiments}">
+ {{file.name}}
+ </div>
+ <div class="mx-2 hover-hide">
+ {{file.lastUpdated | date}}
+ </div>
+ <div class="mx-2 hover-show" *ngIf="selectedTab !== TabType.PublicDatasets && selectedTab !== TabType.PublicModels">
+ <button class="btn-clear file-button" (click)="deleteFile(file, $event)">
+ <mat-icon>delete</mat-icon>
+ </button>
+ </div>
</div>
- <div class="mx-2 hover-hide">
- {{file.lastUpdated | date}}
- </div>
- <div class="mx-2 hover-show" *ngIf="selectedTab !== TabType.PublicDatasets && selectedTab !== TabType.PublicModels">
- <button class="btn-clear file-button" (click)="deleteFile(file, $event)">
- <mat-icon>delete</mat-icon>
- </button>
+ <div *ngIf="type == FolderType.Experiment" class="list-view">
+ <div *ngFor="let predictor of predictorsForExp[file._id];" class="list-item">
+
+ <div class="mx-3">
+ <div class="f-row">
+ <mat-icon>subdirectory_arrow_right</mat-icon>
+ <div class="mx-1">{{predictor.name}}</div>
+ </div>
+ </div>
+ <div class="mx-2 hover-hide">
+ {{predictor.lastUpdated | date}}
+ </div>
+ <div class="mx-2 hover-show">
+ <button class="btn-clear file-button" (click)="deleteFile(predictor, $event)">
+ <mat-icon>delete</mat-icon>
+ </button>
+ </div>
+ </div>
</div>
</div>
diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts
index fabb524c..665659a8 100644
--- a/frontend/src/app/_elements/folder/folder.component.ts
+++ b/frontend/src/app/_elements/folder/folder.component.ts
@@ -11,6 +11,8 @@ import { ExperimentsService } from 'src/app/_services/experiments.service';
import { PredictorsService } from 'src/app/_services/predictors.service';
import { SignalRService } from 'src/app/_services/signal-r.service';
import { FormModelComponent } from '../form-model/form-model.component';
+import { Router } from '@angular/router';
+import Predictor from 'src/app/_data/Predictor';
@Component({
selector: 'app-folder',
@@ -30,7 +32,7 @@ export class FolderComponent implements AfterViewInit {
@Input() type: FolderType = FolderType.Dataset;
@Input() forExperiment!: Experiment;
@Input() startingTab!: TabType;
-
+ @Input() archive: boolean = false;
newFileSelected: boolean = true;
selectedFileIndex: number = -1;
@@ -44,7 +46,7 @@ export class FolderComponent implements AfterViewInit {
searchTerm: string = '';
- constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService, private signalRService: SignalRService) {
+ constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService, private signalRService: SignalRService, private router: Router) {
this.tabsToShow.forEach(tab => this.folders[tab] = []);
}
@@ -91,20 +93,23 @@ export class FolderComponent implements AfterViewInit {
this.newFileSelected = true;
this.listView = false;
this.displayFile();
- if(this.type == FolderType.Dataset)
+ if (this.type == FolderType.Dataset)
this.formDataset.clear();
}
selectFile(file?: FolderFile) {
this.selectedFile = file;
this.fileToDisplay = file;
+ if (this.type == FolderType.Experiment && file) {
+ this.router.navigate(['/experiment'/*, file._id*/])
+ }
this.newFileSelected = false;
this.listView = false;
this.selectedFileChanged.emit(this.selectedFile);
this.selectTab(TabType.File);
this.displayFile();
- if(this.type == FolderType.Dataset)
+ if (this.type == FolderType.Dataset)
this.formDataset.loadExisting();
}
@@ -122,7 +127,7 @@ export class FolderComponent implements AfterViewInit {
_initialized: boolean = false;
- refreshFiles(selectedDatasetId: string | null) {
+ refreshFiles(selectedDatasetId: string | null = null, selectedModelId: string | null = null) {
this.files = []
this.filteredFiles.length = 0;
this.folders[TabType.NewFile] = [];
@@ -131,42 +136,85 @@ export class FolderComponent implements AfterViewInit {
this.folders[tab] = [];
});
- this.datasetsService.getMyDatasets().subscribe((datasets) => {
- this.folders[TabType.MyDatasets] = datasets;
- if (selectedDatasetId) {
- this.selectFile(datasets.filter(x => x._id == selectedDatasetId)[0]);
+ if (this.archive) {
+ this.refreshDatasets(selectedDatasetId);
+ this.refreshModels(selectedModelId);
+ this.refreshExperiments();
+ } else {
+ switch (this.type) {
+ case FolderType.Dataset:
+ this.refreshDatasets(selectedDatasetId);
+ break;
+
+ case FolderType.Model:
+ this.refreshModels(selectedModelId);
+ break;
+
+ case FolderType.Experiment:
+ this.refreshExperiments();
+ break;
+
+ default:
+ console.error("Bad folder type.");
+ break;
}
- });
-
- this.experimentsService.getMyExperiments().subscribe((experiments) => {
- this.folders[TabType.MyExperiments] = experiments;
- });
+ }
- this.datasetsService.getPublicDatasets().subscribe((datasets) => {
- this.folders[TabType.PublicDatasets] = datasets;
- });
+ if (!this._initialized) {
+ this.files = this.folders[this.startingTab];
+ this.filteredFiles = [];
+ this.selectTab(this.startingTab);
+ this._initialized = true;
+ }
+ }
+ refreshModels(selectedModelId: string | null) {
this.modelsService.getMyModels().subscribe((models) => {
this.folders[TabType.MyModels] = models;
+ if (selectedModelId) {
+ this.selectFile(models.filter(x => x._id == selectedModelId)[0]);
+ }
+ this.searchTermsChanged();
});
-
/*this.modelsService.getMyModels().subscribe((models) => {
this.folders[TabType.PublicModels] = models;
+ this.searchTermsChanged();
});*/
this.folders[TabType.PublicModels] = [];
+ }
+ refreshDatasets(selectedDatasetId: string | null) {
+ this.datasetsService.getMyDatasets().subscribe((datasets) => {
+ this.folders[TabType.MyDatasets] = datasets;
+ if (selectedDatasetId) {
+ this.selectFile(datasets.filter(x => x._id == selectedDatasetId)[0]);
+ }
+ this.searchTermsChanged();
+ });
+ this.datasetsService.getPublicDatasets().subscribe((datasets) => {
+ this.folders[TabType.PublicDatasets] = datasets;
+ this.searchTermsChanged();
+ });
+ }
+
+ refreshExperiments() {
this.experimentsService.getMyExperiments().subscribe((experiments) => {
this.folders[TabType.MyExperiments] = experiments;
+ this.predictorsService.getMyPredictors().subscribe((predictors) => {
+ this.predictorsForExp = {};
+ experiments.forEach(exp => {
+ this.predictorsForExp[exp._id] = predictors.filter(pred => pred.experimentId == exp._id);
+ /* TODO IZMENI OVO DA SE SETUJE NA BACKU AUTOMATSKI */
+ this.predictorsForExp[exp._id].forEach(pred => {
+ const model = this.folders[TabType.MyModels].find(model => model._id == pred.modelId);
+ pred.name = model?.name!;
+ pred.lastUpdated = model?.lastUpdated!;
+ })
+ /* ------------------------------------------------ */
+ this.searchTermsChanged();
+ })
+ });
});
-
- if (!this._initialized) {
- this.files = this.folders[this.startingTab];
- this.filteredFiles = [];
- this.selectTab(this.startingTab);
- this._initialized = true;
- }
-
- this.searchTermsChanged();
}
saveNewFile() {
@@ -175,7 +223,7 @@ export class FolderComponent implements AfterViewInit {
this.formDataset!.uploadDataset((dataset: Dataset) => {
this.newFile = undefined;
Shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira.");
- this.refreshFiles(null);
+ this.refreshFiles();
},
() => {
Shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset.");
@@ -185,7 +233,7 @@ export class FolderComponent implements AfterViewInit {
this.modelsService.addModel(this.formModel.newModel).subscribe(model => {
this.newFile = undefined;
Shared.openDialog("Obaveštenje", "Uspešno ste dodali novu konfiguraciju neuronske mreže u kolekciju.");
- this.refreshFiles(null); // todo select model
+ this.refreshFiles(null, model._id); // todo select model
}, (err) => {
Shared.openDialog("Neuspeo pokušaj!", "Konfiguracija neuronske mreže sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeću konfiguraciju.");
});
@@ -193,19 +241,7 @@ export class FolderComponent implements AfterViewInit {
}
}
-
- /*calcZIndex(i: number) {
- let zIndex = (this.files.length - i - 1)
- if (this.selectedFileIndex == i)
- zIndex = this.files.length + 2;
- if (this.hoveringOverFileIndex == i)
- zIndex = this.files.length + 3;
- return zIndex;
- }
-
- newFileZIndex() {
- return (this.files.length + 1);
- }*/
+ predictorsForExp: { [expId: string]: Predictor[] } = {}
clearSearchTerm() {
this.searchTerm = '';
@@ -232,10 +268,6 @@ export class FolderComponent implements AfterViewInit {
listView: boolean = true;
- toggleListView() {
- this.listView = !this.listView;
- }
-
deleteFile(file: FolderFile, event: Event) {
event.stopPropagation();
//console.log('delete');
@@ -349,16 +381,22 @@ export class FolderComponent implements AfterViewInit {
}
hoverOverTab(tab: TabType) {
- this.listView = this.getListView(tab);
- this.privacy = this.getPrivacy(tab);
+ // this.listView = this.getListView(tab);
+ // this.privacy = this.getPrivacy(tab);
this.hoverTab = tab;
- if (tab == TabType.None) {
- this.listView = this.getListView(this.selectedTab);
- this.files = this.folders[this.selectedTab];
- } else {
- this.files = this.folders[tab];
+ // if (tab == TabType.None) {
+ // this.listView = this.getListView(this.selectedTab);
+ // this.files = this.folders[this.selectedTab];
+ // } else {
+ // this.files = this.folders[tab];
+ // }
+ // this.searchTermsChanged();
+ }
+
+ updateExperiment() {
+ if (this.formModel) {
+ this.formModel.updateGraph();
}
- this.searchTermsChanged();
}
}
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.css b/frontend/src/app/_elements/form-dataset/form-dataset.component.css
index 953daa0c..079711d0 100644
--- a/frontend/src/app/_elements/form-dataset/form-dataset.component.css
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.css
@@ -5,25 +5,27 @@
}
.topBar {
+ display: table;
width: 100%;
- margin: 1rem;
- align-items: flex-start;
+ height: 100px;
+ margin-left: 1.5%;
+ margin-top: 1.5% ;
+}
+.kolona{
+ float: left;
+ margin-left: 20px;
}
-
.topBar label{
font-size: 30px;
}
.topBar mat-form-field{
width: 250px;
+ margin: 0;
}
-.toptop{
- margin-left: 1.5%;
- width: 50%;
-}
-
-.fileButton{
- margin-top: 10px;
+.fileButton button{
+ height: 51px;
+ width: 250px;
}
.file-container {
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.html b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
index 281f9c05..b96276bd 100644
--- a/frontend/src/app/_elements/form-dataset/form-dataset.component.html
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
@@ -1,18 +1,16 @@
<div class="folderBox" *ngIf="dataset">
- <div class="row" style="margin-right: 0;">
+
<div class="topBar">
- <div class="row toptop">
- <div class="col-sm mb-3">
+ <div class="kolona mb-3">
<div class="fileButton">
<button type="button" mat-raised-button (click)="fileInput.click()">Dodaj izvor podataka</button>
</div>
</div>
- <div class="col-sm">
+ <div class="kolona">
<div role="group">
- <div class="row">
<mat-form-field class="example-full-width" appearance="fill">
<mat-label>Naziv</mat-label>
<input type="text" matInput value="{{dataset?.name}}" [(ngModel)]="dataset.name">
@@ -22,30 +20,24 @@
Naziv je <strong>obavezan</strong>
</mat-error>
</mat-form-field>
- </div>
</div>
</div>
- <div class="col-sm">
+ <div class="kolona">
<mat-form-field appearance="fill">
<mat-label>Delimiter</mat-label>
- <mat-select id="delimiterOptions" [(ngModel)]="dataset.delimiter" (change)="update()" value=",">
+ <mat-select id="delimiterOptions" [(ngModel)]="dataset.delimiter" (selectionChange)="update()" value=",">
<mat-option *ngFor="let option of delimiterOptions" [value]="option">
{{ option }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
- <div class="col-sm">
-
- </div>
- </div>
- <div class="row" *ngIf="firstInput">
- <label class=" mt-5">{{filename}}</label>
-
- </div>
</div>
- </div>
+
+ <div class="row" *ngIf="firstInput">
+ <label class=" mt-5">{{filename}}</label>
+ </div>
<div class="row" style="margin-right: 0;">
<div class="file-container" [ngClass]="{'dottedClass': !tableData.hasInput}">
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.ts b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
index 1eed2cdc..94ef9905 100644
--- a/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
@@ -101,7 +101,7 @@ export class FormDatasetComponent {
this.tableData.loaded = true;
this.tableData.numRows = this.dataset.rowCount;
this.tableData.numCols = this.dataset.columnInfo.length;
- this.tableData.data = this.csv.csvToArray(file, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter);
+ this.tableData.data = this.csv.csvToArray(file, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter);
}
});
diff --git a/frontend/src/app/_elements/form-model/form-model.component.css b/frontend/src/app/_elements/form-model/form-model.component.css
index 11b6ef5e..95ace1ef 100644
--- a/frontend/src/app/_elements/form-model/form-model.component.css
+++ b/frontend/src/app/_elements/form-model/form-model.component.css
@@ -101,4 +101,4 @@ mat-slider {
padding-bottom: 15px;
font-size: 20px !important;
font-weight: 600;
-} \ No newline at end of file
+}
diff --git a/frontend/src/app/_elements/form-model/form-model.component.html b/frontend/src/app/_elements/form-model/form-model.component.html
index 96a5e1b6..09e44a99 100644
--- a/frontend/src/app/_elements/form-model/form-model.component.html
+++ b/frontend/src/app/_elements/form-model/form-model.component.html
@@ -11,7 +11,12 @@
<div class="ns-col">
<mat-form-field appearance="fill" class="mat-fix">
<mat-label>Tip problema</mat-label>
- <mat-select [(ngModel)]="newModel.type">
+ <mat-select [(ngModel)]="newModel.type" (selectionChange)="filterLossFunction()" *ngIf="this.hideProblemType" disabled="true">
+ <mat-option *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ <mat-select [(ngModel)]="newModel.type" (selectionChange)="filterLossFunction()" *ngIf="!this.hideProblemType" disabled="false">
<mat-option *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" [value]="option">
{{ optionName }}
</mat-option>
@@ -36,7 +41,7 @@
<mat-form-field appearance="fill" class="mat-fix">
<mat-label>Funkcija troška</mat-label>
<mat-select [(ngModel)]="newModel.lossFunction">
- <mat-option *ngFor="let option of Object.keys(LossFunction); let optionName of Object.values(LossFunction)" [value]="option">
+ <mat-option *ngFor="let option of Object.keys(lossFunction); let optionName of Object.values(lossFunction)" [value]="option">
{{ optionName }}
</mat-option>
</mat-select>
@@ -109,7 +114,7 @@
<div class="col-sm-9">
<!-- {{forExperiment._columnsSelected}} -->
- <app-graph [model]="newModel" *ngIf="forExperiment._columnsSelected" [inputColumns]="forExperiment.inputColumns"></app-graph>
+ <app-graph [model]="newModel" *ngIf="forExperiment._columnsSelected" [inputColumns]="getInputColumns()"></app-graph>
<app-graph [model]="newModel" *ngIf="!forExperiment._columnsSelected" [inputColumns]="['Nisu odabrane ulazne kolone']"></app-graph>
</div>
</div>
@@ -121,14 +126,15 @@
<div class="ns-col" id="layers-control">
<div>Broj Skrivenih Slojeva</div>
- <button class="btn-clear btn-icon bubble" (click)="addLayer()">
- <mat-icon>add</mat-icon>
- </button>
- <div>{{newModel.hiddenLayers}}</div>
<button class="btn-clear btn-icon bubble" (click)="removeLayer()">
- <mat-icon>remove</mat-icon>
- </button>
+ <mat-icon>remove</mat-icon>
+ </button>
+ <div>{{newModel.hiddenLayers}}</div>
+
+ <button class="btn-clear btn-icon bubble" (click)="addLayer()">
+ <mat-icon>add</mat-icon>
+ </button>
</div>
<div class="break-1"></div>
<div class="ns-col">
@@ -196,13 +202,13 @@
<div class="d-flex flex-row align-items-center justify-content-center tm">
<div class="col-6" style="font-size: 13px;">Broj čvorova</div>
- <button class="btn-clear btn-icon bubble" (click)="addNeuron(i)">
- <mat-icon>add</mat-icon>
- </button>
- <div class="col-2 text-center">{{newModel.layers[i].neurons}}</div>
<button class="btn-clear btn-icon bubble" (click)="removeNeuron(i)">
- <mat-icon>remove</mat-icon>
- </button>
+ <mat-icon>remove</mat-icon>
+ </button>
+ <div class="col-2 text-center">{{newModel.layers[i].neurons}}</div>
+ <button class="btn-clear btn-icon bubble" (click)="addNeuron(i)">
+ <mat-icon>add</mat-icon>
+ </button>
</div>
<mat-form-field appearance="fill" class="mat-fix">
diff --git a/frontend/src/app/_elements/form-model/form-model.component.ts b/frontend/src/app/_elements/form-model/form-model.component.ts
index 71b374b0..e01c2339 100644
--- a/frontend/src/app/_elements/form-model/form-model.component.ts
+++ b/frontend/src/app/_elements/form-model/form-model.component.ts
@@ -15,8 +15,13 @@ export class FormModelComponent implements AfterViewInit {
@ViewChild(GraphComponent) graph!: GraphComponent;
@Input() forExperiment!: Experiment;
@Output() selectedModelChangeEvent = new EventEmitter<Model>();
+ @Input() hideProblemType:boolean;
+ @Input() forProblemType:ProblemType;
testSetDistribution: number = 70;
- constructor() { }
+ constructor() {
+ this.hideProblemType=false;
+ this.forProblemType=ProblemType.BinaryClassification;
+ }
ngAfterViewInit(): void { }
@@ -80,13 +85,6 @@ export class FormModelComponent implements AfterViewInit {
}
}
- /*
- setNeurons()
- {
- for(let i=0;i<this.newModel.hiddenLayers;i++){
- this.newModel.hiddenLayerNeurons[i]=1;
- }
- }*/
numSequence(n: number): Array<number> {
return Array(n);
}
@@ -111,9 +109,7 @@ export class FormModelComponent implements AfterViewInit {
changeAllActivation() {
for (let i = 0; i < this.newModel.layers.length; i++) {
this.newModel.layers[i].activationFunction = this.selectedActivation;
-
}
-
}
changeAllRegularisation() {
for (let i = 0; i < this.newModel.layers.length; i++) {
@@ -131,8 +127,25 @@ export class FormModelComponent implements AfterViewInit {
this.updateGraph();
}
}
-
updateTestSet(event: MatSliderChange) {
this.testSetDistribution = event.value!;
}
+ filterLossFunction() {
+ if(this.newModel.type==ProblemType.Regression){
+ this.lossFunction = LossFunctionRegression;
+ this.newModel.lossFunction=LossFunction.MeanSquaredError;
+ }
+ else if(this.newModel.type==ProblemType.BinaryClassification){
+ this.lossFunction= LossFunctionBinaryClassification;
+ this.newModel.lossFunction=LossFunction.BinaryCrossEntropy;
+ }
+ else if(this.newModel.type==ProblemType.MultiClassification){
+ this.lossFunction = LossFunctionMultiClassification;
+ this.newModel.lossFunction=LossFunction.SparseCategoricalCrossEntropy;
+ }
+
+}
+getInputColumns() {
+ return this.forExperiment.inputColumns.filter(x => x != this.forExperiment.outputColumn);
+}
}
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.css b/frontend/src/app/_elements/metric-view/metric-view.component.css
index f91c1ccc..d27418c3 100644
--- a/frontend/src/app/_elements/metric-view/metric-view.component.css
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.css
@@ -1,10 +1,23 @@
-#container{
+.row{
+ margin: 0 !important;
+ padding: 0 !important;
+}
+.container{
width: 100%;
height: 90%;
border-radius: 5px;
- background-color:var(--ns-primary-25);
- border:1px solid var(--ns-accent);
+ border:1px solid var(--ns-primary);
+ background-color: rgba(0, 65, 101, 0.7);
+ margin-top: 20px;
+ margin: 0 !important;
+ padding: 0 !important;
+ width: 100%;
+ height: 100%;
+}
+app-line-chart{
+ width: 100%;
+ height: 100%;
+ border-radius: 5px;
+ border:1px solid var(--ns-primary);
+ background-color: rgba(0, 65, 101, 0.7);
}
-#line{
- background-color:#dfd7d7f0 ;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.html b/frontend/src/app/_elements/metric-view/metric-view.component.html
index d72bc92b..2ab0a425 100644
--- a/frontend/src/app/_elements/metric-view/metric-view.component.html
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.html
@@ -1,8 +1,21 @@
-<div id="container" class="d-flex justify-content-center flex-row w-100">
- <div id="line" style="width: 100%;height: 100%;background-color:var(--ns-bg-dark-100);">
- <app-line-chart></app-line-chart>
+ <!--<div class="container" style="margin:auto;">
+ <div class="row">
+ <div class="col-xl">
+ <div class="demo-content"><app-line-chart-acc [dataAcc]="myAcc" [dataEpoch]=""[dataValAcc]=""></app-line-chart-acc></div>
+ </div>
+ <div class="col-xl">
+ <div class="demo-content"><app-line-chart-loss></app-line-chart-loss></div>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xl">
+ <div class="demo-content"><app-line-chart-mae></app-line-chart-mae></div>
</div>
- <div style="background-color: var(--ns-bg-dark-100);width: 50%;height: 50%;">
- <app-scatterchart></app-scatterchart>
+ <div class="col-xl">
+ <div class="demo-content"><app-line-chart-mse></app-line-chart-mse></div>
</div>
-</div> \ No newline at end of file
+</div>
+</div>-->
+<app-line-chart></app-line-chart>
+
+
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.ts b/frontend/src/app/_elements/metric-view/metric-view.component.ts
index 6fd2f320..fbca2edf 100644
--- a/frontend/src/app/_elements/metric-view/metric-view.component.ts
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.ts
@@ -1,6 +1,5 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { LineChartComponent } from '../_charts/line-chart/line-chart.component';
-
@Component({
selector: 'app-metric-view',
templateUrl: './metric-view.component.html',
@@ -9,6 +8,7 @@ import { LineChartComponent } from '../_charts/line-chart/line-chart.component';
export class MetricViewComponent implements OnInit {
@ViewChild(LineChartComponent) linechartComponent!: LineChartComponent;
+
constructor() { }
ngOnInit(): void {
@@ -21,6 +21,10 @@ export class MetricViewComponent implements OnInit {
const myMae: number[] = [];
const myMse: number[] = [];
const myLoss: number[] = [];
+ const myValLoss: number[] = [];
+ const myValAcc: number[] = [];
+ const myValMAE: number[] = [];
+ const myValMSE: number[] = [];
const myEpochs: number[] = [];
this.history = history;
@@ -41,9 +45,21 @@ export class MetricViewComponent implements OnInit {
else if (key === 'mse') {
myMse.push(parseFloat(value));
}
+ else if (key === 'val_acc') {
+ myValAcc.push(parseFloat(value));
+ }
+ else if (key === 'val_loss') {
+ myValLoss.push(parseFloat(value));
+ }
+ else if (key === 'val_mae') {
+ myValMAE.push(parseFloat(value));
+ }
+ else if (key === 'val_mse') {
+ myValMSE.push(parseFloat(value));
+ }
}
});
- this.linechartComponent.update(myEpochs, myAcc, myLoss, myMae, myMse);
+ this.linechartComponent.update(myEpochs, myAcc, myLoss, myMae, myMse, myValAcc,myValLoss,myValMAE,myValMSE);
}
} \ No newline at end of file
diff --git a/frontend/src/app/_elements/model-load/model-load.component.html b/frontend/src/app/_elements/model-load/model-load.component.html
deleted file mode 100644
index dcb35c21..00000000
--- a/frontend/src/app/_elements/model-load/model-load.component.html
+++ /dev/null
@@ -1,215 +0,0 @@
-<div>
- <div class="d-flex flex-row justify-content-center align-items-center mt-3 mb-5">
- <button type="button" id="btnMyModel" class="btn" (click)="viewMyModelsForm()"
- [ngClass]="{'btnType1': showMyModels, 'btnType2': !showMyModels}">
- Izaberite model iz kolekcije
- </button>
- <h3 class="mt-3 mx-3">ili</h3>
- <button type="button" id="btnNewModel" class="btn" (click)="viewNewModelForm()"
- [ngClass]="{'btnType1': !showMyModels, 'btnType2': showMyModels}">
- Dodajte novi model
- </button>
- </div>
-
- <div *ngIf="showMyModels" class="px-5 my-3">
- <input *ngIf="showMyModels" type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term">
- </div>
- <div *ngIf="showMyModels" class="px-5">
- <div class="overflow-auto" style="max-height: 500px;">
- <ul class="list-group">
- <li class="list-group-item p-3" *ngFor="let model of myModels|filter:term|filter:(forExperiment ? forExperiment.type : '')"
- [ngClass]="{'selectedModelClass': this.selectedModel == model}">
- <app-item-model name="usersModel" [model]="model" (click)="selectThisModel(model);">
- </app-item-model>
- </li>
- </ul>
- <div class="px-5 mt-5">
- <!--prikaz izabranog modela-->
- </div>
- </div>
- </div>
-
-
- <div *ngIf="!showMyModels">
- <div class="form-group row mt-3 mb-2 d-flex justify-content-center">
-
- <div class="col-3">
- <label for="name" class="col-form-label">Naziv modela:</label>
- <input type="text" class="form-control" name="name" placeholder="Naziv..." [(ngModel)]="newModel.name">
- </div>
- <div class="col-5">
- <label for="desc" class="col-sm-2 col-form-label">Opis:</label>
- <div>
- <textarea class="form-control" name="desc" rows="3" [(ngModel)]="newModel.description"></textarea>
- </div>
- </div>
-
- </div>
- <h2 class="mt-5 mb-4 mx-5">Parametri treniranja modela:</h2>
- <div>
-
- <div class="row p-2">
- <div class="col-1"></div>
- <div class="col-3">
- <label for="type" class="col-form-label">Tip problema: </label>
- </div>
- <div class="col-2">
- <select id=typeOptions class="form-select" name="type" [(ngModel)]="newModel.type"
- (change)="filterOptions()">
- <option
- *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)"
- [value]="option">
- {{ optionName }}
- </option>
- </select>
- </div>
- <div class="col-1"></div>
- <div class="col-3">
- <label for="hiddenLayers" class="col-form-label">Broj skrivenih slojeva: </label>
- </div>
- <div class="col-1">
- <input type="number" min="1" class="form-control" name="hiddenLayers"
- [(ngModel)]="newModel.hiddenLayers"
- (change)="newModel.hiddenLayerActivationFunctions = [].constructor(newModel.hiddenLayers).fill(newModel.hiddenLayerActivationFunctions[0])"
- (ngModelChange)="updateGraph()">
- </div>
- </div>
-
- <div class="row p-2">
- <div class="col-1">
- </div>
- <div class="col-3">
- <label for="optimizer" class="col-form-label">Optimizacija: </label>
- </div>
- <div class="col-2">
- <select id=optimizerOptions class="form-select" name="optimizer" [(ngModel)]="newModel.optimizer">
- <option
- *ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)"
- [value]="option">
- {{ optionName }}
- </option>
- </select>
- </div>
- <div class="col-1">
- </div>
- <div class="col-3">
- <label for="hiddenLayerNeurons" class="col-form-label">Broj neurona skrivenih slojeva: </label>
- </div>
- <div class="col-1">
- <input type="number" min="1" class="form-control" name="hiddenLayerNeurons"
- [(ngModel)]="newModel.hiddenLayerNeurons" (ngModelChange)="updateGraph()">
- </div>
- </div>
-
- <div class="row p-2">
- <div class="col-1"></div>
- <div class="col-3">
- <label for="lossFunction" class="col-form-label">Funkcija troška: </label>
- </div>
- <div class="col-2">
- <select id=lossFunctionOptions class="form-select" name="lossFunction"
- [(ngModel)]="newModel.lossFunction" aria-checked="true">
- <option
- *ngFor="let option of Object.keys(lossFunction); let optionName of Object.values(lossFunction)"
- [value]="option">
- {{ optionName }}
- </option>
- </select>
- </div>
- <div class="col-1"></div>
- <div class="col-3">
- <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label>
- </div>
- <div class="col-1">
-
- <input type="number" min="0" step="1" max="7" class="form-control" name="batchSizePower" [(ngModel)]="batchSizePower" (click)="updateBatchSize()" >
- {{newModel.batchSize}}
-
- </div>
-
- <div class="row p-2">
- <div class="col-1"></div>
- <div class="col-3 m-1">
- <label for="epochs" class="col-form-label">Broj epoha: </label>
- </div>
- <div class="col-1">
- <input type="number" min="1" max="1000" class="form-control" name="epochs"
- [(ngModel)]="newModel.epochs">
- </div>
- </div>
- </div>
-
- <div class="m-5">
- <app-graph [model]="newModel" [inputCols]="1"></app-graph>
- </div>
-
- <h3 class="mx-5 mt-4">Aktivacione funkcije:</h3>
-
- <div class="row p-2" style="align-self: center;">
- <div class="col-1"></div>
- <div class="col-3">
- <label for="hiddenLayerActivationFunction" class="col-form-label"
- style="text-align: center;">Funkcija aktivacije<br>skrivenih slojeva:</label>
- </div>
- <div class="col-2 mt-2">
- <div *ngFor="let item of [].constructor(newModel.hiddenLayers); let i = index">
- <div class="input-group mb-2">
- <div class="input-group-prepend">
- <span class="input-group-text">#{{i+1}}</span>
- </div>
- <select [id]="'hiddenLayerActivationFunctionOption_'+i" class="form-select"
- [(ngModel)]="newModel.hiddenLayerActivationFunctions[i]" >
- <option
- *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)"
- [value]="option">
- {{ optionName }}
- </option>
- </select>
- </div>
- </div>
- </div>
- <div class="col-1"></div>
- <div class="col-2">
- <label for="outputLayerActivationFunction" class="col-form-label"
- style="text-align: center;">Funkcija aktivacije<br>izlaznog sloja:</label>
- </div>
- <div class="col-2 mt-2">
- <select id=outputLayerActivationFunctionOptions class="form-select"
- name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction">
- <option
- *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)"
- [value]="option">
- {{ optionName }}
- </option>
- </select>
- </div>
- <div class="col">
- </div>
- </div>
- </div>
-
- <div class="form-check form-check-inline overflow-auto m-4" style="width: max-content;">
- <h3>Izaberite metrike:</h3>
- <div id="divMetricsinput" class="mt-2 mx-5">
-
- <div *ngFor="let option of Object.keys(metrics); let optionName of Object.values(metrics) "
- class="form-check form-check-inline">
-
- <input name="cbmetrics" class="form-check-input" type="checkbox" value="{{option}}"
- id="metrics_{{option}}" style="float: left;" checked>
- <label class="form-check-label" for="metrics_{{option}}" for="inlineCheckbox2">
- {{optionName}}
- </label>
- </div>
- </div>
- </div>
-
- <div class="form-group row mt-3 mb-3">
- <div class="col"></div>
- <button class="btn btn-lg col-4" style="background-color:#003459; color:white;"
- (click)="uploadModel();">Sačuvaj
- model</button>
- <div class="col"></div>
- </div>
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/model-load/model-load.component.ts b/frontend/src/app/_elements/model-load/model-load.component.ts
deleted file mode 100644
index dbca3d17..00000000
--- a/frontend/src/app/_elements/model-load/model-load.component.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from '@angular/core';
-import Shared from 'src/app/Shared';
-import Experiment from 'src/app/_data/Experiment';
-import Model, { ActivationFunction, LossFunction, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType } from 'src/app/_data/Model';
-import { ModelsService } from 'src/app/_services/models.service';
-import { GraphComponent } from '../graph/graph.component';
-
-
-@Component({
- selector: 'app-model-load',
- templateUrl: './model-load.component.html',
- styleUrls: ['./model-load.component.css']
-})
-export class ModelLoadComponent implements OnInit {
-
- @ViewChild(GraphComponent) graph!: GraphComponent;
- @Input() forExperiment?:Experiment;
- @Output() selectedModelChangeEvent = new EventEmitter<Model>();
-
- newModel: Model = new Model();
- myModels?: Model[];
- selectedModel?: Model;
-
- ProblemType = ProblemType;
- ActivationFunction = ActivationFunction;
- metrics: any = Metrics;
- LossFunction = LossFunction;
- Optimizer = Optimizer;
- Object = Object;
- document = document;
- shared = Shared;
-
- term: string = "";
- selectedProblemType: string = '';
- selectedMetrics = [];
- lossFunction: any = LossFunction;
-
- showMyModels: boolean = true;
-
- constructor(private modelsService: ModelsService) {
- this.modelsService.getMyModels().subscribe((models) => {
- this.myModels = models;
- });
- }
-
- ngOnInit(): void {
- }
- batchSizePower:number=1;
- updateBatchSize()
- {
- this.newModel.batchSize=2**this.batchSizePower;
- }
-
- updateGraph() {
- this.graph.update();
- }
-
- getMetrics() {
- this.newModel.metrics = [];
- let cb = document.getElementsByName("cbmetrics");
-
- for (let i = 0; i < cb.length; i++) {
- let chb = <HTMLInputElement>cb[i];
- if (chb.checked == true)
- this.newModel.metrics.push(chb.value);
- }
- }
-
- uploadModel() {
- this.getMetrics();
-
- this.newModel.uploaderId = Shared.userId;
-
- this.modelsService.addModel(this.newModel).subscribe((response) => {
- Shared.openDialog('Model dodat', 'Model je uspešno dodat u bazu.');
- // treba da se selektuje nov model u listi modela
- //this.selectedModel =
- }, (error) => {
- Shared.openDialog('Greška', 'Model sa unetim nazivom već postoji u Vašoj kolekciji. Promenite naziv modela i nastavite sa kreiranim datasetom.');
- });
- }
-
- filterOptions() {
- switch (this.newModel.type) {
- case 'regresioni':
- this.lossFunction = LossFunctionRegression;
- this.metrics = MetricsRegression;
- break;
- case 'binarni-klasifikacioni':
- this.lossFunction = LossFunctionBinaryClassification;
- this.metrics = MetricsBinaryClassification;
- break;
- case 'multi-klasifikacioni':
- this.lossFunction = LossFunctionMultiClassification;
- this.metrics = MetricsMultiClassification;
- break;
- default:
- break;
- }
- }
-
- viewMyModelsForm() {
- this.showMyModels = true;
- }
- viewNewModelForm() {
- this.showMyModels = false;
- }
-
- selectThisModel(model: Model) {
- this.selectedModel = model;
- this.selectedModelChangeEvent.emit(this.selectedModel);
- }
-
-}