aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/app')
-rw-r--r--frontend/src/app/_data/Dataset.ts22
-rw-r--r--frontend/src/app/_data/Experiment.ts6
-rw-r--r--frontend/src/app/_data/FolderFile.ts3
-rw-r--r--frontend/src/app/_data/Model.ts7
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts8
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.html8
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts37
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css6
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html6
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts21
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.css63
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.html437
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.ts135
-rw-r--r--frontend/src/app/_elements/datatable/datatable.component.ts1
-rw-r--r--frontend/src/app/_elements/folder/folder.component.css18
-rw-r--r--frontend/src/app/_elements/folder/folder.component.html48
-rw-r--r--frontend/src/app/_elements/folder/folder.component.ts215
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.css61
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.html100
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.ts32
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.css17
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.html346
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.ts22
-rw-r--r--frontend/src/app/_elements/graph/graph.component.css7
-rw-r--r--frontend/src/app/_elements/graph/graph.component.html2
-rw-r--r--frontend/src/app/_elements/graph/graph.component.ts3
-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.html9
-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.html12
-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/_pages/experiment/experiment.component.css4
-rw-r--r--frontend/src/app/_pages/experiment/experiment.component.html17
-rw-r--r--frontend/src/app/_pages/experiment/experiment.component.ts46
-rw-r--r--frontend/src/app/_pages/home/home.component.css2
-rw-r--r--frontend/src/app/_pages/home/home.component.html3
-rw-r--r--frontend/src/app/_services/csv-parse.service.ts40
-rw-r--r--frontend/src/app/_services/datasets.service.ts4
-rw-r--r--frontend/src/app/_services/experiments.service.ts4
-rw-r--r--frontend/src/app/app.module.ts8
41 files changed, 1148 insertions, 688 deletions
diff --git a/frontend/src/app/_data/Dataset.ts b/frontend/src/app/_data/Dataset.ts
index e8207718..7ae5c4ab 100644
--- a/frontend/src/app/_data/Dataset.ts
+++ b/frontend/src/app/_data/Dataset.ts
@@ -5,7 +5,6 @@ export default class Dataset extends FolderFile {
constructor(
name: string = 'Novi izvor podataka',
public description: string = '',
- public header: string[] = [],
public fileId?: number,
public extension: string = '.csv',
public isPublic: boolean = false,
@@ -14,7 +13,6 @@ export default class Dataset extends FolderFile {
lastUpdated: Date = new Date(),
public uploaderId: string = '',
public delimiter: string = ',',
- public hasHeader: boolean = true,
public columnInfo: ColumnInfo[] = [],
public rowCount: number = 0,
@@ -29,12 +27,28 @@ export default class Dataset extends FolderFile {
export class ColumnInfo {
constructor(
public columnName: string = '',
+ public columnType: ColumnType,
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
- ) { }
+ public max?: number,
+ public q1?: number,
+ public q3?: number,
+ ) {
+ /*if (isNumber)
+ this.columnType = ColumnType.numerical;
+ else
+ this.columnType = ColumnType.categorical;*/
+ }
+
}
+
+export enum ColumnType {
+ categorical = "Kategorijski",
+ numerical = "Numerički"
+} \ No newline at end of file
diff --git a/frontend/src/app/_data/Experiment.ts b/frontend/src/app/_data/Experiment.ts
index ec966008..31816c19 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 = '',
@@ -14,10 +15,7 @@ export default class Experiment {
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[] = []//[{columnName: "", columnEncoding: Encoding.Label}]
) { }
diff --git a/frontend/src/app/_data/FolderFile.ts b/frontend/src/app/_data/FolderFile.ts
index a79eeac5..c228f25e 100644
--- a/frontend/src/app/_data/FolderFile.ts
+++ b/frontend/src/app/_data/FolderFile.ts
@@ -9,5 +9,6 @@ export class FolderFile {
export enum FolderType {
Dataset,
- Model
+ 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 6281748c..185e2257 100644
--- a/frontend/src/app/_data/Model.ts
+++ b/frontend/src/app/_data/Model.ts
@@ -23,7 +23,12 @@ export default class Model extends FolderFile {
public epochs: number = 5, // TODO add to add-model form
public inputColNum: number = 5,
public learningRate: LearningRate = LearningRate.LR1,
- public layers: Layer[] = [new Layer()]
+ 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
) {
super(name, dateCreated, lastUpdated);
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 3faa4794..d6f4b6ec 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
@@ -27,8 +27,8 @@ export class BoxPlotComponent implements AfterViewInit {
labels: ['January'/*, 'February', 'March', 'April', 'May', 'June', 'July'*/],
datasets: [{
label: 'Dataset 1',
- backgroundColor: 'rgba(0, 65, 101, 1.0)',
- borderColor: '#0063AB',
+ backgroundColor: '#0063AB',
+ borderColor: '#dfd7d7',
borderWidth: 1,
outlierColor: '#999999',
scaleFontColor: '#0063AB',
@@ -78,7 +78,7 @@ export class BoxPlotComponent implements AfterViewInit {
scales : {
x: {
ticks: {
- color: 'rgba(0, 65, 101, 1.0)'
+ color: '#dfd7d7'
},
grid: {
color: "rgba(0, 99, 171, 0.5)"
@@ -88,7 +88,7 @@ export class BoxPlotComponent implements AfterViewInit {
min: -50,
max: 200,
ticks: {
- color: 'rgba(0, 65, 101, 1.0)'
+ color: '#dfd7d7'
},
grid: {
color: "rgba(0, 99, 171, 0.5)"
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 c8f406f4..7f18256a 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,5 +1,3 @@
-<div class="chart-wrapper">
- <canvas id="myChart">
-
- </canvas>
-</div> \ No newline at end of file
+
+ <canvas id="myChart" style="width: 100%; height: 530px;">
+ </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 49558025..34df38bc 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
@@ -1,4 +1,4 @@
-import { Component, AfterViewInit } from '@angular/core';
+import { Component, AfterViewInit, ViewChild } from '@angular/core';
import { Chart } from 'chart.js';
@Component({
@@ -9,12 +9,12 @@ import { Chart } from 'chart.js';
export class LineChartComponent implements AfterViewInit {
- dataAcc: number[] = [];
- dataMAE: number[] = [];
- dataMSE: number[] = [];
- dataLOSS: number[] = [];
+ dataAcc: number[] = [2,3,5,5,6,7,8,8,4,6,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97];
+ dataMAE: number[] = [2,3,5,5,6,7,8,8,4,6,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97];
+ dataMSE: number[] = [2,3,5,5,6,7,8,8,4,6,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97];
+ dataLOSS: number[] =[2,3,5,5,6,7,8,8,4,6,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97];
- dataEpoch: number[] = [];
+ dataEpoch: number[] = [2,3,5,5,6,7,8,8,4,6,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97];
constructor() {
/*let i = 0;
@@ -77,11 +77,28 @@ export class LineChartComponent implements AfterViewInit {
},
options: {
scales: {
- y: {
- beginAtZero: true
- }
+ x:{
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ },
+ y: {
+ beginAtZero: true,
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ }
+
}
- }
+
+
+ }
}
);
}
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
index 5735217e..005cb692 100644
--- a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
@@ -1,6 +1,4 @@
#divScatterChart{
- background-color: beige;
- display: block;
- width: 400px;
- height: 200px;
+
+ 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
index 2b30fe1f..ef41775a 100644
--- a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
@@ -1,4 +1,4 @@
-<p>Scatter chart:</p>
-<div id="divScatterChart">
- <canvas id="ScatterCharts"> </canvas>
+
+<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/_elements/_charts/scatterchart/scatterchart.component.ts b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts
index 9dfef4c3..12795c70 100644
--- a/frontend/src/app/_elements/_charts/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/column-table/column-table.component.css b/frontend/src/app/_elements/column-table/column-table.component.css
index 108efb32..0477b7be 100644
--- a/frontend/src/app/_elements/column-table/column-table.component.css
+++ b/frontend/src/app/_elements/column-table/column-table.component.css
@@ -3,7 +3,6 @@ table.fixed {
display: block;
overflow-x: auto;
white-space: nowrap;
- border: 1px solid var(--ns-primary-50);
font-size: 12px;
border-radius: 4px;
}
@@ -18,7 +17,6 @@ table.fixed td {
max-width: 200px;
min-width: 200px;
vertical-align: middle;
- background-color: var(--ns-bg-dark-100);
margin: 4px;
}
@@ -27,13 +25,12 @@ table.fixed th {
max-width: 120px;
min-width: 120px;
vertical-align: middle;
- background-color: var(--ns-bg-dark-100);
+ background-color: var(--ns-primary-50);
font-size: 14px;
}
table.fixed th:first-child {
text-align: center;
- background-color: var(--ns-primary-25);
}
.columnNames {
@@ -58,8 +55,8 @@ mat-slider {
}
#missingValuesHeader {
- font-size: 12px;
- line-height: 110% !important;
+ font-size: 13px;
+ line-height: 140% !important;
}
.verticalAlign {
@@ -90,7 +87,7 @@ table ::ng-deep .mat-form-field-wrapper {
}
.no-pad {
- padding: 1px;
+ padding: 2px;
margin: 0;
}
@@ -107,7 +104,7 @@ table ::ng-deep .mat-form-field-wrapper {
}
.graphic-class {
- background-color: white !important;
+ opacity: 0.5;
}
@@ -192,8 +189,9 @@ table ::ng-deep .mat-form-field-wrapper {
}
.hidden {
- visibility: hidden;
- height: 1px;
+ /*visibility: hidden;
+ height: 1px;*/
+ display: none;
}
.bottom-button {
@@ -220,4 +218,49 @@ table ::ng-deep .mat-form-field-wrapper {
.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
index 53cb3551..543a0018 100644
--- a/frontend/src/app/_elements/column-table/column-table.component.html
+++ b/frontend/src/app/_elements/column-table/column-table.component.html
@@ -1,235 +1,231 @@
-<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)">
+<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>
+ </div>
+ <!--
<button mat-button class="p-1 folder-tab-end rounded-top">
Kolone
<mat-icon>keyboard_double_arrow_down</mat-icon>
- <!--meni ovde-->
</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">
- <thead>
- <tr>
- <th>#</th>
- <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index">
- <div class="cell-align">
- #{{i + 1}}&nbsp;&nbsp;{{colInfo.columnName}}
- <mat-checkbox color="primary" checked (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">
- <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">
- <thead>
- <tr>
- <th>Naziv</th>
- <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index">
- #{{i + 1}}&nbsp;&nbsp;{{colInfo.columnName}}
- </th>
- </tr>
- </thead>
- <tbody>
- <tr *ngFor="let colInfo of dataset.columnInfo; let i = index">
- <th>
- <div class="text-left">
- {{colInfo.columnName}}
- </div>
- </th>
- <td *ngFor="let colInfo of dataset.columnInfo; let j = index">
- <div class="text-overflow">
- 0.1
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
-
- <div [ngClass]="{'hidden': tabToDisplay != Table.Columns}">
- <table class="table text-offwhite fixed bg-blur">
- <thead>
- <tr>
- <th>Naziv</th>
- <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index">
- <div class="cell-align">
- #{{i + 1}}&nbsp;&nbsp;{{colInfo.columnName}}
- <mat-checkbox color="primary" checked (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">
- <mat-form-field>
- <mat-select matNativeControl [(value)]="colInfo.isNumber">
- <mat-option [value]="false">Kategorijski</mat-option>
- <mat-option [value]="true">Numerički</mat-option>
- </mat-select>
- </mat-form-field>
- </td>
- </tr>
- <tr class="graphics-row">
- <th class="no-pad border-bottom">Grafik</th>
- <td class="graphic-class no-pad" *ngFor="let colInfo of dataset.columnInfo; let i = index">
- <app-box-plot *ngIf="colInfo.isNumber" [width]="150" [height]="150"></app-box-plot>
- <app-pie-chart *ngIf="!colInfo.isNumber" [width]="150" [height]="150"></app-pie-chart>
- </td>
- </tr>
- <tr>
- <th class="brighter">Statistika</th>
- <td *ngFor="let colInfo of dataset.columnInfo; let i = index">
- <span *ngIf="colInfo.isNumber">
- Mean: {{colInfo.mean}}<br>
- Median: {{colInfo.median}}<br>
- Min: {{colInfo.min}}<br>
- Max: {{colInfo.max}}<br>
- <!-- TODO na ML-u: Q1 i Q3 u statistici
- Q1: {{colInfo.q1}}<br>
- Q3: {{colInfo.q3}}<br>
- -->
- </span>
- <div class="text-overflow" *ngIf="!colInfo.isNumber">
- <span *ngFor="let uniqueValue of colInfo.uniqueValues | slice:0:6; let i = index">
- {{uniqueValue}}<br><!-- TODO na ML-u: broj ponavljanja unique values-a u zagradi nek pise -->
- </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">settings</span>
- </th>
- <td *ngFor="let colInfo of dataset.columnInfo; let i = index" class="pad-fix">
- <mat-form-field>
- <mat-select matNativeControl [(value)]="experiment.encodings[i].encoding">
- <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">Regulisanje<br>nedostajućih<br>vrednosti<br></div>
- <span class="material-icons-round">settings</span>
- </th>
- <td *ngFor="let colInfo of dataset.columnInfo; let i = index">
-
- <button class="w-100" mat-raised-button [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</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]>
- <button [disabled]="getValue(colInfo.columnName) == ''" mat-menu-item value={{getValue(colInfo.columnName)}} (click)="MissValsReplaceClicked($event, colInfo.columnName, i)">Potvrdi unos</button>
- </mat-menu>
-
- </td>
- </tr>
- <!--<tr class="row-height" *ngFor="let row of tableData; let i = index">
- <th *ngIf="i == 0" [attr.rowspan]="tableData!.length">Vrednosti</th>
-
-
- <td class="text-center" *ngFor="let col of row; let j = index">
- <div class="text-overflow">
- {{col}}
- </div>
- </td>
- </tr>-->
- </tbody>
- </table>
- </div>
+ -->
</div>
-</div>
-
-<div class="container-fluid text-offwhite belowColumn mt-3">
- <div class="ns-row">
- <div class="ns-col slider rounded" style="border:1px solid var(--ns-primary)">
+ <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 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 [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 colInfo of dataset.columnInfo; let i = index">
+ <th [ngClass]="{'header-disabled col-disabled' : !columnsChecked[i]}">
+ <div class="text-left">
+ {{colInfo.columnName}}
+ </div>
+ </th>
+ <td *ngFor="let colInfo of dataset.columnInfo; let j = index" [ngClass]="{'text-disabled col-disabled' : !columnsChecked[j] || !columnsChecked[i]}">
+ <div class="text-overflow">
+ 0.1
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
- </div>
- <div class="ns-col slider rounded" style="border:1px solid var(--ns-primary);margin-left: 10px;">
- <div class="text-center text-offwhite justify-content-center align-items-center">
- <mat-checkbox class="pt-4" color="accent">Nasumični redosled podataka</mat-checkbox>
+ <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)]="colInfo.columnType" [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="colInfo.columnType == ColumnType.numerical" [width]="150" [height]="150"></app-box-plot>
+ <app-pie-chart *ngIf="colInfo.columnType == 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="colInfo.columnType == 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="colInfo.columnType == 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]>
+ <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 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>Tip problema</mat-label>
- <mat-select value="ToDo1">
- <mat-option value="ToDo1">Regresioni</mat-option>
- <mat-option value="ToDo2">Binarni-Klasifikacioni</mat-option>
- <mat-option value="ToDo3">Multi-Klasifikacioni</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>Izlazna kolona</mat-label>
- <mat-select>
- <mat-option *ngFor="let item of dataset?.columnInfo" [value]="item.columnName">{{item.columnName}}</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 mat-button (click)="ok()" class="bottom-button text-offwhite rounded-bottom">
+ <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 mat-button (click)="saveExperiment()" class="bottom-button text-offwhite rounded-bottom">
<div class="f-row" style="justify-content: space-around;">
<div>Potvrdi</div>
<div class="icon-double pt-1">
@@ -238,6 +234,7 @@
</div>
</div>
</button>
+ </div>
</div>
</div>
</div> \ No newline at end of file
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 9cabf190..c3d4f206 100644
--- a/frontend/src/app/_elements/column-table/column-table.component.ts
+++ b/frontend/src/app/_elements/column-table/column-table.component.ts
@@ -1,13 +1,15 @@
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChildren } from '@angular/core';
-import Dataset from 'src/app/_data/Dataset';
+import Dataset, { ColumnType } from 'src/app/_data/Dataset';
import Experiment, { ColumnEncoding, Encoding, NullValReplacer, 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 { MatSliderChange } from '@angular/material/slider';
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';
@Component({
selector: 'app-column-table',
@@ -17,43 +19,69 @@ import { CsvParseService } from 'src/app/_services/csv-parse.service';
export class ColumnTableComponent implements AfterViewInit {
@Input() dataset?: Dataset;
- @Input() experiment?: Experiment;
+ @Input() experiment!: Experiment;
@ViewChildren("nullValMenu") nullValMenus!: ElementRef[];
@Output() okPressed: EventEmitter<string> = new EventEmitter();
+ @Output() columnTableChanged = new EventEmitter();
+
Object = Object;
Encoding = Encoding;
NullValueOptions = NullValueOptions;
+ ColumnType = ColumnType;
+ ProblemType = ProblemType;
tableData?: any[][];
nullValOption: string[] = [];
- testSetDistribution: number = 70;
- constructor(private datasetService: DatasetsService, public csvParseService: CsvParseService, public dialog: MatDialog) {
+ 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
}
- ngAfterViewInit(): void {
- this.datasetService.getMyDatasets().subscribe((datasets) => {
- this.dataset = datasets[0];
- this.experiment = new Experiment();
+ loadDataset(dataset: Dataset) {
+ this.dataset = dataset;
- console.log(datasets);
- for (let i = 0; i < this.dataset?.columnInfo.length; i++) {
- this.experiment?.inputColumns.push(this.dataset.columnInfo[i].columnName);
- }
- this.resetColumnEncodings(Encoding.Label);
- this.setDeleteColumnsForMissingValTreatment();
+ this.setColumnTypeInitial();
+
+ this.dataset.columnInfo.forEach(column => {
+ this.columnsChecked.push(true);
+ });
+
+ for (let i = 0; i < this.dataset?.columnInfo.length; i++) {
+ this.experiment.inputColumns.push(this.dataset.columnInfo[i].columnName);
+ }
+ this.experiment.outputColumn = this.experiment.inputColumns[0];
+ this.resetColumnEncodings(Encoding.Label);
+ this.setDeleteRowsForMissingValTreatment();
- this.nullValOption = [].constructor(this.dataset.columnInfo.length).fill('Obriši redove');
+ 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.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;
}
- setDeleteColumnsForMissingValTreatment() {
+ ngAfterViewInit(): void {
+
+ }
+
+ setColumnTypeInitial() {
+ if (this.dataset != undefined) {
+ for (let i = 0; i < this.dataset.columnInfo.length; i++) {
+ this.dataset.columnInfo[i].columnType = (this.dataset.columnInfo[i].isNumber) ? ColumnType.numerical : ColumnType.categorical;
+ }
+ }
+ }
+
+ setDeleteRowsForMissingValTreatment() {
if (this.experiment != undefined) {
this.experiment.nullValues = NullValueOptions.DeleteRows;
this.experiment.nullValuesReplacers = [];
@@ -67,8 +95,20 @@ export class ColumnTableComponent implements AfterViewInit {
}
}
+ 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);
@@ -80,7 +120,26 @@ 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];
+ }
+ this.columnTableChangeDetected();
+ }
+ }
+
+ changeOutputColumn(columnName: string) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+ let column = this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.outputColumn)[0];
+ if (column.columnType == ColumnType.numerical) {
+ this.experiment.type = ProblemType.Regression;
}
+ else {
+ if (column.uniqueValues!.length == 2)
+ this.experiment.type = ProblemType.BinaryClassification;
+ else
+ this.experiment.type = ProblemType.MultiClassification;
+ }
+ this.columnTableChangeDetected();
}
}
@@ -91,6 +150,7 @@ export class ColumnTableComponent implements AfterViewInit {
this.experiment.encodings.push(new ColumnEncoding(this.dataset?.columnInfo[i].columnName, encodingType));
//console.log(this.experiment.encodings);
}
+ this.columnTableChangeDetected();
}
}
openEncodingDialog() {
@@ -127,9 +187,11 @@ export class ColumnTableComponent implements AfterViewInit {
option: NullValueOptions.DeleteRows,
value: ""
});
- this.nullValOption[i] = "Obriši redove";
+ 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() {
@@ -141,13 +203,25 @@ export class ColumnTableComponent implements AfterViewInit {
this.resetMissingValuesTreatment(selectedMissingValuesOption);
});
}
- updateTestSet(event: MatSliderChange) {
- this.testSetDistribution = event.value!;
+
+ 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) => {
+ console.log(response);
+ this.okPressed.emit();
+ });
+ });
}
+
MissValsDeleteClicked(event: Event, replacementType: NullValueOptions, index: number) {
- if (this.experiment != undefined) {
+ if (this.experiment != undefined && this.dataset != undefined) {
let columnName = (<HTMLInputElement>event.currentTarget).value;
let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0];
@@ -163,7 +237,9 @@ export class ColumnTableComponent implements AfterViewInit {
arrayElement.value = "";
}
- this.nullValOption[index] = (replacementType == NullValueOptions.DeleteColumns) ? "Obriši kolonu" : "Obriši redove";
+ 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();
}
}
@@ -185,6 +261,7 @@ export class ColumnTableComponent implements AfterViewInit {
}
this.nullValOption[index] = "Popuni sa: " + fillValue;
+ this.columnTableChangeDetected();
}
}
getValue(columnName: string): string {
@@ -192,8 +269,8 @@ export class ColumnTableComponent implements AfterViewInit {
return (<HTMLInputElement>document.getElementById(columnName)).value;
return '0';
}
- ok() {
- this.okPressed.emit();
+ saveExperiment() {
+ this.openSaveExperimentDialog();
}
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
index 137a9643..2340ee8a 100644
--- a/frontend/src/app/_elements/folder/folder.component.css
+++ b/frontend/src/app/_elements/folder/folder.component.css
@@ -129,6 +129,8 @@
.list-view {
height: 100%;
overflow-y: auto;
+ display: flex;
+ flex-direction: column;
}
.list-item {
@@ -138,6 +140,7 @@
align-items: center;
justify-content: space-between;
border-bottom: 1px solid var(--ns-primary);
+ flex-shrink: 0;
}
.list-item:hover {
@@ -149,6 +152,15 @@
display: none;
}
+.list-add {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ flex-grow: 1;
+ height: 100%;
+}
+
.folder-inside {
width: 100%;
height: 40rem;
@@ -157,7 +169,7 @@
.file-content {
width: 100%;
- height: 92%;
+ height: 100%;
position: relative;
}
@@ -186,4 +198,8 @@
.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
index 36f70c97..48b59dc8 100644
--- a/frontend/src/app/_elements/folder/folder.component.html
+++ b/frontend/src/app/_elements/folder/folder.component.html
@@ -1,9 +1,9 @@
<div id="folder">
<div id="tabs">
- <div id="new-file-tab" 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}">
+ <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)">
- {{tabTitles[TabType.NewFile]}}
+ {{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}">
@@ -12,6 +12,10 @@
<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">
@@ -26,20 +30,20 @@
</mat-form-field>
</div>
<div id="search-options">
- <div id="collapseFilters" class="collapse collapse-horizontal">
+ <!-- <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>
+ </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">
+ <!-- <div id="collapseSort" class="collapse collapse-horizontal">
[sort options here TODO]
- </div>
+ </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>
@@ -49,35 +53,37 @@
</div>
</div>
<!--{{fileToDisplay ? fileToDisplay.name : 'No file selected.'}} {{selectedFileIndex}} {{hoveringOverFileIndex}}-->
- <div [ngSwitch]="listView" class="folder-inside bg-blur">
- <div class="file-content" [ngSwitch]="type" *ngSwitchCase="false">
- <div class="file-bottom-buttons">
- <button class="btn-clear file-button" (click)="deleteFile()">
+ <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)">
<mat-icon>delete</mat-icon>
</button>
- <button class="btn-clear file-button">
- <mat-icon>link</mat-icon>
- </button>
- <button class="btn-clear file-button">
+ <!-- <button class="btn-clear file-button">
<mat-icon>zoom_out_map</mat-icon>
- </button>
+ </button> -->
</div>
- <app-form-model [forExperiment]="forExperiment" [model]="fileToDisplay" *ngSwitchCase="FolderType.Model"></app-form-model>
- <app-form-dataset *ngSwitchCase="FolderType.Dataset" ></app-form-dataset>
+ <app-form-model [ngClass]="{'form-hidden': type != FolderType.Model}"></app-form-model>
+ <app-form-dataset [ngClass]="{'form-hidden': type != FolderType.Dataset}"></app-form-dataset>
</div>
- <div *ngSwitchCase="true" class="list-view">
- <div *ngFor="let file of filteredFiles; let i = index" class="list-item">
+ <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">
- <a class="force-link" (click)="selectFile(i)">{{file.name}}</a>
+ {{file.name}}
</div>
<div class="mx-2 hover-hide">
{{file.lastUpdated | date}}
</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">
+ <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>
diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts
index 06b4d893..20ca1121 100644
--- a/frontend/src/app/_elements/folder/folder.component.ts
+++ b/frontend/src/app/_elements/folder/folder.component.ts
@@ -1,33 +1,35 @@
-import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
+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 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 OnInit {
+export class FolderComponent implements AfterViewInit {
- @ViewChild(FormDatasetComponent) formDataset?: FormDatasetComponent;
+ @ViewChild(FormDatasetComponent) formDataset!: FormDatasetComponent;
+ @ViewChild(FormModelComponent) formModel!: FormModelComponent;
@Input() folderName: string = 'Moji podaci';
-
@Input() files!: FolderFile[]
- newFile!: Dataset | Model;
+ newFile?: Dataset | Model;
@Input() type: FolderType = FolderType.Dataset;
-
- @Input() forExperiment?: Experiment;
+ @Input() forExperiment!: Experiment;
+ @Input() startingTab!: TabType;
newFileSelected: boolean = true;
@@ -42,34 +44,32 @@ export class FolderComponent implements OnInit {
searchTerm: string = '';
- constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService) {
- //PLACEHOLDER
- this.forExperiment = new Experiment();
- this.forExperiment.inputColumns = ['kolona1', 'kol2', '???', 'test'];
+ constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService, private signalRService: SignalRService) {
+ this.tabsToShow.forEach(tab => this.folders[tab] = []);
+ }
- this.folders[TabType.File] = [];
- this.folders[TabType.NewFile] = [];
+ ngAfterViewInit(): void {
+ this.refreshFiles(null);
- this.refreshFiles();
+ if (this.signalRService.hubConnection) {
+ this.signalRService.hubConnection.on("NotifyDataset", (dName: string, dId: string) => {
+ this.refreshFiles(dId);
-
- }
-
- ngOnInit(): void {
- if (this.files.length > 0)
- this.selectFile(0);
- else {
- this.selectNewFile();
+ });
+ } else {
+ console.warn("Dataset-Load: No connection!");
}
}
- displayFile(){
- if(this.type == FolderType.Dataset)
- this.formDataset!.dataset = <Dataset>this.fileToDisplay;
+ 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;
+ /*this.hoveringOverFileIndex = i;
if (i != -1) {
this.fileToDisplay = this.files[i];
} else {
@@ -79,7 +79,7 @@ export class FolderComponent implements OnInit {
this.fileToDisplay = this.files[this.selectedFileIndex];
}
}
- this.displayFile();
+ this.displayFile();*/
}
selectNewFile() {
@@ -87,20 +87,19 @@ export class FolderComponent implements OnInit {
this.createNewFile();
}
this.fileToDisplay = this.newFile;
- this.selectedFile = this.newFile;
this.newFileSelected = true;
this.listView = false;
- this.selectedFileChanged.emit(this.newFile);
this.displayFile();
}
- selectFile(index: number) {
- this.selectedFile = this.filteredFiles[index];
- this.fileToDisplay = this.filteredFiles[index];
+ selectFile(file?: FolderFile) {
+ this.selectedFile = file;
+ this.fileToDisplay = file;
this.newFileSelected = false;
this.listView = false;
this.selectedFileChanged.emit(this.selectedFile);
this.displayFile();
+ this.selectTab(TabType.File);
}
createNewFile() {
@@ -115,9 +114,22 @@ export class FolderComponent implements OnInit {
this.okPressed.emit();
}
- refreshFiles(){
+ _initialized: boolean = false;
+
+ refreshFiles(selectedDatasetId: string | null) {
+ 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) => {
@@ -125,6 +137,7 @@ export class FolderComponent implements OnInit {
});
this.modelsService.getMyModels().subscribe((models) => {
+ console.log(models);
this.folders[TabType.MyModels] = models;
});
@@ -137,20 +150,40 @@ export class FolderComponent implements OnInit {
this.folders[TabType.MyExperiments] = experiments;
});
- this.files = [];
-
- this.filteredFiles.length = 0;
- this.filteredFiles.push(...this.files);
+ if (!this._initialized) {
+ this.files = this.folders[this.startingTab];
+ this.filteredFiles = [];
+ this.selectTab(this.startingTab);
+ this._initialized = true;
+ }
this.searchTermsChanged();
-
}
saveNewFile() {
- if(this.type == FolderType.Dataset)
- this.formDataset!.uploadDataset();
+ switch (this.type) {
+ case FolderType.Dataset:
+ this.formDataset!.uploadDataset((dataset: Dataset) => {
+ Shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira.");
+ this.refreshFiles(dataset._id);
+ },
+ () => {
+ 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.formModel.newModel = model;
+ 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)
@@ -159,7 +192,7 @@ export class FolderComponent implements OnInit {
zIndex = this.files.length + 3;
return zIndex;
}
-
+
newFileZIndex() {
return (this.files.length + 1);
}*/
@@ -172,25 +205,47 @@ export class FolderComponent implements OnInit {
filteredFiles: FolderFile[] = [];
searchTermsChanged() {
+ if (!this.files) return;
this.filteredFiles.length = 0;
this.filteredFiles.push(...this.files.filter((file) => file.name.toLowerCase().includes(this.searchTerm.toLowerCase())));
- if (this.selectedFile) {
+ /*if (this.selectedFile) {
if (!this.filteredFiles.includes(this.selectedFile)) {
- this.selectFile(-1);
+ 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);
+ //this.selectedFileIndex = this.filteredFiles.indexOf(this.selectedFile);
}
- }
+ }*/
}
- listView: boolean = false;
+ listView: boolean = true;
toggleListView() {
this.listView = !this.listView;
}
- deleteFile() {
+ deleteFile(file: FolderFile) {
console.log('delete');
+ switch (this.type) {
+ case FolderType.Dataset:
+ this.datasetsService.deleteDataset(<Dataset>file).subscribe((response) => {
+ console.log(response);
+ });
+ break;
+ case FolderType.Model:
+ this.modelsService.deleteModel(<Model>file).subscribe((response) => {
+ console.log(response);
+ });
+ break;
+ case FolderType.Experiment:
+ // this.experimentsService.deleteExperiment(<Model>file).subscribe((response) => {
+ // console.log(response);
+ // });
+ //todo delete za predictor
+ break;
+ }
}
folders: { [tab: number]: FolderFile[] } = {};
@@ -206,51 +261,87 @@ export class FolderComponent implements OnInit {
};
FolderType = FolderType;
-
+ Privacy = Privacy;
TabType = TabType;
+ privacy: Privacy = Privacy.Private;
+
@Input() tabsToShow: TabType[] = [
TabType.MyDatasets,
TabType.PublicDatasets,
TabType.MyModels,
TabType.PublicModels,
- TabType.MyExperiments,
- TabType.File
+ TabType.MyExperiments
]
@Input() selectedTab: TabType = TabType.NewFile;
hoverTab: TabType = TabType.None;
selectTab(tab: TabType) {
- this.checkListView(tab);
- this.selectedTab = tab;
- this.files = this.folders[tab];
+ setTimeout(() => {
+ if (tab == TabType.NewFile) {
+ this.selectNewFile();
+ }
- this.searchTermsChanged();
+ 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();
+ });
}
- checkListView(tab: TabType) {
+ getListView(tab: TabType) {
switch (tab) {
case TabType.File:
case TabType.NewFile:
case TabType.None:
- this.listView = false;
- break;
+ return false;
case TabType.MyExperiments:
case TabType.MyDatasets:
case TabType.MyModels:
case TabType.PublicDatasets:
case TabType.PublicModels:
- this.listView = true;
- break;
+ 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.checkListView(tab);
+ this.listView = this.getListView(tab);
+ this.privacy = this.getPrivacy(tab);
this.hoverTab = tab;
if (tab == TabType.None) {
- this.checkListView(this.selectedTab);
+ this.listView = this.getListView(this.selectedTab);
this.files = this.folders[this.selectedTab];
} else {
this.files = this.folders[tab];
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 da31cfcb..7c7eb0d3 100644
--- a/frontend/src/app/_elements/form-dataset/form-dataset.component.css
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.css
@@ -4,26 +4,23 @@
position: relative;
}
+.bottomBar {
+ width: 50%;
+ margin: 1rem;
+ align-items: flex-start;
+}
+
+.fileButton{
+ margin-top: 10px;
+}
+
.file-container {
border: 4px solid transparent;
position: relative;
margin-left: 3%;
- margin-top: 3rem;
width: 94%;
- min-height: 300px;
- height: 75%;
-}
-
-.fileButton {
- position: absolute;
- margin-top: -3rem;
- display: flex;
- flex-direction: row;
- align-items: center;
-}
-
-.fileButton label {
- margin-left: 10px;
+ min-height: 400px;
+ height: 95%;
}
.dottedClass {
@@ -31,6 +28,13 @@
border-radius: 25px;
}
+.icon-display {
+ position: absolute;
+ top: 45%;
+ left: 50%;
+ transform: translate(-50%, -50%) scale(4);
+}
+
.hidden {
visibility: hidden;
}
@@ -42,28 +46,7 @@
opacity: 0;
}
-.file input {
- border-radius: 4px;
- margin-top: -15px;
- width: 100%;
- height: 100%;
-}
-
-.icon-display {
- position: absolute;
- top: 45%;
- left: 50%;
- transform: translate(-50%, -50%) scale(4);
-}
-
-.bottomBar {
- width: 50%;
- margin: 1rem;
- align-items: flex-start;
-}
-
-#bottomButton {
- background-color: var(--ns-bg-dark-100);
- width: 10%;
- height: 65%;
+.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
index 2176b130..5c2b29ba 100644
--- a/frontend/src/app/_elements/form-dataset/form-dataset.component.html
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
@@ -1,74 +1,58 @@
-<div class="folderBox">
-
- <div class="file-container" [ngClass]="{'dottedClass': !tableData.hasInput}">
-
- <i class="material-icons-outlined icon-display" [ngClass]="{'hidden': tableData.hasInput}">file_upload</i>
-
- <div class="fileButton">
- <button type="button" mat-raised-button (click)="fileInput.click()">Choose File</button>
- <label>{{filename}}</label>
- </div>
-
- <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 class="folderBox" *ngIf="dataset">
+
+ <div class="row" style="margin-right: 0;">
+ <div class="bottomBar">
+ <div class="row">
+ <div class="col-sm mb-3">
+ <div class="fileButton">
+ <button type="button" mat-raised-button (click)="fileInput.click()">Choose File</button>
+ <label>{{filename}}</label>
+ </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">
- <div class="bottomBar">
- <div class="row">
- <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}}">
- <!--[formControl]="nameFormControl"-->
- <mat-error *ngIf="nameFormControl.hasError('required')">
- Naziv je <strong>obavezan</strong>
- </mat-error>
- </mat-form-field>
+ <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>
- <div class="col-sm mb-3">
+ </div>
+ </div>
- <!--<input id="fileInput" class="form-control btn-lg" type="file" class="upload" (change)="changeListener($event)" accept=".csv">
- -->
- </div>
- <div class="col-sm">
+ <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>
- <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>
+ <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 class="btn-group" role="group" aria-label="Button group with nested dropdown">
+ </div>
</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>
--->
</div> \ No newline at end of file
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 63376524..62afaa47 100644
--- a/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
@@ -5,7 +5,7 @@ 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';
+import { FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-form-dataset',
@@ -18,7 +18,7 @@ export class FormDatasetComponent {
nameFormControl = new FormControl('', [Validators.required, Validators.email]);
- delimiterOptions: Array<string> = [",", ";", "|", "razmak", "novi red"]; //podrazumevano ","
+ delimiterOptions: Array<string> = [",", ";", "|", "razmak", "novi red"]; //podrazumevano ","
csvRecords: any[] = [];
files: File[] = [];
@@ -29,7 +29,7 @@ export class FormDatasetComponent {
tableData: TableData = new TableData();
- @ViewChild('fileInput') fileInput! : ElementRef
+ @ViewChild('fileInput') fileInput!: ElementRef
filename: String;
@@ -65,16 +65,13 @@ export class FormDatasetComponent {
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;
@@ -85,32 +82,37 @@ export class FormDatasetComponent {
this.dataset.name = this.filename.slice(0, this.filename.length - 4);
}
+ /*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
index 8c279523..11b6ef5e 100644
--- a/frontend/src/app/_elements/form-model/form-model.component.css
+++ b/frontend/src/app/_elements/form-model/form-model.component.css
@@ -84,4 +84,21 @@ hr {
.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
index 76601465..c0318012 100644
--- a/frontend/src/app/_elements/form-model/form-model.component.html
+++ b/frontend/src/app/_elements/form-model/form-model.component.html
@@ -1,202 +1,226 @@
-<div id="container">
- <div class="ns-row">
+<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 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>
+
+ <!-- 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">
+ <app-graph [model]="newModel"></app-graph>
+ </div>
</div>
+ </div>
- <div class="break-1"></div>
+ <!-- SVI LAYERI -->
- <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>
+ <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>
- </mat-form-field>
</div>
+ <div class="break-1"></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">
+ <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="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-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>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">
+ <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="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 required [(value)]="newModel.batchSize">
- <mat-option *ngFor="let option of Object.keys(BatchSize); let optionName of Object.values(BatchSize)" [value]="option">{{option}}</mat-option>
+ <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>
- </div>
-</div>
-
-
-<!--kraj unosa parametara-->
-<hr>
-<div class="m-2">
- <app-graph [model]="newModel" [inputColumns]="forExperiment?.inputColumns"></app-graph>
-</div>
-<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>
-
-
-</div>
-
-<!--kraj selectall**********************************************************************************-->
-<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)">
+ <!-- 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)">
+ <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>
+ </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>
+ <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/form-model/form-model.component.ts b/frontend/src/app/_elements/form-model/form-model.component.ts
index 2c78cd56..71b374b0 100644
--- a/frontend/src/app/_elements/form-model/form-model.component.ts
+++ b/frontend/src/app/_elements/form-model/form-model.component.ts
@@ -4,7 +4,7 @@ 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',
@@ -13,13 +13,12 @@ import { GraphComponent } from '../graph/graph.component';
})
export class FormModelComponent implements AfterViewInit {
@ViewChild(GraphComponent) graph!: GraphComponent;
- @Input() forExperiment?: Experiment;
+ @Input() forExperiment!: Experiment;
@Output() selectedModelChangeEvent = new EventEmitter<Model>();
-
+ testSetDistribution: number = 70;
constructor() { }
- ngAfterViewInit(): void {
- }
+ ngAfterViewInit(): void { }
selectFormControl = new FormControl('', Validators.required);
nameFormControl = new FormControl('', [Validators.required, Validators.email]);
@@ -34,8 +33,7 @@ export class FormModelComponent implements AfterViewInit {
selectRegularisationFormControl = new FormControl('', Validators.required);
selectRRateFormControl = new FormControl('', Validators.required);
- newModel: Model = new Model();
- myModels?: Model[];
+ newModel!: Model;
selectedModel?: Model;
@@ -57,7 +55,9 @@ export class FormModelComponent implements AfterViewInit {
selectedMetrics = [];
lossFunction: any = LossFunction;
- showMyModels: boolean = true;
+ loadModel(model: Model) {
+ this.newModel = model;
+ }
updateGraph() {
//console.log(this.newModel.layers);
@@ -121,7 +121,6 @@ export class FormModelComponent implements AfterViewInit {
}
}
changeAllRegularisationRate() {
-
for (let i = 0; i < this.newModel.layers.length; i++) {
this.newModel.layers[i].regularisationRate = this.selectedRegularisationRate;
}
@@ -133,6 +132,7 @@ export class FormModelComponent implements AfterViewInit {
}
}
-
-
+ updateTestSet(event: MatSliderChange) {
+ this.testSetDistribution = event.value!;
+ }
}
diff --git a/frontend/src/app/_elements/graph/graph.component.css b/frontend/src/app/_elements/graph/graph.component.css
index 361e7249..456a8df1 100644
--- a/frontend/src/app/_elements/graph/graph.component.css
+++ b/frontend/src/app/_elements/graph/graph.component.css
@@ -1,6 +1,5 @@
.node-text {
position: absolute;
- color: transparent;
width: 100px;
height: 40px;
text-align: center;
@@ -12,6 +11,10 @@
transform: translate(-50%, -50%);
}
-.node-text:hover {
+.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 19e5c14a..3c01bc5e 100644
--- a/frontend/src/app/_elements/graph/graph.component.html
+++ b/frontend/src/app/_elements/graph/graph.component.html
@@ -1,6 +1,6 @@
<div #graphWrapper class="w-100 position-relative" style="height: 14rem;">
<ng-container *ngFor="let layer of layers; let i = index">
- <div class="node-text" *ngFor="let node of layer; let j = index" [style.left.%]="node.x * 99.4" [style.top.%]="node.y * 100">
+ <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[j] : 'nepoznato') : (i > 0 && i
< layers.length - 1 ? model!.layers[i-1].activationFunction : (i==layers.length - 1 ? 'out' : '')) }} </div>
</ng-container>
diff --git a/frontend/src/app/_elements/graph/graph.component.ts b/frontend/src/app/_elements/graph/graph.component.ts
index 31814c2c..c7f8d964 100644
--- a/frontend/src/app/_elements/graph/graph.component.ts
+++ b/frontend/src/app/_elements/graph/graph.component.ts
@@ -28,7 +28,7 @@ export class GraphComponent implements AfterViewInit {
@Input() outputNodeColor: string = '#dfd7d7';
private ctx!: CanvasRenderingContext2D;
- @Input() inputColumns?: string[] = [];
+ @Input() inputColumns?: string[] = ['Nije odabran eksperiment'];
constructor() { }
@@ -43,6 +43,7 @@ export class GraphComponent implements AfterViewInit {
window.addEventListener('resize', () => { this.resize() });
this.update();
this.resize();
+ console.log(this.layers);
}
layers: Node[][] = [];
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 3a6cce8d..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,3 +1,8 @@
-<div>
-
+<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/_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..a0b5d771
--- /dev/null
+++ b/frontend/src/app/_modals/save-experiment-dialog/save-experiment-dialog.component.html
@@ -0,0 +1,12 @@
+<h1 mat-dialog-title>Čuvanje eksperimenta</h1>
+<div mat-dialog-content>
+ <p>Unesite naziv eksperimenta:</p>
+ <mat-form-field>
+ <input type="text" matInput [(ngModel)]="selectedName">
+ </mat-form-field>
+ <p>Da li ste sigurni u izbor?</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/_pages/experiment/experiment.component.css b/frontend/src/app/_pages/experiment/experiment.component.css
index aca0562a..36c35484 100644
--- a/frontend/src/app/_pages/experiment/experiment.component.css
+++ b/frontend/src/app/_pages/experiment/experiment.component.css
@@ -49,7 +49,7 @@ mat-stepper {
}
.step-content-inside {
- width: 90%;
- height: 90%;
+ 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
index 08d709b2..baae864e 100644
--- a/frontend/src/app/_pages/experiment/experiment.component.html
+++ b/frontend/src/app/_pages/experiment/experiment.component.html
@@ -1,4 +1,4 @@
-<div class="container-fluid p-0 text-offwhite holder" style="height: calc(100vh - 64px);">
+<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>
@@ -18,22 +18,31 @@
<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 [type]="FolderType.Dataset" [tabsToShow]="[TabType.MyDatasets, TabType.PublicDatasets, TabType.File]" (okPressed)="goToPage(1)"></app-folder>
+ <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)"></app-column-table>
+ <app-column-table (okPressed)="goToPage(2)" (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 [type]="FolderType.Model" [tabsToShow]="[TabType.MyModels, TabType.PublicModels, TabType.File]" (okPressed)="goToPage(0)"></app-folder>
+ <app-folder #folderModel [type]="FolderType.Model" [forExperiment]="experiment" [startingTab]="TabType.NewFile" [tabsToShow]="[TabType.MyModels]" (okPressed)="goToPage(3)"></app-folder>
+ </div>
+ </div>
+ <div #steps id="step_4" class="step-content">
+ <div class="step-content-inside">
+ <app-metric-view></app-metric-view>
</div>
</div>
</div>
diff --git a/frontend/src/app/_pages/experiment/experiment.component.ts b/frontend/src/app/_pages/experiment/experiment.component.ts
index 70f941b6..28552664 100644
--- a/frontend/src/app/_pages/experiment/experiment.component.ts
+++ b/frontend/src/app/_pages/experiment/experiment.component.ts
@@ -1,9 +1,15 @@
-import { AfterViewInit, Component, ElementRef, ViewChild, ViewChildren } from '@angular/core';
+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 { FolderType } from 'src/app/_data/FolderFile';
-import { TabType } from 'src/app/_elements/folder/folder.component';
+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';
@Component({
selector: 'app-experiment',
@@ -17,8 +23,28 @@ export class ExperimentComponent implements AfterViewInit {
@ViewChildren('steps') steps!: ElementRef[];
event: number = 0;
+ experiment: Experiment;
+ dataset?: Dataset;
+ @ViewChild("folderDataset") folderDataset!: FolderComponent;
+ @ViewChild("folderModel") folderModel!: FolderComponent;
+ @ViewChild(ColumnTableComponent) columnTable!: ColumnTableComponent;
- constructor() { }
+
+ constructor(private experimentsService: ExperimentsService, private modelsService: ModelsService) {
+ this.experiment = new Experiment("exp1");
+ }
+
+ /*updateExperiment(){
+
+ }*/
+
+ addNewExperiment() {
+ this.experimentsService.addExperiment(this.experiment).subscribe(() => { console.log("new Experiment") });
+ }
+
+ trainModel() {
+ this.modelsService.trainModel((<Model>this.folderModel.selectedFile)._id, this.experiment._id).subscribe(() => { console.log("pocelo treniranje") })
+ }
stepHeight = this.calcStepHeight();
@@ -92,4 +118,16 @@ export class ExperimentComponent implements AfterViewInit {
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);
+ }
}
diff --git a/frontend/src/app/_pages/home/home.component.css b/frontend/src/app/_pages/home/home.component.css
index 22137c24..906f5728 100644
--- a/frontend/src/app/_pages/home/home.component.css
+++ b/frontend/src/app/_pages/home/home.component.css
@@ -15,6 +15,6 @@ h1 {
.card {
margin: 2.5rem !important;
- padding: 3rem;
+ 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 508382da..2825b3bf 100644
--- a/frontend/src/app/_pages/home/home.component.html
+++ b/frontend/src/app/_pages/home/home.component.html
@@ -27,6 +27,7 @@
</p>
</div>
</div>
- </div>
+
+ </div>
</div> \ No newline at end of file
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 3b6e6b64..d3f646cb 100644
--- a/frontend/src/app/_services/datasets.service.ts
+++ b/frontend/src/app/_services/datasets.service.ts
@@ -25,10 +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}`, { headers: this.authService.authHeader(), responseType: 'text' });
}
getDatasetFilePartial(fileId: any, startRow: number, rowNum: number): Observable<any> {
- return this.http.get(`${Configuration.settings.apiURL}/file/csvRead/true/${fileId}/${startRow}/${rowNum}`, { headers: this.authService.authHeader(), responseType: 'text' });
+ 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 bdaf62a7..29569fca 100644
--- a/frontend/src/app/_services/experiments.service.ts
+++ b/frontend/src/app/_services/experiments.service.ts
@@ -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/app.module.ts b/frontend/src/app/app.module.ts
index fc6d3c6b..d44bf6ad 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -21,6 +21,7 @@ import { AlertDialogComponent } from './_modals/alert-dialog/alert-dialog.compon
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 { ProfileComponent } from './_pages/profile/profile.component';
@@ -32,6 +33,7 @@ import { ScatterchartComponent } from './_elements/_charts/scatterchart/scatterc
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';
@@ -49,6 +51,7 @@ 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();
@@ -85,7 +88,10 @@ export function initializeApp(appConfig: Configuration) {
MissingvaluesDialogComponent,
TestComponent,
DoughnutChartComponent,
- HeatmapComponent
+ HeatmapComponent,
+ MetricViewComponent,
+ LineChartComponent,
+ SaveExperimentDialogComponent
],
imports: [
BrowserModule,