aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/app/_elements
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/app/_elements')
-rw-r--r--frontend/src/app/_elements/_charts/barchart/barchart.component.css6
-rw-r--r--frontend/src/app/_elements/_charts/barchart/barchart.component.html4
-rw-r--r--frontend/src/app/_elements/_charts/barchart/barchart.component.spec.ts (renamed from frontend/src/app/_elements/carousel/carousel.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/barchart/barchart.component.ts55
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.css (renamed from frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.html3
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.spec.ts (renamed from frontend/src/app/_elements/annvisual/annvisual.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts102
-rw-r--r--frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.css (renamed from frontend/src/app/_elements/carousel/carousel.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html1
-rw-r--r--frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.spec.ts (renamed from frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts37
-rw-r--r--frontend/src/app/_elements/_charts/heatmap/heatmap.component.css (renamed from frontend/src/app/_elements/item-experiment/item-experiment.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/heatmap/heatmap.component.html3
-rw-r--r--frontend/src/app/_elements/_charts/heatmap/heatmap.component.spec.ts (renamed from frontend/src/app/_elements/item-model/item-model.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/heatmap/heatmap.component.ts108
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.css (renamed from frontend/src/app/_elements/item-predictor/item-predictor.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.html5
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.spec.ts (renamed from frontend/src/app/_elements/model-load/model-load.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts88
-rw-r--r--frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.css0
-rw-r--r--frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.html2
-rw-r--r--frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.ts56
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.css0
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html3
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts46
-rw-r--r--frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.css0
-rw-r--r--frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.html2
-rw-r--r--frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.spec.ts (renamed from frontend/src/app/_elements/item-experiment/item-experiment.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.ts57
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css6
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html4
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts39
-rw-r--r--frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html49
-rw-r--r--frontend/src/app/_elements/annvisual/annvisual.component.css4
-rw-r--r--frontend/src/app/_elements/annvisual/annvisual.component.html5
-rw-r--r--frontend/src/app/_elements/annvisual/annvisual.component.ts45
-rw-r--r--frontend/src/app/_elements/carousel/carousel.component.html17
-rw-r--r--frontend/src/app/_elements/carousel/carousel.component.ts18
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.css223
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.html243
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.spec.ts (renamed from frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.ts250
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.css18
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.html40
-rw-r--r--frontend/src/app/_elements/datatable/datatable.component.html30
-rw-r--r--frontend/src/app/_elements/folder/folder.component.css189
-rw-r--r--frontend/src/app/_elements/folder/folder.component.html99
-rw-r--r--frontend/src/app/_elements/folder/folder.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/folder/folder.component.ts276
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.css69
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.html74
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.spec.ts (renamed from frontend/src/app/_elements/dataset-load/dataset-load.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.ts (renamed from frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts)34
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.css87
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.html202
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.ts138
-rw-r--r--frontend/src/app/_elements/gradient-background/gradient-background.component.css0
-rw-r--r--frontend/src/app/_elements/gradient-background/gradient-background.component.html1
-rw-r--r--frontend/src/app/_elements/gradient-background/gradient-background.component.spec.ts (renamed from frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/gradient-background/gradient-background.component.ts47
-rw-r--r--frontend/src/app/_elements/graph/graph.component.css17
-rw-r--r--frontend/src/app/_elements/graph/graph.component.html11
-rw-r--r--frontend/src/app/_elements/graph/graph.component.ts84
-rw-r--r--frontend/src/app/_elements/item-dataset/item-dataset.component.css23
-rw-r--r--frontend/src/app/_elements/item-dataset/item-dataset.component.html41
-rw-r--r--frontend/src/app/_elements/item-dataset/item-dataset.component.ts40
-rw-r--r--frontend/src/app/_elements/item-experiment/item-experiment.component.html10
-rw-r--r--frontend/src/app/_elements/item-experiment/item-experiment.component.ts15
-rw-r--r--frontend/src/app/_elements/item-model/item-model.component.css23
-rw-r--r--frontend/src/app/_elements/item-model/item-model.component.html58
-rw-r--r--frontend/src/app/_elements/item-model/item-model.component.ts33
-rw-r--r--frontend/src/app/_elements/item-predictor/item-predictor.component.html35
-rw-r--r--frontend/src/app/_elements/item-predictor/item-predictor.component.ts23
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.css0
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.html3
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.ts49
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.css17
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.css8
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.html34
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.ts5
-rw-r--r--frontend/src/app/_elements/notifications/notifications.component.ts1
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.css61
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.html19
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.ts49
-rw-r--r--frontend/src/app/_elements/reactive-background/reactive-background.component.html2
-rw-r--r--frontend/src/app/_elements/reactive-background/reactive-background.component.ts166
93 files changed, 3190 insertions, 705 deletions
diff --git a/frontend/src/app/_elements/_charts/barchart/barchart.component.css b/frontend/src/app/_elements/_charts/barchart/barchart.component.css
new file mode 100644
index 00000000..c3634c9f
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/barchart/barchart.component.css
@@ -0,0 +1,6 @@
+#divBarChart{
+ background-color: beige;
+ display: block;
+ width: 400px;
+ height: 200px;
+}
diff --git a/frontend/src/app/_elements/_charts/barchart/barchart.component.html b/frontend/src/app/_elements/_charts/barchart/barchart.component.html
new file mode 100644
index 00000000..48b7bd3e
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/barchart/barchart.component.html
@@ -0,0 +1,4 @@
+<p>Bar chart:</p>
+<div id="divBarChart">
+ <canvas id="Barchart"> </canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/carousel/carousel.component.spec.ts b/frontend/src/app/_elements/_charts/barchart/barchart.component.spec.ts
index 9196e044..8b346d1c 100644
--- a/frontend/src/app/_elements/carousel/carousel.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/barchart/barchart.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { CarouselComponent } from './carousel.component';
+import { BarchartComponent } from './barchart.component';
-describe('CarouselComponent', () => {
- let component: CarouselComponent;
- let fixture: ComponentFixture<CarouselComponent>;
+describe('BarchartComponent', () => {
+ let component: BarchartComponent;
+ let fixture: ComponentFixture<BarchartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ CarouselComponent ]
+ declarations: [ BarchartComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(CarouselComponent);
+ fixture = TestBed.createComponent(BarchartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/barchart/barchart.component.ts b/frontend/src/app/_elements/_charts/barchart/barchart.component.ts
new file mode 100644
index 00000000..904335d7
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/barchart/barchart.component.ts
@@ -0,0 +1,55 @@
+import { Component, OnInit } from '@angular/core';
+import {Chart} from 'node_modules/chart.js';
+
+@Component({
+ selector: 'app-barchart',
+ templateUrl: './barchart.component.html',
+ styleUrls: ['./barchart.component.css']
+})
+export class BarchartComponent implements OnInit {
+
+
+ constructor() { }
+
+ ngOnInit(){
+ const myChart = new Chart("Barchart", {
+ type: 'bar',
+ data: {
+ labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
+ datasets: [{
+ label: 'Number of Votes',
+ data: [12, 19, 3, 5, 2, 3],
+ backgroundColor: [
+ 'rgba(255, 99, 132, 1)',
+ 'rgba(54, 162, 235, 1)',
+ 'rgba(255, 206, 86, 1)',
+ 'rgba(75, 192, 192, 1)',
+ 'rgba(153, 102, 255, 1)',
+ 'rgba(255, 159, 64, 1)'
+ ],
+ borderColor: [
+ 'rgba(255, 99, 132, 1)',
+ 'rgba(54, 162, 235, 1)',
+ 'rgba(255, 206, 86, 1)',
+ 'rgba(75, 192, 192, 1)',
+ 'rgba(153, 102, 255, 1)',
+ 'rgba(255, 159, 64, 1)'
+ ],
+ borderWidth: 1
+ }]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+
+
+ });
+
+
+ }
+
+}
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.css b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.css
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.css
diff --git a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html
new file mode 100644
index 00000000..688eafae
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html
@@ -0,0 +1,3 @@
+<div class="chart-wrapper">
+ <canvas #boxplot [width]="width" [height]="height"></canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.spec.ts b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.spec.ts
index cb07ef1d..759e7c5e 100644
--- a/frontend/src/app/_elements/annvisual/annvisual.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { AnnvisualComponent } from './annvisual.component';
+import { BoxPlotComponent } from './box-plot.component';
-describe('AnnvisualComponent', () => {
- let component: AnnvisualComponent;
- let fixture: ComponentFixture<AnnvisualComponent>;
+describe('BoxPlotComponent', () => {
+ let component: BoxPlotComponent;
+ let fixture: ComponentFixture<BoxPlotComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ AnnvisualComponent ]
+ declarations: [ BoxPlotComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(AnnvisualComponent);
+ fixture = TestBed.createComponent(BoxPlotComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts
new file mode 100644
index 00000000..3faa4794
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts
@@ -0,0 +1,102 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { Chart, LinearScale, CategoryScale } from 'chart.js';
+import { BoxPlotController, BoxAndWiskers } from '@sgratzl/chartjs-chart-boxplot';
+
+function randomValues(count: number, min: number, max: number) {
+ const delta = max - min;
+ return Array.from({ length: count }).map(() => Math.random() * delta + min);
+}
+
+Chart.register(BoxPlotController, BoxAndWiskers, LinearScale, CategoryScale);
+
+@Component({
+ selector: 'app-box-plot',
+ templateUrl: './box-plot.component.html',
+ styleUrls: ['./box-plot.component.css']
+})
+export class BoxPlotComponent implements AfterViewInit {
+
+ @Input()width?: number;
+ @Input()height?: number;
+
+ @ViewChild('boxplot') chartRef!: ElementRef;
+ constructor() { }
+
+ boxplotData = {
+ // define label tree
+ labels: ['January'/*, 'February', 'March', 'April', 'May', 'June', 'July'*/],
+ datasets: [{
+ label: 'Dataset 1',
+ backgroundColor: 'rgba(0, 65, 101, 1.0)',
+ borderColor: '#0063AB',
+ borderWidth: 1,
+ outlierColor: '#999999',
+ scaleFontColor: '#0063AB',
+ padding: 10,
+ itemRadius: 0,
+ data: [
+ randomValues(100, 0, 100),
+ /*randomValues(100, 0, 20),
+ randomValues(100, 20, 70),
+ randomValues(100, 60, 100),
+ randomValues(40, 50, 100),
+ randomValues(100, 60, 120),
+ randomValues(100, 80, 100)*/
+ ]}/*, {
+ label: 'Dataset 2',
+ backgroundColor: 'rgba(0,0,255,0.5)',
+ borderColor: 'blue',
+ borderWidth: 1,
+ outlierColor: '#999999',
+ padding: 10,
+ itemRadius: 0,
+ data: [
+ randomValues(100, 60, 100),
+ randomValues(100, 0, 100),
+ randomValues(100, 0, 20),
+ randomValues(100, 20, 70),
+ randomValues(40, 60, 120),
+ randomValues(100, 20, 100),
+ randomValues(100, 80, 100)
+ ]
+ }*/]
+ };
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: "boxplot",
+ data: this.boxplotData,
+ options: {
+ /*title: {
+ display: true,
+ text: 'Predicted world population (millions) in 2050'
+ }*/
+ plugins:{
+ legend: {
+ display: false
+ },
+ },
+ scales : {
+ x: {
+ ticks: {
+ color: 'rgba(0, 65, 101, 1.0)'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ },
+ y : {
+ min: -50,
+ max: 200,
+ ticks: {
+ color: 'rgba(0, 65, 101, 1.0)'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ }
+ }
+ }
+ });
+}
+
+}
diff --git a/frontend/src/app/_elements/carousel/carousel.component.css b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/carousel/carousel.component.css
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.css
diff --git a/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html
new file mode 100644
index 00000000..9c464534
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html
@@ -0,0 +1 @@
+<canvas #doughnut [width]="width" [height]="height"></canvas>
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.spec.ts
index b5c2d91c..67309670 100644
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemPredictorComponent } from './item-predictor.component';
+import { DoughnutChartComponent } from './doughnut-chart.component';
-describe('ItemPredictorComponent', () => {
- let component: ItemPredictorComponent;
- let fixture: ComponentFixture<ItemPredictorComponent>;
+describe('DoughnutChartComponent', () => {
+ let component: DoughnutChartComponent;
+ let fixture: ComponentFixture<DoughnutChartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemPredictorComponent ]
+ declarations: [ DoughnutChartComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemPredictorComponent);
+ fixture = TestBed.createComponent(DoughnutChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts
new file mode 100644
index 00000000..fc13289c
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts
@@ -0,0 +1,37 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'node_modules/chart.js';
+
+@Component({
+ selector: 'app-doughnut-chart',
+ templateUrl: './doughnut-chart.component.html',
+ styleUrls: ['./doughnut-chart.component.css']
+})
+export class DoughnutChartComponent implements AfterViewInit {
+
+ @Input()width: number = 800;
+ @Input()height: number = 450;
+
+ @ViewChild('doughnut') chartRef!: ElementRef;
+ constructor() { }
+
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'doughnut',
+ data: {
+ labels: ["Africa", "Asia", "Europe", "Latin America", "North America"],
+ datasets: [{
+ label: "Population (millions)",
+ backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
+ data: [2478,5267,734,784,433]
+ }]
+ },
+ /*options: {
+ title: {
+ display: true,
+ text: 'Predicted world population (millions) in 2050'
+ }
+ }*/
+ });
+ }
+
+}
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.css b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.css
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.css
diff --git a/frontend/src/app/_elements/_charts/heatmap/heatmap.component.html b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.html
new file mode 100644
index 00000000..52d95516
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.html
@@ -0,0 +1,3 @@
+<div style="width:800px; height: 400px; background-color: red;">
+ <ejs-heatmap [dataSource]='dataSource' [xAxis]='xAxis' [yAxis]='yAxis'></ejs-heatmap>
+</div>
diff --git a/frontend/src/app/_elements/item-model/item-model.component.spec.ts b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.spec.ts
index f696a160..fa0a90cc 100644
--- a/frontend/src/app/_elements/item-model/item-model.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemModelComponent } from './item-model.component';
+import { HeatmapComponent } from './heatmap.component';
-describe('ItemModelComponent', () => {
- let component: ItemModelComponent;
- let fixture: ComponentFixture<ItemModelComponent>;
+describe('HeatmapComponent', () => {
+ let component: HeatmapComponent;
+ let fixture: ComponentFixture<HeatmapComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemModelComponent ]
+ declarations: [ HeatmapComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemModelComponent);
+ fixture = TestBed.createComponent(HeatmapComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/heatmap/heatmap.component.ts b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.ts
new file mode 100644
index 00000000..f3d1af98
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.ts
@@ -0,0 +1,108 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'chart.js';
+import { HeatMapAllModule } from '@syncfusion/ej2-angular-heatmap';
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+
+
+
+@Component({
+ selector: 'app-heatmap',
+ templateUrl: './heatmap.component.html',
+ styleUrls: ['./heatmap.component.css']
+})
+export class HeatmapComponent implements OnInit {
+
+
+ @Input()width?: number;
+ @Input()height?: number;
+
+ dataSource = [
+
+ [73, 39, 26, 39, 94, 0],
+
+ [93, 58, 53, 38, 26, 68],
+
+ [99, 28, 22, 4, 66, 90],
+
+ [14, 26, 97, 69, 69, 3],
+
+ [7, 46, 47, 47, 88, 6],
+
+ [41, 55, 73, 23, 3, 79],
+
+ [56, 69, 21, 86, 3, 33],
+
+ [45, 7, 53, 81, 95, 79],
+
+ [60, 77, 74, 68, 88, 51],
+
+ [25, 25, 10, 12, 78, 14],
+
+ [25, 56, 55, 58, 12, 82],
+
+ [74, 33, 88, 23, 86, 59]
+
+ ];
+
+ xAxis = {
+
+ labels: ['Nancy', 'Andrew', 'Janet', 'Margaret', 'Steven', 'Michael', 'Robert',
+
+ 'Laura', 'Anne', 'Paul', 'Karin', 'Mario'],
+
+ };
+
+ yAxis = {
+
+ labels: ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat'],
+
+ }
+
+ //@ViewChild('heatmap') chartRef!: ElementRef;
+ constructor() { }
+
+ ngOnInit(): void {
+
+
+
+ /*
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'pie',
+ data: {
+ datasets: [{
+ data: [
+
+ [73, 39, 26, 39, 94, 0],
+
+ [93, 58, 53, 38, 26, 68],
+
+ [99, 28, 22, 4, 66, 90],
+
+ [14, 26, 97, 69, 69, 3],
+
+ [7, 46, 47, 47, 88, 6],
+
+ [41, 55, 73, 23, 3, 79],
+
+ [56, 69, 21, 86, 3, 33],
+
+ [45, 7, 53, 81, 95, 79],
+
+ [60, 77, 74, 68, 88, 51],
+
+ [25, 25, 10, 12, 78, 14],
+
+ [25, 56, 55, 58, 12, 82],
+
+ [74, 33, 88, 23, 86, 59]
+
+ ],
+ }]
+ }
+});
+ */
+
+ }
+
+}
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.css b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.css
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
new file mode 100644
index 00000000..c8f406f4
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
@@ -0,0 +1,5 @@
+<div class="chart-wrapper">
+ <canvas id="myChart">
+
+ </canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/model-load/model-load.component.spec.ts b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.spec.ts
index 1dafd966..0c5e7ef5 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ModelLoadComponent } from './model-load.component';
+import { LineChartComponent } from './line-chart.component';
-describe('ModelLoadComponent', () => {
- let component: ModelLoadComponent;
- let fixture: ComponentFixture<ModelLoadComponent>;
+describe('LineChartComponent', () => {
+ let component: LineChartComponent;
+ let fixture: ComponentFixture<LineChartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ModelLoadComponent ]
+ declarations: [ LineChartComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ModelLoadComponent);
+ fixture = TestBed.createComponent(LineChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
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
new file mode 100644
index 00000000..49558025
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts
@@ -0,0 +1,88 @@
+import { Component, AfterViewInit } from '@angular/core';
+import { Chart } from 'chart.js';
+
+@Component({
+ selector: 'app-line-chart',
+ templateUrl: './line-chart.component.html',
+ styleUrls: ['./line-chart.component.css']
+})
+
+export class LineChartComponent implements AfterViewInit {
+
+ dataAcc: number[] = [];
+ dataMAE: number[] = [];
+ dataMSE: number[] = [];
+ dataLOSS: number[] = [];
+
+ dataEpoch: number[] = [];
+
+ constructor() {
+ /*let i = 0;
+ setInterval(() => {
+ this.dataAcc.push(0.5);
+ this.dataEpoch.push(i);
+ i++;
+ this.update();
+ }, 200);*/
+ }
+
+ myChart!: Chart;
+
+ update(myEpochs: number[], myAcc: number[], myLoss: number[], myMae: number[], myMse: number[]) {
+ this.dataAcc.length = 0;
+ this.dataAcc.push(...myAcc);
+
+ this.dataEpoch.length = 0;
+ this.dataEpoch.push(...myEpochs);
+
+ this.dataMAE.length = 0;
+ this.dataMAE.push(...myMae);
+
+ this.dataLOSS.length = 0;
+ this.dataLOSS.push(...myLoss);
+
+ this.dataMSE.length = 0;
+ this.dataMSE.push(...myMse);
+
+ this.myChart.update();
+ }
+
+ ngAfterViewInit(): void {
+ this.myChart = new Chart("myChart",
+ {
+ type: 'line',
+ data: {
+ labels: this.dataEpoch,
+ datasets: [{
+ label: 'Accuracy',
+ data: this.dataAcc,
+ borderWidth: 1
+ },
+ {
+ label: 'Loss',
+ data: this.dataLOSS,
+ borderWidth: 1
+ },
+ {
+ label: 'MAE',
+ data: this.dataMAE,
+ borderWidth: 1
+ },
+ {
+ label: 'MSE',
+ data: this.dataMSE,
+ borderWidth: 1
+ }
+ ]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ }
+ );
+ }
+}
diff --git a/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.css b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.css
diff --git a/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.html b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.html
new file mode 100644
index 00000000..806ea9e8
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.html
@@ -0,0 +1,2 @@
+<canvas #mixedchart width="800" height="450"></canvas>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
diff --git a/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.spec.ts b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.spec.ts
new file mode 100644
index 00000000..361cd047
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MixedChartComponent } from './mixed-chart.component';
+
+describe('MixedChartComponent', () => {
+ let component: MixedChartComponent;
+ let fixture: ComponentFixture<MixedChartComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ MixedChartComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MixedChartComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.ts b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.ts
new file mode 100644
index 00000000..2524ee36
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.ts
@@ -0,0 +1,56 @@
+import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'node_modules/chart.js';
+
+@Component({
+ selector: 'app-mixed-chart',
+ templateUrl: './mixed-chart.component.html',
+ styleUrls: ['./mixed-chart.component.css']
+})
+export class MixedChartComponent implements AfterViewInit {
+
+ @ViewChild('mixedchart') chartRef!: ElementRef;
+ constructor() { }
+
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'bar',
+ data: {
+ labels: ["1900", "1950", "1999", "2050"],
+ datasets: [{
+ label: "Europe",
+ type: "line",
+ borderColor: "#8e5ea2",
+ data: [408,547,675,734],
+ fill: false
+ }, {
+ label: "Africa",
+ type: "line",
+ borderColor: "#3e95cd",
+ data: [133,221,783,2478],
+ fill: false
+ }, {
+ label: "Europe",
+ type: "bar",
+ backgroundColor: "rgba(0,0,0,0.2)",
+ data: [408,547,675,734],
+ }, {
+ label: "Africa",
+ type: "bar",
+ backgroundColor: "rgba(0,0,0,0.2)",
+ //backgroundColorHover: "#3e95cd",
+ data: [133,221,783,2478]
+ }
+ ]
+ },
+ /*options: {
+ title: {
+ display: true,
+ text: 'Population growth (millions): Europe & Africa'
+ },
+ legend: { display: false }
+ }*/
+
+ });
+ }
+
+}
diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.css b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.css
diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html
new file mode 100644
index 00000000..7faf3af0
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html
@@ -0,0 +1,3 @@
+<div class="chart-wrapper">
+ <canvas #piechart [width]="width" [height]="height"></canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.spec.ts b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.spec.ts
new file mode 100644
index 00000000..64f36b7d
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PieChartComponent } from './pie-chart.component';
+
+describe('PieChartComponent', () => {
+ let component: PieChartComponent;
+ let fixture: ComponentFixture<PieChartComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PieChartComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PieChartComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts
new file mode 100644
index 00000000..f141f522
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts
@@ -0,0 +1,46 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'chart.js';
+
+@Component({
+ selector: 'app-pie-chart',
+ templateUrl: './pie-chart.component.html',
+ styleUrls: ['./pie-chart.component.css']
+})
+export class PieChartComponent implements AfterViewInit {
+
+ @Input()width?: number;
+ @Input()height?: number;
+
+ @ViewChild('piechart') chartRef!: ElementRef;
+ constructor() { }
+
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'pie',
+ data: {
+ labels: ["Africa", "Asia", "Europe", "Latin America", "North America"],
+ datasets: [{
+ label: "Population (millions)",
+ backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
+ data: [2478,5267,734,784,433],
+ }]
+ },
+ options: {
+ /*title: {
+ display: true,
+ text: 'Predicted world population (millions) in 2050'
+ }*/
+ plugins:{
+ legend: {
+ display: false
+ },
+ },
+ layout: {
+ padding: 15}
+ }
+});
+
+ }
+
+
+}
diff --git a/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.css b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.css
diff --git a/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.html b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.html
new file mode 100644
index 00000000..f9f9a24a
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.html
@@ -0,0 +1,2 @@
+<canvas #linechart width="800" height="450">Point line chart:</canvas>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.spec.ts b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.spec.ts
index 1da7d05d..fe08fe7c 100644
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemExperimentComponent } from './item-experiment.component';
+import { PointLinechartComponent } from './point-linechart.component';
-describe('ItemExperimentComponent', () => {
- let component: ItemExperimentComponent;
- let fixture: ComponentFixture<ItemExperimentComponent>;
+describe('PointLinechartComponent', () => {
+ let component: PointLinechartComponent;
+ let fixture: ComponentFixture<PointLinechartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemExperimentComponent ]
+ declarations: [ PointLinechartComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemExperimentComponent);
+ fixture = TestBed.createComponent(PointLinechartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.ts b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.ts
new file mode 100644
index 00000000..3497a20c
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.ts
@@ -0,0 +1,57 @@
+import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'node_modules/chart.js';
+
+
+@Component({
+ selector: 'app-point-linechart',
+ templateUrl: './point-linechart.component.html',
+ styleUrls: ['./point-linechart.component.css']
+})
+export class PointLinechartComponent implements AfterViewInit {
+
+ @ViewChild('linechart') chartRef!: ElementRef;
+ constructor() { }
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'line',
+ data: {
+ labels: [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050],
+ datasets: [{
+ data: [86,114,106,106,107,111,133,221,783,2478],
+ label: "Africa",
+ borderColor: "#3e95cd",
+ fill: false
+ }, {
+ data: [282,350,411,502,635,809,947,1402,3700,5267],
+ label: "Asia",
+ borderColor: "#8e5ea2",
+ fill: false
+ }, {
+ data: [168,170,178,190,203,276,408,547,675,734],
+ label: "Europe",
+ borderColor: "#3cba9f",
+ fill: false
+ }, {
+ data: [40,20,10,16,24,38,74,167,508,784],
+ label: "Latin America",
+ borderColor: "#e8c3b9",
+ fill: false
+ }, {
+ data: [6,3,2,2,7,26,82,172,312,433],
+ label: "North America",
+ borderColor: "#c45850",
+ fill: false
+ }
+ ]
+ },
+ /*options: {
+ title: {
+ display: true,
+ text: 'World population per region (in millions)'
+ }
+ }*/
+
+ });
+
+ }
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
new file mode 100644
index 00000000..5735217e
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
@@ -0,0 +1,6 @@
+#divScatterChart{
+ background-color: beige;
+ display: block;
+ width: 400px;
+ height: 200px;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
new file mode 100644
index 00000000..2b30fe1f
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
@@ -0,0 +1,4 @@
+<p>Scatter chart:</p>
+<div id="divScatterChart">
+ <canvas id="ScatterCharts"> </canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.spec.ts b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.spec.ts
new file mode 100644
index 00000000..1db81051
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ScatterchartComponent } from './scatterchart.component';
+
+describe('ScatterchartComponent', () => {
+ let component: ScatterchartComponent;
+ let fixture: ComponentFixture<ScatterchartComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ ScatterchartComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ScatterchartComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts
new file mode 100644
index 00000000..9dfef4c3
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts
@@ -0,0 +1,39 @@
+import { Component, OnInit } from '@angular/core';
+import {Chart} from 'node_modules/chart.js';
+
+@Component({
+ selector: 'app-scatterchart',
+ templateUrl: './scatterchart.component.html',
+ styleUrls: ['./scatterchart.component.css']
+})
+export class ScatterchartComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(){
+ const myChart = new Chart("ScatterCharts", {
+ type: 'scatter',
+ data: {
+ datasets: [{
+ label: 'Scatter Example:',
+ data: [{x: 1, y: 11}, {x:2, y:12}, {x: 1, y: 2}, {x: 2, y: 4}, {x: 3, y: 8},{x: 4, y: 16}, {x: 1, y: 3}, {x: 3, y: 4}, {x: 4, y: 6}, {x: 6, y: 9},
+ {x: 11, y: 9},
+ {x: 12, y: 8},
+ {x: 13, y: 6},
+ {x: 14, y: 0},
+ {x: 15, y: 5},
+ {x: 16, y: 3},
+ {x: 17, y: 2}],
+ backgroundColor: 'rgb(255, 99, 132)'
+ }]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html
deleted file mode 100644
index bff8b022..00000000
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<div class="row mb-4">
- <div class="col-2">
- </div>
- <div class="col-3">
- <label for="name" class="col-form-label">Naziv dataseta:</label>
- <input type="text" class="form-control mb-1" name="name" placeholder="Naziv..." [(ngModel)]="dataset.name">
-
- <label for="desc" class="col-sm-2 col-form-label">Opis:</label>
- <div>
- <textarea class="form-control" name="desc" rows="3" [(ngModel)]="dataset.description"></textarea>
- </div>
-
- <label for="checkboxIsPublic" class="form-check-label mt-3 mb-1">Želite li da dataset bude javan?
- <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="dataset.isPublic" (change)="checkAccessible()" type="checkbox"
- value="" id="checkboxIsPublic">
- </label>
-
- <label for="checkboxAccessibleByLink" class="form-check-label">Želite li da bude deljiv linkom? &nbsp;
- <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="dataset.accessibleByLink" type="checkbox"
- value="" id="checkboxAccessibleByLink">
- </label>
- </div>
- <div class="col-1">
- </div>
- <div class="col-4 mt-4">
-
- <input list="delimiterOptions" placeholder="Izaberite ili ukucajte delimiter za .csv fajl" class="form-control mt-2"
- [(ngModel)]="dataset.delimiter" (input)="update()">
- <datalist id="delimiterOptions">
- <option *ngFor="let option of delimiterOptions">{{option}}</option>
- </datalist>
-
- <label for="type" class="form-check-label my-5">Da li .csv ima header?
- <input class="mx-3 form-check-input" type="checkbox" (input)="update()" [(ngModel)]="dataset.hasHeader" type="checkbox"
- value="" id="checkboxHeader" checked>
- </label>
- <br>
- <input id="fileInput" class="form-control" type="file" class="upload" (change)="changeListener($event)"
- accept=".csv">
- </div>
-</div>
-
-<div class="px-5 mt-5">
- <app-datatable [tableData]="tableData"></app-datatable>
-</div>
-
-<div class="d-flex flex-row align-items-center justify-content-center w-100 my-2">
- <button (click)="uploadDataset()" class="btn btn-lg col-4" style="background-color:#003459; color:white;">Dodaj izvor podataka</button>
-</div>
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.css b/frontend/src/app/_elements/annvisual/annvisual.component.css
deleted file mode 100644
index 857a3390..00000000
--- a/frontend/src/app/_elements/annvisual/annvisual.component.css
+++ /dev/null
@@ -1,4 +0,0 @@
-#graph{
- width: 100%;
- text-align: center;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.html b/frontend/src/app/_elements/annvisual/annvisual.component.html
deleted file mode 100644
index 09251398..00000000
--- a/frontend/src/app/_elements/annvisual/annvisual.component.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<div style="text-align: center; " >
- <button (click)="d3()" mat-raised-button color="primary">Prikaz veštačke neuronske mreže</button>
- <div id="graph" align-items-center style="width: 12rem;"></div>
- </div>
-
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.ts b/frontend/src/app/_elements/annvisual/annvisual.component.ts
deleted file mode 100644
index df0a3898..00000000
--- a/frontend/src/app/_elements/annvisual/annvisual.component.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Component, OnInit, Input } from '@angular/core';
-import Model from 'src/app/_data/Model';
-import { graphviz } from 'd3-graphviz';
-
-@Component({
- selector: 'app-annvisual',
- templateUrl: './annvisual.component.html',
- styleUrls: ['./annvisual.component.css']
-})
-export class AnnvisualComponent implements OnInit {
- ngOnInit(): void {
- }
-
- @Input() model: Model = new Model();
-
- d3() {
- let inputlayerstring: string = '';
- let hiddenlayerstring: string = '';
- let digraphstring: string = 'digraph {';
-
- for (let i = 0; i < /*this.model.inputColumns.length*/ 10; i++) {
- inputlayerstring = inputlayerstring + 'i' + i + ',';
- }
- inputlayerstring = inputlayerstring.slice(0, -1);
-
- digraphstring = digraphstring + inputlayerstring + '->';
-
- for (let j = 0; j < this.model.hiddenLayers; j++) {
- for (let i = 0; i < this.model.hiddenLayerNeurons; i++) {
- hiddenlayerstring = hiddenlayerstring + 'h' + j + '_' + i + ',';
- }
- hiddenlayerstring = hiddenlayerstring.slice(0, -1);
- digraphstring = digraphstring + hiddenlayerstring + '->';
- hiddenlayerstring = '';
- }
- digraphstring = digraphstring + 'o}';
-
- graphviz('#graph').renderDot(digraphstring);
- }
-
- //'digraph {i0,i1,i2->h1,h2,h3->h21,h22,h23->o}'
-}
-
-
-
diff --git a/frontend/src/app/_elements/carousel/carousel.component.html b/frontend/src/app/_elements/carousel/carousel.component.html
deleted file mode 100644
index eb1041ce..00000000
--- a/frontend/src/app/_elements/carousel/carousel.component.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<div class="container">
- <div class="row d-flex align-items-stretch flex-row mx-5 align-items-stretch">
- <div class="col my-1" *ngFor="let item of items" [ngSwitch]="type">
- <ng-template ngSwitchCase="Object">
- Unknown item type
- </ng-template>
- <ng-template ngSwitchCase="Dataset">
- <app-item-dataset [dataset]="item">
- </app-item-dataset>
- </ng-template>
- <ng-template ngSwitchCase="Predictor">
- <app-item-predictor [predictor]="item">
- </app-item-predictor>
- </ng-template>
- </div>
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/carousel/carousel.component.ts b/frontend/src/app/_elements/carousel/carousel.component.ts
deleted file mode 100644
index e0112121..00000000
--- a/frontend/src/app/_elements/carousel/carousel.component.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-carousel',
- templateUrl: './carousel.component.html',
- styleUrls: ['./carousel.component.css']
-})
-export class CarouselComponent {
-
- @Input() items: any[] = [];
- @Input() type: string = "Object";
-
- constructor() { }
-
- ngOnInit(): void {
- }
-
-}
diff --git a/frontend/src/app/_elements/column-table/column-table.component.css b/frontend/src/app/_elements/column-table/column-table.component.css
new file mode 100644
index 00000000..108efb32
--- /dev/null
+++ b/frontend/src/app/_elements/column-table/column-table.component.css
@@ -0,0 +1,223 @@
+table.fixed {
+ table-layout: fixed;
+ display: block;
+ overflow-x: auto;
+ white-space: nowrap;
+ border: 1px solid var(--ns-primary-50);
+ font-size: 12px;
+ border-radius: 4px;
+}
+
+#divTable {
+ height: 100%;
+ overflow-y: auto;
+}
+
+table.fixed td {
+ overflow: hidden;
+ max-width: 200px;
+ min-width: 200px;
+ vertical-align: middle;
+ background-color: var(--ns-bg-dark-100);
+ margin: 4px;
+}
+
+table.fixed th {
+ overflow: hidden;
+ max-width: 120px;
+ min-width: 120px;
+ vertical-align: middle;
+ background-color: var(--ns-bg-dark-100);
+ font-size: 14px;
+}
+
+table.fixed th:first-child {
+ text-align: center;
+ background-color: var(--ns-primary-25);
+}
+
+.columnNames {
+ background-color: var(--ns-primary-50) !important;
+}
+
+.brighter {
+ background-color: var(--ns-primary) !important;
+ border-color: var(--offwhite);
+}
+
+.border-bottom {
+ border-bottom-color: var(--offwhite) !important;
+}
+
+mat-slider {
+ width: 300px;
+}
+
+.slider {
+ background-color: var(--ns-bg-dark-100);
+}
+
+#missingValuesHeader {
+ font-size: 12px;
+ line-height: 110% !important;
+}
+
+.verticalAlign {
+ vertical-align: center;
+}
+
+.cell-align {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ vertical-align: middle;
+ height: 100%;
+}
+
+.text-left {
+ text-align: left !important;
+}
+
+table ::ng-deep .mat-form-field-wrapper {
+ margin-top: -2rem;
+}
+
+.graphics-row {
+ height: 100px;
+ padding: 1px;
+ margin: 0;
+}
+
+.no-pad {
+ padding: 1px;
+ margin: 0;
+}
+
+.text-overflow {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.row-height {
+ height: 30px;
+ border: none;
+ outline: none;
+}
+
+.graphic-class {
+ background-color: white !important;
+}
+
+
+/* TABS STYLE */
+
+#folder-table {
+ border: 1px solid var(--ns-primary);
+ border-radius: 4px;
+ height: 70%;
+}
+
+#tabs {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ height: 3.2rem;
+}
+
+#tabs>.folder-tab:not(:first-child) {
+ margin-left: -5px;
+}
+
+.folder-tab-end {
+ margin-left: auto;
+ color: var(--offwhite) !important;
+ overflow: hidden;
+}
+
+.folder-tab,
+.folder-tab-end {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ position: relative;
+ overflow-x: hidden;
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-color: var(--ns-primary);
+ color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 0 1px;
+}
+
+.folder-tab:not(:first-child) {
+ margin-block-start: auto;
+}
+
+.folder-tab {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.selected-tab {
+ height: 3rem;
+ background-color: var(--ns-primary);
+ color: var(--offwhite);
+}
+
+.hover-tab {
+ height: 3.2rem;
+}
+
+.selected-tab,
+.hover-tab {
+ width: fit-content !important;
+}
+
+.tab-link {
+ color: var(--offwhite) !important;
+ text-decoration: none !important;
+ cursor: pointer;
+}
+
+.tab-link:active {
+ text-decoration: underline !important;
+}
+
+.selected-tab {
+ background-color: var(--ns-primary);
+}
+
+.hidden {
+ visibility: hidden;
+ height: 1px;
+}
+
+.bottom-button {
+ font-size: large;
+ position: relative;
+ background-color: var(--ns-primary);
+ width: 10rem;
+ height: 2.3rem;
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 0px 1px 1px 1px;
+}
+
+#footer {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+}
+
+.pad-fix {
+ padding-top: 0.6rem;
+ padding-bottom: 0;
+}
+
+.long {
+ height: 3rem;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/column-table/column-table.component.html b/frontend/src/app/_elements/column-table/column-table.component.html
new file mode 100644
index 00000000..53cb3551
--- /dev/null
+++ b/frontend/src/app/_elements/column-table/column-table.component.html
@@ -0,0 +1,243 @@
+<div id="tabs">
+ <div class="folder-tab p-1 rounded-top" *ngFor="let tab of tabs; let i = index" [style]="'z-index:' + calcZIndex(i) + ' ;'" [ngClass]="{'selected-tab' : selectedTab.index == i, 'hover-tab' : hoveringOverTab?.index == i}">
+ <a class="m-1 stretched-link tab-link" (click)="selectTab(i)" (mouseenter)="hoverOverTab(i)" (mouseleave)="hoverOverTab(-1)">
+ {{tab.name}}
+ </a>
+ </div>
+ <button mat-button class="p-1 folder-tab-end rounded-top">
+ Kolone
+ <mat-icon>keyboard_double_arrow_down</mat-icon>
+ <!--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 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="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>
+ </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 class="f-row" style="justify-content: space-around;">
+ <div>Potvrdi</div>
+ <div class="icon-double pt-1">
+ <mat-icon>check</mat-icon>
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts b/frontend/src/app/_elements/column-table/column-table.component.spec.ts
index 603889b2..360a8109 100644
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts
+++ b/frontend/src/app/_elements/column-table/column-table.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemDatasetComponent } from './item-dataset.component';
+import { ColumnTableComponent } from './column-table.component';
-describe('ItemDatasetComponent', () => {
- let component: ItemDatasetComponent;
- let fixture: ComponentFixture<ItemDatasetComponent>;
+describe('ColumnTableComponent', () => {
+ let component: ColumnTableComponent;
+ let fixture: ComponentFixture<ColumnTableComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemDatasetComponent ]
+ declarations: [ ColumnTableComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemDatasetComponent);
+ fixture = TestBed.createComponent(ColumnTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/column-table/column-table.component.ts b/frontend/src/app/_elements/column-table/column-table.component.ts
new file mode 100644
index 00000000..9cabf190
--- /dev/null
+++ b/frontend/src/app/_elements/column-table/column-table.component.ts
@@ -0,0 +1,250 @@
+import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChildren } from '@angular/core';
+import Dataset 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';
+
+@Component({
+ selector: 'app-column-table',
+ templateUrl: './column-table.component.html',
+ styleUrls: ['./column-table.component.css']
+})
+export class ColumnTableComponent implements AfterViewInit {
+
+ @Input() dataset?: Dataset;
+ @Input() experiment?: Experiment;
+ @ViewChildren("nullValMenu") nullValMenus!: ElementRef[];
+ @Output() okPressed: EventEmitter<string> = new EventEmitter();
+ Object = Object;
+ Encoding = Encoding;
+ NullValueOptions = NullValueOptions;
+ tableData?: any[][];
+ nullValOption: string[] = [];
+
+ testSetDistribution: number = 70;
+ constructor(private datasetService: DatasetsService, 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();
+
+ 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.nullValOption = [].constructor(this.dataset.columnInfo.length).fill('Obriši redove');
+
+ 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);
+ }
+ });
+ });
+ }
+
+ setDeleteColumnsForMissingValTreatment() {
+ if (this.experiment != undefined) {
+ this.experiment.nullValues = NullValueOptions.DeleteRows;
+ this.experiment.nullValuesReplacers = [];
+ for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ this.experiment.nullValuesReplacers.push({
+ column: this.experiment.inputColumns[i],
+ option: NullValueOptions.DeleteRows,
+ value: ""
+ });
+ }
+ }
+ }
+
+ changeInputColumns(targetMatCheckbox: MatCheckboxChange, columnName: string) {
+ if (this.experiment != undefined) {
+ if (targetMatCheckbox.checked) {
+ if (this.experiment.inputColumns.filter(x => x == columnName)[0] == undefined) {
+ this.experiment.inputColumns.push(columnName);
+ }
+ }
+ else {
+ this.experiment.inputColumns = this.experiment.inputColumns.filter(x => x != columnName);
+ //console.log("Input columns: ", this.experiment.inputColumns);
+ //TODO: da se zatamni kolona koja je unchecked
+ //this.experiment.encodings = this.experiment.encodings.filter(x => x.columnName != columnName); samo na kraju iz enkodinga skloni necekirane
+ this.experiment.nullValuesReplacers = this.experiment.nullValuesReplacers.filter(x => x.column != columnName);
+ }
+ }
+ }
+
+ resetColumnEncodings(encodingType: Encoding) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+ this.experiment.encodings = [];
+ for (let i = 0; i < this.dataset?.columnInfo.length; i++) {
+ this.experiment.encodings.push(new ColumnEncoding(this.dataset?.columnInfo[i].columnName, encodingType));
+ //console.log(this.experiment.encodings);
+ }
+ }
+ }
+ openEncodingDialog() {
+ const dialogRef = this.dialog.open(EncodingDialogComponent, {
+ width: '300px'
+ });
+ dialogRef.afterClosed().subscribe(selectedEncoding => {
+ if (selectedEncoding != undefined)
+ this.resetColumnEncodings(selectedEncoding);
+ });
+ }
+
+ resetMissingValuesTreatment(selectedMissingValuesOption: NullValueOptions) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+
+ if (selectedMissingValuesOption == NullValueOptions.DeleteColumns) {
+ this.experiment.nullValues = NullValueOptions.DeleteColumns;
+ this.experiment.nullValuesReplacers = [];
+ for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ this.experiment.nullValuesReplacers.push({
+ column: this.experiment.inputColumns[i],
+ option: NullValueOptions.DeleteColumns,
+ value: ""
+ });
+ this.nullValOption[i] = "Obriši kolonu";
+ }
+ }
+ else if (selectedMissingValuesOption == NullValueOptions.DeleteRows) {
+ this.experiment.nullValues = NullValueOptions.DeleteRows;
+ this.experiment.nullValuesReplacers = [];
+ for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ this.experiment.nullValuesReplacers.push({
+ column: this.experiment.inputColumns[i],
+ option: NullValueOptions.DeleteRows,
+ value: ""
+ });
+ this.nullValOption[i] = "Obriši redove";
+ }
+ }
+ }
+ }
+ openMissingValuesDialog() {
+ const dialogRef = this.dialog.open(MissingvaluesDialogComponent, {
+ width: '400px'
+ });
+ dialogRef.afterClosed().subscribe(selectedMissingValuesOption => {
+ if (selectedMissingValuesOption != undefined)
+ this.resetMissingValuesTreatment(selectedMissingValuesOption);
+ });
+ }
+ updateTestSet(event: MatSliderChange) {
+ this.testSetDistribution = event.value!;
+ }
+
+
+ MissValsDeleteClicked(event: Event, replacementType: NullValueOptions, index: number) {
+ if (this.experiment != undefined) {
+ let columnName = (<HTMLInputElement>event.currentTarget).value;
+ let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0];
+
+ if (arrayElement == undefined) {
+ this.experiment.nullValuesReplacers.push({
+ column: columnName,
+ option: (replacementType == NullValueOptions.DeleteColumns) ? NullValueOptions.DeleteColumns : NullValueOptions.DeleteRows,
+ value: ""
+ });
+ }
+ else {
+ arrayElement.option = (replacementType == NullValueOptions.DeleteColumns) ? NullValueOptions.DeleteColumns : NullValueOptions.DeleteRows;
+ arrayElement.value = "";
+ }
+
+ this.nullValOption[index] = (replacementType == NullValueOptions.DeleteColumns) ? "Obriši kolonu" : "Obriši redove";
+ }
+ }
+
+ MissValsReplaceClicked(event: Event, columnName: string, index: number) {
+ if (this.experiment != undefined) {
+ let fillValue = (<HTMLInputElement>event.currentTarget).value;
+ let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0];
+
+ if (arrayElement == undefined) {
+ this.experiment.nullValuesReplacers.push({
+ column: columnName,
+ option: NullValueOptions.Replace,
+ value: fillValue
+ });
+ }
+ else {
+ arrayElement.option = NullValueOptions.Replace;
+ arrayElement.value = fillValue;
+ }
+
+ this.nullValOption[index] = "Popuni sa: " + fillValue;
+ }
+ }
+ getValue(columnName: string): string {
+ if (<HTMLInputElement>document.getElementById(columnName) != undefined)
+ return (<HTMLInputElement>document.getElementById(columnName)).value;
+ return '0';
+ }
+ ok() {
+ this.okPressed.emit();
+ }
+
+
+ tabs = [
+ new Tab(0, 'Podešavanja kolona', Table.Columns),
+ new Tab(1, 'Podaci', Table.Data),
+ new Tab(2, 'Korelaciona matrica', Table.CorrelationMatrix)
+ ]
+
+ selectedTab: Tab = this.tabs[0];
+ hoveringOverTab: (Tab | null) = null;
+
+ tabToDisplay: Table = Table.Columns;
+
+ selectTab(index: number) {
+ this.selectedTab = this.tabs[index];
+ this.tabToDisplay = this.tabs[index].value;
+ }
+
+ hoverOverTab(index: number) {
+ if (index < 0) {
+ this.hoveringOverTab = null;
+ this.tabToDisplay = this.selectedTab.value;
+ } else {
+ this.hoveringOverTab = this.tabs[index];
+ this.tabToDisplay = this.tabs[index].value;
+ }
+ }
+
+ calcZIndex(i: number) {
+ let zIndex = (this.tabs.length - i - 1)
+ if (this.selectedTab.index == i)
+ zIndex = this.tabs.length + 1;
+ if (this.hoveringOverTab?.index == i)
+ zIndex = this.tabs.length + 2;
+ return zIndex;
+ }
+
+ Table = Table;
+}
+
+export enum Table {
+ Columns,
+ Data,
+ CorrelationMatrix
+}
+
+export class Tab {
+ constructor(
+ public index: number,
+ public name: string,
+ public value: Table
+ ) { }
+}
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.css b/frontend/src/app/_elements/dataset-load/dataset-load.component.css
deleted file mode 100644
index ff6e2750..00000000
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.btnType1 {
- background-color: #003459;
- color: white;
- padding-top: 2vh;
- padding-bottom: 2vh;
-}
-.btnType2 {
- background-color: white;
- color: #003459;
- border-color: #003459;
- padding-top: 2vh;
- padding-bottom: 2vh;
-
-}
-.selectedDatasetClass {
- /*border-color: 2px solid #003459;*/
- background-color: lightblue;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.html b/frontend/src/app/_elements/dataset-load/dataset-load.component.html
deleted file mode 100644
index 56a3b3c9..00000000
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<div>
-
- <!--Sklonjeno ucitavanje novog dataseta i sve opcije u vezi sa tim, premesteno u add-new-dataset-->
-
- <div class="d-flex flex-row justify-content-center align-items-center mt-3 mb-5">
- <button type="button" id="btnMyDataset" class="btn" (click)="viewMyDatasetsForm()"
- [ngClass]="{'btnType1': showMyDatasets, 'btnType2': !showMyDatasets}">
- Izaberite dataset iz kolekcije
- </button>
- <h3 class="mt-3 mx-3">ili</h3>
- <button type="button" id="btnNewDataset" class="btn" (click)="viewNewDatasetForm()"
- [ngClass]="{'btnType1': !showMyDatasets, 'btnType2': showMyDatasets}">
- Dodajte novi dataset
- </button>
- </div>
-
- <div class="px-5 my-2">
- <input *ngIf="showMyDatasets" type="text" class="form-control" placeholder="Pretraga"
- [(ngModel)]="term">
- </div>
- <div class="px-5" *ngIf="showMyDatasets">
- <div class="overflow-auto" style="max-height: 500px;">
- <ul class="list-group">
- <li class="list-group-item p-3" *ngFor="let dataset of myDatasets|filter:term"
- [ngClass]="{'selectedDatasetClass': this.selectedDataset == dataset}">
- <app-item-dataset name="usersDataset" [dataset]="dataset"
- (click)="selectThisDataset(dataset);"></app-item-dataset>
- </li>
- </ul>
- </div>
- <div class="px-5 mt-5">
- <app-datatable [tableData]="tableData"></app-datatable>
- </div>
- </div>
-
- <app-add-new-dataset [style]="(showMyDatasets)?'display:none;visibility:hidden;':''" id="dataset"
- (newDatasetAdded)="refreshMyDatasets()">
- </app-add-new-dataset>
-
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/datatable/datatable.component.html b/frontend/src/app/_elements/datatable/datatable.component.html
index 8db62aff..17a187ef 100644
--- a/frontend/src/app/_elements/datatable/datatable.component.html
+++ b/frontend/src/app/_elements/datatable/datatable.component.html
@@ -1,18 +1,16 @@
-<div *ngIf="tableData.hasInput">
- <div>
- <div *ngIf="!tableData.loaded" backgroundColor="secondary" style="width: 100%; height: 100%;"
- class="d-flex justify-content-center align-items-center">
+<div *ngIf="tableData.hasInput" class="position-relative">
+ <div class="text-white">
+ <div *ngIf="!tableData.loaded" style="width: 100%; height: 100%;" class="d-flex justify-content-center align-items-center">
<app-loading></app-loading>
</div>
<div *ngIf="tableData.loaded && tableData.data">
- <div id="info" *ngIf="tableData.data.length > 0 && tableData.data[0].length > 0"
- class="d-flex flex-row justify-content-center align-items-center">
+ <div id="info" *ngIf="tableData.data.length > 0 && tableData.data[0].length > 0" class="d-flex flex-row justify-content-center align-items-center">
<div class="fs-5 mb-3">
Tabela {{tableData.numCols}}x{{tableData.numRows}}
</div>
</div>
- <div class="table-responsive" style="overflow: auto; border-radius: 5px;">
- <table *ngIf="tableData.data.length > 0 && tableData.hasHeader && tableData.data[0].length > 0" class="table table-bordered table-light">
+ <div style="border-radius: 5px; overflow-x: auto; overflow-y: hidden;">
+ <table *ngIf="tableData.data.length && tableData.data[0].length > 0" class="table table-responsive table-sm text-offwhite row-height">
<thead>
<tr>
<th *ngFor="let item of tableData.data[0]; let i = index">{{item}}</th>
@@ -22,22 +20,12 @@
<tr *ngFor="let row of tableData.data | slice:1">
<td *ngFor="let col of row">{{col}}</td>
</tr>
- <tr>
- <td colspan="100" class="text-lg-center fs-6">+ {{tableData.numRows - 11}} redova...</td>
- </tr>
- </tbody>
- </table>
- <table *ngIf="tableData.data.length > 0 && !tableData.hasHeader && tableData.data[0].length > 0" class="table table-bordered table-light">
- <tbody>
- <tr *ngFor="let row of tableData.data">
- <td *ngFor="let col of row">{{col}}</td>
- </tr>
- <tr>
- <td colspan="100" class="text-lg-center fs-6">+ {{tableData.numRows - 10}} redova...</td>
- </tr>
</tbody>
</table>
</div>
+ <div class="footer-center" >
+ <div>+ {{tableData.numRows - 11}} redova...</div>
+ </div>
</div>
</div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/folder/folder.component.css b/frontend/src/app/_elements/folder/folder.component.css
new file mode 100644
index 00000000..137a9643
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.css
@@ -0,0 +1,189 @@
+#folder {
+ /*position: absolute;
+ left: 50%;
+ transform: translateX(-50%);*/
+}
+
+#tabs {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ height: 3.1rem;
+}
+
+#tabs>.folder-tab:not(:first-child) {
+ margin-left: -5px;
+}
+
+.folder-tab {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ position: relative;
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-color: var(--ns-primary);
+ color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 0 1px;
+}
+
+.folder-tab:not(:first-child) {
+ margin-block-start: auto;
+}
+
+.selected-tab {
+ height: 3rem;
+ background-color: var(--ns-primary);
+ color: var(--offwhite);
+}
+
+.hover-tab {
+ height: 3.2rem;
+}
+
+.selected-tab,
+.hover-tab {
+ width: fit-content !important;
+}
+
+.tab-link {
+ color: var(--offwhite) !important;
+ text-decoration: none !important;
+ cursor: pointer;
+ padding: 0.5rem;
+}
+
+.tab-link:active {
+ text-decoration: underline !important;
+}
+
+.selected-tab {
+ background-color: var(--ns-primary);
+}
+
+#searchbar {
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-bottom: 1px solid var(--ns-primary);
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: flex-start;
+ width: 100%;
+}
+
+.collapse-horizontal {
+ white-space: nowrap;
+ height: 2.5rem;
+ overflow-x: hidden;
+}
+
+#search-options {
+ margin-left: auto;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ height: 100%;
+}
+
+#selected-content {
+ background-color: var(--ns-bg-dark-50);
+ width: 100%;
+ /*backdrop-filter: blur(2px);*/
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 1px 1px;
+ border-top-right-radius: 4px;
+}
+
+#footer {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+}
+
+.bottom-button {
+ font-size: large;
+ position: relative;
+ background-color: var(--ns-primary);
+ width: 10rem;
+ height: 2.3rem;
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 0px 1px 1px 1px;
+}
+
+.rounded-bottom {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.separator {
+ border-left-color: var(--ns-primary);
+ border-left-width: 1px;
+ border-left-style: solid;
+}
+
+.list-view {
+ height: 100%;
+ overflow-y: auto;
+}
+
+.list-item {
+ height: 3rem;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ border-bottom: 1px solid var(--ns-primary);
+}
+
+.list-item:hover {
+ background-color: var(--ns-bg-dark-100);
+ box-shadow: 0px 3px 3px var(--ns-primary);
+}
+
+.list-item:hover>.hover-hide {
+ display: none;
+}
+
+.folder-inside {
+ width: 100%;
+ height: 40rem;
+ overflow-y: auto;
+}
+
+.file-content {
+ width: 100%;
+ height: 92%;
+ position: relative;
+}
+
+.file-bottom-buttons {
+ position: absolute;
+ bottom: 15px;
+ right: 15px;
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+.file-button {
+ position: relative;
+ color: var(--offwhite);
+ border-radius: 4px;
+ border: 1px solid var(--ns-primary);
+ margin: 5px;
+ padding: 5px;
+ cursor: pointer;
+ z-index: 1001;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+.file-button:hover {
+ background-color: var(--ns-primary);
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/folder/folder.component.html b/frontend/src/app/_elements/folder/folder.component.html
new file mode 100644
index 00000000..36f70c97
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.html
@@ -0,0 +1,99 @@
+<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}">
+ <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]}}
+ </a>
+ </div>
+ <!--<div class="folder-tab p-1 rounded-top" *ngFor="let file of filteredFiles; let i = index" [style]="'z-index:' + calcZIndex(i) + ' ;'" [ngClass]="{'selected-tab' : selectedFileIndex == i, 'hover-tab' : hoveringOverFileIndex == i}">
+ <a class="m-1 stretched-link tab-link" (click)="selectFile(i)" (mouseenter)="hoverOverFile(i)" (mouseleave)="hoverOverFile(-1)">{{file.name}}</a>
+ </div>-->
+ <div class="folder-tab p-1 rounded-top" *ngFor="let tab of tabsToShow; let i = index" [style]="'z-index:' + (selectedTab == tab ? 11 : (tabsToShow.length - i)) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == tab, 'hover-tab' : hoverTab == tab}">
+ <a class="m-1 stretched-link tab-link" (click)="selectTab(tab)" (mouseenter)="hoverOverTab(tab)" (mouseleave)="hoverOverTab(TabType.None)">{{tabTitles[tab]}}</a>
+ </div>
+ </div>
+ <div id="selected-content" class="rounded-bottom text-offwhite">
+ <div id="searchbar" *ngIf="listView">
+ <!-- <div id="path" class="ps-2">{{folderName}}
+ </div>
+ <mat-icon>keyboard_arrow_right</mat-icon> -->
+ <div id="search" class="text-offwhite mx-1">
+ <mat-form-field>
+ <button matPrefix class="btn-clear input-icon"><mat-icon>search</mat-icon></button>
+ <input type="search" matInput name="search" [(ngModel)]="searchTerm" (input)="searchTermsChanged()">
+ <button matSuffix class="btn-clear input-icon" (click)="clearSearchTerm()"><mat-icon>clear</mat-icon></button>
+ </mat-form-field>
+ </div>
+ <div id="search-options">
+ <div id="collapseFilters" class="collapse collapse-horizontal">
+ <mat-icon class="text-offwhite ">timeline</mat-icon>
+ Regresioni
+ <mat-icon class="text-offwhite ">looks_two</mat-icon>
+ Binarni klasifikacioni
+ <mat-icon class="text-offwhite ">auto_awesome_motion</mat-icon>
+ Multiklasifikacioni
+ </div>
+ <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters">
+ <mat-icon>filter_alt</mat-icon>
+ </button>
+ <div id="collapseSort" class="collapse collapse-horizontal">
+ [sort options here TODO]
+ </div>
+ <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseSort" aria-expanded="false" aria-controls="collapseSort">
+ <mat-icon>sort</mat-icon>
+ </button>
+ <!-- <button class="btn-clear icon-toggle separator" [ngClass]="{'icon-toggle-on': listView}" (click)="toggleListView()">
+ <mat-icon>view_list</mat-icon>
+ </button> -->
+ </div>
+ </div>
+ <!--{{fileToDisplay ? fileToDisplay.name : 'No file selected.'}} {{selectedFileIndex}} {{hoveringOverFileIndex}}-->
+ <div [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()">
+ <mat-icon>delete</mat-icon>
+ </button>
+ <button class="btn-clear file-button">
+ <mat-icon>link</mat-icon>
+ </button>
+ <button class="btn-clear file-button">
+ <mat-icon>zoom_out_map</mat-icon>
+ </button>
+ </div>
+ <app-form-model [forExperiment]="forExperiment" [model]="fileToDisplay" *ngSwitchCase="FolderType.Model"></app-form-model>
+ <app-form-dataset *ngSwitchCase="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 class="mx-2">
+ <a class="force-link" (click)="selectFile(i)">{{file.name}}</a>
+ </div>
+ <div class="mx-2 hover-hide">
+ {{file.lastUpdated | date}}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer" [ngSwitch]="newFileSelected">
+ <button mat-button (click)="saveNewFile()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="true">
+ <div class="f-row">
+ <div>Sačuvaj</div>
+ <div class="pt-1">
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ <button mat-button (click)="ok()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="false">
+ <div class="f-row">
+ <div>Ok</div>
+ <div class="icon-double pt-1">
+ <mat-icon>check</mat-icon>
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/folder/folder.component.spec.ts b/frontend/src/app/_elements/folder/folder.component.spec.ts
new file mode 100644
index 00000000..33a573a7
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FolderComponent } from './folder.component';
+
+describe('FolderComponent', () => {
+ let component: FolderComponent;
+ let fixture: ComponentFixture<FolderComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ FolderComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FolderComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts
new file mode 100644
index 00000000..06b4d893
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.ts
@@ -0,0 +1,276 @@
+import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import { FolderFile, FolderType } from 'src/app/_data/FolderFile';
+import Model from 'src/app/_data/Model';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+import shared from 'src/app/Shared';
+import { ModelsService } from 'src/app/_services/models.service';
+import { FormDatasetComponent } from '../form-dataset/form-dataset.component';
+import Experiment from 'src/app/_data/Experiment';
+import { ExperimentsService } from 'src/app/_services/experiments.service';
+import { PredictorsService } from 'src/app/_services/predictors.service';
+
+@Component({
+ selector: 'app-folder',
+ templateUrl: './folder.component.html',
+ styleUrls: ['./folder.component.css']
+})
+export class FolderComponent implements OnInit {
+
+ @ViewChild(FormDatasetComponent) formDataset?: FormDatasetComponent;
+
+ @Input() folderName: string = 'Moji podaci';
+
+ @Input() files!: FolderFile[]
+
+ newFile!: Dataset | Model;
+
+ @Input() type: FolderType = FolderType.Dataset;
+
+ @Input() forExperiment?: Experiment;
+
+ newFileSelected: boolean = true;
+
+ selectedFileIndex: number = -1;
+ selectedFile?: FolderFile;
+ hoveringOverFileIndex: number = -1;
+
+ fileToDisplay?: FolderFile;
+
+ @Output() selectedFileChanged: EventEmitter<FolderFile> = new EventEmitter();
+ @Output() okPressed: EventEmitter<string> = new EventEmitter();
+
+ searchTerm: string = '';
+
+ constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService) {
+ //PLACEHOLDER
+ this.forExperiment = new Experiment();
+ this.forExperiment.inputColumns = ['kolona1', 'kol2', '???', 'test'];
+
+ this.folders[TabType.File] = [];
+ this.folders[TabType.NewFile] = [];
+
+ this.refreshFiles();
+
+
+ }
+
+ ngOnInit(): void {
+ if (this.files.length > 0)
+ this.selectFile(0);
+ else {
+ this.selectNewFile();
+ }
+ }
+
+ displayFile(){
+ if(this.type == FolderType.Dataset)
+ this.formDataset!.dataset = <Dataset>this.fileToDisplay;
+ }
+
+ hoverOverFile(i: number) {
+ this.hoveringOverFileIndex = i;
+ if (i != -1) {
+ this.fileToDisplay = this.files[i];
+ } else {
+ if (this.newFileSelected) {
+ this.fileToDisplay = this.newFile;
+ } else {
+ this.fileToDisplay = this.files[this.selectedFileIndex];
+ }
+ }
+ this.displayFile();
+ }
+
+ selectNewFile() {
+ if (!this.newFile) {
+ this.createNewFile();
+ }
+ this.fileToDisplay = this.newFile;
+ this.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];
+ this.newFileSelected = false;
+ this.listView = false;
+ this.selectedFileChanged.emit(this.selectedFile);
+ this.displayFile();
+ }
+
+ createNewFile() {
+ if (this.type == FolderType.Dataset) {
+ this.newFile = new Dataset();
+ } else if (this.type == FolderType.Model) {
+ this.newFile = new Model();
+ }
+ }
+
+ ok() {
+ this.okPressed.emit();
+ }
+
+ refreshFiles(){
+ this.datasetsService.getMyDatasets().subscribe((datasets) => {
+ this.folders[TabType.MyDatasets] = datasets;
+ });
+
+ this.datasetsService.getPublicDatasets().subscribe((datasets) => {
+ this.folders[TabType.PublicDatasets] = datasets;
+ });
+
+ this.modelsService.getMyModels().subscribe((models) => {
+ this.folders[TabType.MyModels] = models;
+ });
+
+ /*this.modelsService.getMyModels().subscribe((models) => {
+ this.folders[TabType.PublicModels] = models;
+ });*/
+ this.folders[TabType.PublicModels] = [];
+
+ this.experimentsService.getMyExperiments().subscribe((experiments) => {
+ this.folders[TabType.MyExperiments] = experiments;
+ });
+
+ this.files = [];
+
+ this.filteredFiles.length = 0;
+ this.filteredFiles.push(...this.files);
+
+ this.searchTermsChanged();
+
+ }
+
+ saveNewFile() {
+ if(this.type == FolderType.Dataset)
+ this.formDataset!.uploadDataset();
+ }
+
+ /*calcZIndex(i: number) {
+ let zIndex = (this.files.length - i - 1)
+ if (this.selectedFileIndex == i)
+ zIndex = this.files.length + 2;
+ if (this.hoveringOverFileIndex == i)
+ zIndex = this.files.length + 3;
+ return zIndex;
+ }
+
+ newFileZIndex() {
+ return (this.files.length + 1);
+ }*/
+
+ clearSearchTerm() {
+ this.searchTerm = '';
+ this.searchTermsChanged();
+ }
+
+ filteredFiles: FolderFile[] = [];
+
+ searchTermsChanged() {
+ this.filteredFiles.length = 0;
+ this.filteredFiles.push(...this.files.filter((file) => file.name.toLowerCase().includes(this.searchTerm.toLowerCase())));
+ if (this.selectedFile) {
+ if (!this.filteredFiles.includes(this.selectedFile)) {
+ this.selectFile(-1);
+ } else {
+ this.selectedFileIndex = this.filteredFiles.indexOf(this.selectedFile);
+ }
+ }
+ }
+
+ listView: boolean = false;
+
+ toggleListView() {
+ this.listView = !this.listView;
+ }
+
+ deleteFile() {
+ console.log('delete');
+ }
+
+ folders: { [tab: number]: FolderFile[] } = {};
+
+ tabTitles: { [tab: number]: string } = {
+ [TabType.File]: 'Fajl',
+ [TabType.NewFile]: 'Novi fajl',
+ [TabType.MyDatasets]: 'Moji izvori podataka',
+ [TabType.PublicDatasets]: 'Javni izvori podataka',
+ [TabType.MyModels]: 'Moje konfiguracije neuronske mreže',
+ [TabType.PublicModels]: 'Javne konfiguracije neuronske mreže',
+ [TabType.MyExperiments]: 'Eksperimenti',
+ };
+
+ FolderType = FolderType;
+
+ TabType = TabType;
+
+ @Input() tabsToShow: TabType[] = [
+ TabType.MyDatasets,
+ TabType.PublicDatasets,
+ TabType.MyModels,
+ TabType.PublicModels,
+ TabType.MyExperiments,
+ TabType.File
+ ]
+
+ @Input() selectedTab: TabType = TabType.NewFile;
+ hoverTab: TabType = TabType.None;
+
+ selectTab(tab: TabType) {
+ this.checkListView(tab);
+ this.selectedTab = tab;
+ this.files = this.folders[tab];
+
+ this.searchTermsChanged();
+ }
+
+ checkListView(tab: TabType) {
+ switch (tab) {
+ case TabType.File:
+ case TabType.NewFile:
+ case TabType.None:
+ this.listView = false;
+ break;
+ case TabType.MyExperiments:
+ case TabType.MyDatasets:
+ case TabType.MyModels:
+ case TabType.PublicDatasets:
+ case TabType.PublicModels:
+ this.listView = true;
+ break;
+ }
+ }
+
+ hoverOverTab(tab: TabType) {
+ this.checkListView(tab);
+ this.hoverTab = tab;
+ if (tab == TabType.None) {
+ this.checkListView(this.selectedTab);
+ this.files = this.folders[this.selectedTab];
+ } else {
+ this.files = this.folders[tab];
+ }
+ this.searchTermsChanged();
+ }
+}
+
+export enum Privacy {
+ Private,
+ Public
+}
+
+export enum TabType {
+ NewFile,
+ File,
+ MyDatasets,
+ PublicDatasets,
+ MyModels,
+ PublicModels,
+ MyExperiments,
+ None
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.css b/frontend/src/app/_elements/form-dataset/form-dataset.component.css
new file mode 100644
index 00000000..da31cfcb
--- /dev/null
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.css
@@ -0,0 +1,69 @@
+.folderBox {
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+
+.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;
+}
+
+.dottedClass {
+ border: 4px dotted white;
+ border-radius: 25px;
+}
+
+.hidden {
+ visibility: hidden;
+}
+
+.file {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ 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%;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.html b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
new file mode 100644
index 00000000..2176b130
--- /dev/null
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
@@ -0,0 +1,74 @@
+<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="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>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm mb-3">
+
+ <!--<input id="fileInput" class="form-control btn-lg" type="file" class="upload" (change)="changeListener($event)" accept=".csv">
+ -->
+ </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>
+
+ <div class="btn-group" role="group" aria-label="Button group with nested dropdown">
+
+ </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/dataset-load/dataset-load.component.spec.ts b/frontend/src/app/_elements/form-dataset/form-dataset.component.spec.ts
index 5601b57b..51491c58 100644
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.spec.ts
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { DatasetLoadComponent } from './dataset-load.component';
+import { FormDatasetComponent } from './form-dataset.component';
-describe('DatasetLoadComponent', () => {
- let component: DatasetLoadComponent;
- let fixture: ComponentFixture<DatasetLoadComponent>;
+describe('FormDatasetComponent', () => {
+ let component: FormDatasetComponent;
+ let fixture: ComponentFixture<FormDatasetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ DatasetLoadComponent ]
+ declarations: [ FormDatasetComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(DatasetLoadComponent);
+ fixture = TestBed.createComponent(FormDatasetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
index 3e1b5c73..63376524 100644
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
@@ -1,34 +1,42 @@
-import { Component, EventEmitter, Output, ViewChild } from '@angular/core';
+import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import Dataset from 'src/app/_data/Dataset';
import { DatasetsService } from 'src/app/_services/datasets.service';
import { ModelsService } from 'src/app/_services/models.service';
import shared from 'src/app/Shared';
import { DatatableComponent, TableData } from '../datatable/datatable.component';
import { CsvParseService } from 'src/app/_services/csv-parse.service';
+import {FormControl, Validators} from '@angular/forms';
@Component({
- selector: 'app-add-new-dataset',
- templateUrl: './add-new-dataset.component.html',
- styleUrls: ['./add-new-dataset.component.css']
+ selector: 'app-form-dataset',
+ templateUrl: './form-dataset.component.html',
+ styleUrls: ['./form-dataset.component.css']
})
-export class AddNewDatasetComponent {
+export class FormDatasetComponent {
- @Output() newDatasetAdded = new EventEmitter<string>();
@ViewChild(DatatableComponent) datatable!: DatatableComponent;
- delimiterOptions: Array<string> = [",", ";", "\t", "razmak", "|"]; //podrazumevano ","
+ nameFormControl = new FormControl('', [Validators.required, Validators.email]);
+
+ delimiterOptions: Array<string> = [",", ";", "|", "razmak", "novi red"]; //podrazumevano ","
csvRecords: any[] = [];
files: File[] = [];
rowsNumber: number = 0;
colsNumber: number = 0;
- dataset: Dataset; //dodaj ! potencijalno
+ @Input() dataset: Dataset; //dodaj ! potencijalno
tableData: TableData = new TableData();
+ @ViewChild('fileInput') fileInput! : ElementRef
+
+ filename: String;
+
constructor(private modelsService: ModelsService, private datasetsService: DatasetsService, private csv: CsvParseService) {
this.dataset = new Dataset();
+ this.dataset.delimiter = ',';
+ this.filename = "";
}
//@ViewChild('fileImportInput', { static: false }) fileImportInput: any; cemu je ovo sluzilo?
@@ -36,14 +44,13 @@ export class AddNewDatasetComponent {
changeListener($event: any): void {
this.files = $event.srcElement.files;
if (this.files.length == 0 || this.files[0] == null) {
- //console.log("NEMA FAJLA");
- //this.loaded.emit("not loaded");
this.tableData.hasInput = false;
return;
}
else
this.tableData.hasInput = true;
+ this.filename = this.files[0].name;
this.tableData.loaded = false;
this.update();
}
@@ -56,7 +63,7 @@ export class AddNewDatasetComponent {
const fileReader = new FileReader();
fileReader.onload = (e) => {
if (typeof fileReader.result === 'string') {
- const result = this.csv.csvToArray(fileReader.result, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter)
+ const result = this.csv.csvToArray(fileReader.result, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter)
if (this.dataset.hasHeader)
this.csvRecords = result.splice(0, 11);
@@ -74,6 +81,8 @@ export class AddNewDatasetComponent {
}
}
fileReader.readAsText(this.files[0]);
+
+ this.dataset.name = this.filename.slice(0, this.filename.length - 4);
}
checkAccessible() {
@@ -93,7 +102,6 @@ export class AddNewDatasetComponent {
this.dataset.uploaderId = shared.userId;
this.datasetsService.addDataset(this.dataset).subscribe((dataset) => {
- this.newDatasetAdded.emit("added");
shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira.");
}, (error) => {
shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset.");
@@ -103,4 +111,6 @@ export class AddNewDatasetComponent {
}); //kraj uploadData subscribe
}
+
+
}
diff --git a/frontend/src/app/_elements/form-model/form-model.component.css b/frontend/src/app/_elements/form-model/form-model.component.css
new file mode 100644
index 00000000..8c279523
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.css
@@ -0,0 +1,87 @@
+#container {
+ color: var(--offwhite);
+}
+
+mat-label {
+ color: var(--offwhite) !important;
+}
+
+select {
+ color: var(--offwhite) !important;
+}
+
+mat-form-field {
+ color: var(--offwhite) !important;
+ padding: 0;
+ margin: 5px;
+ font-size: 12px;
+ width: 100%;
+}
+
+hr {
+ color: var(--offwhite) !important;
+ margin-bottom: 30px;
+}
+
+.neuron {
+ text-align: justify;
+ border: 1px solid white;
+ border-radius: 5px;
+ padding: 0;
+ color: var(--offwhite) !important;
+ background-color: var(--ns-bg-dark-100) !important;
+ min-width: none;
+ max-width: 12.5rem;
+}
+
+.row {
+ margin: 0;
+ padding: 0;
+}
+
+.mat-fix ::ng-deep .mat-form-field-wrapper {
+ margin-bottom: -1.85em;
+}
+
+#layers-control {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+#layers {
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: row;
+ overflow-x: auto;
+ overflow-wrap: break-word;
+ overflow-y: hidden;
+ width: 100%;
+}
+
+.layer {
+ border: 1px solid var(--ns-primary);
+ border-radius: 4px;
+ margin: 5px;
+ padding: 0px;
+ width: 12rem;
+ height: 11.1rem;
+}
+
+.tm {
+ margin-left: 10px;
+}
+
+.layer>* {
+ margin-top: 0;
+}
+
+.layer>mat-form-field {
+ margin-left: 0;
+}
+
+.m-2 {
+ max-height: 20 rem;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-model/form-model.component.html b/frontend/src/app/_elements/form-model/form-model.component.html
new file mode 100644
index 00000000..76601465
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.html
@@ -0,0 +1,202 @@
+<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 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-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)">
+ <mat-icon>add</mat-icon>
+ </button>
+ <div class="col-2 text-center">{{newModel.layers[i].neurons}}</div>
+ <button class="btn-clear btn-icon bubble" (click)="removeNeuron(i)">
+ <mat-icon>remove</mat-icon>
+ </button>
+ </div>
+
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Regularizacija</mat-label>
+ <mat-select [(ngModel)]="newModel.layers[i].regularisation">
+ <mat-option *ngFor="let option of Object.keys(Regularisation); let optionName of Object.values(Regularisation)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Stopa regularizacije</mat-label>
+ <mat-select [(ngModel)]="newModel.layers[i].regularisationRate">
+ <mat-option *ngFor="let option of Object.keys(RegularisationRate); let optionName of Object.values(RegularisationRate)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-model/form-model.component.spec.ts b/frontend/src/app/_elements/form-model/form-model.component.spec.ts
new file mode 100644
index 00000000..af1091cc
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FormModelComponent } from './form-model.component';
+
+describe('FormModelComponent', () => {
+ let component: FormModelComponent;
+ let fixture: ComponentFixture<FormModelComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ FormModelComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FormModelComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/form-model/form-model.component.ts b/frontend/src/app/_elements/form-model/form-model.component.ts
new file mode 100644
index 00000000..2c78cd56
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.ts
@@ -0,0 +1,138 @@
+import { Component, OnInit, Input, ViewChild, Output, EventEmitter, AfterViewInit } from '@angular/core';
+import { FormControl, Validators } from '@angular/forms';
+import Shared from 'src/app/Shared';
+import Experiment from 'src/app/_data/Experiment';
+import Model, { Layer, ActivationFunction, LossFunction, LearningRate, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType, Regularisation, RegularisationRate, BatchSize } from 'src/app/_data/Model';
+import { GraphComponent } from '../graph/graph.component';
+
+
+@Component({
+ selector: 'app-form-model',
+ templateUrl: './form-model.component.html',
+ styleUrls: ['./form-model.component.css']
+})
+export class FormModelComponent implements AfterViewInit {
+ @ViewChild(GraphComponent) graph!: GraphComponent;
+ @Input() forExperiment?: Experiment;
+ @Output() selectedModelChangeEvent = new EventEmitter<Model>();
+
+ constructor() { }
+
+ ngAfterViewInit(): void {
+ }
+
+ selectFormControl = new FormControl('', Validators.required);
+ nameFormControl = new FormControl('', [Validators.required, Validators.email]);
+ selectTypeFormControl = new FormControl('', Validators.required);
+ selectOptFormControl = new FormControl('', Validators.required);
+ selectLFFormControl = new FormControl('', Validators.required);
+ selectLRFormControl = new FormControl('', Validators.required);
+ selectEpochFormControl = new FormControl('', Validators.required);
+ selectAFFormControl = new FormControl('', Validators.required);
+ selectBSFormControl = new FormControl('', Validators.required);
+ selectActivationFormControl = new FormControl('', Validators.required);
+ selectRegularisationFormControl = new FormControl('', Validators.required);
+ selectRRateFormControl = new FormControl('', Validators.required);
+
+ newModel: Model = new Model();
+ myModels?: Model[];
+
+ selectedModel?: Model;
+
+ ProblemType = ProblemType;
+ ActivationFunction = ActivationFunction;
+ RegularisationRate = RegularisationRate;
+ Regularisation = Regularisation;
+ metrics: any = Metrics;
+ LossFunction = LossFunction;
+ Optimizer = Optimizer;
+ BatchSize = BatchSize;
+ Object = Object;
+ document = document;
+ shared = Shared;
+ LearningRate = LearningRate;
+ Layer = Layer;
+
+ term: string = "";
+ selectedMetrics = [];
+ lossFunction: any = LossFunction;
+
+ showMyModels: boolean = true;
+
+ updateGraph() {
+ //console.log(this.newModel.layers);
+ this.graph.update();
+ }
+
+ removeLayer() {
+ if (this.newModel.hiddenLayers > 1) {
+ this.newModel.layers.splice(this.newModel.layers.length - 1, 1);
+ this.newModel.hiddenLayers -= 1;
+ this.updateGraph();
+ }
+ }
+ addLayer() {
+ if (this.newModel.hiddenLayers < 128) {
+ this.newModel.layers.push(new Layer(this.newModel.layers.length, this.selectedActivation, this.selectedNumberOfNeurons, this.selectedRegularisation, this.selectedRegularisationRate));
+
+ this.newModel.hiddenLayers += 1;
+ this.updateGraph();
+ }
+
+ }
+ /*
+ setNeurons()
+ {
+ for(let i=0;i<this.newModel.hiddenLayers;i++){
+ this.newModel.hiddenLayerNeurons[i]=1;
+ }
+ }*/
+ numSequence(n: number): Array<number> {
+ return Array(n);
+ }
+
+ removeNeuron(index: number) {
+ if (this.newModel.layers[index].neurons > 1) {
+ this.newModel.layers[index].neurons -= 1;
+ this.updateGraph();
+ }
+ }
+ addNeuron(index: number) {
+ if (this.newModel.layers[index].neurons < 18) {
+ this.newModel.layers[index].neurons += 1;
+ this.updateGraph();
+ }
+ }
+ selectedActivation: ActivationFunction = ActivationFunction.Sigmoid;
+ selectedRegularisationRate: RegularisationRate = RegularisationRate.RR1;
+ selectedRegularisation: Regularisation = Regularisation.L1;
+ selectedNumberOfNeurons: number = 3;
+
+ changeAllActivation() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].activationFunction = this.selectedActivation;
+
+ }
+
+ }
+ changeAllRegularisation() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].regularisation = this.selectedRegularisation;
+ }
+ }
+ changeAllRegularisationRate() {
+
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].regularisationRate = this.selectedRegularisationRate;
+ }
+ }
+ changeAllNumberOfNeurons() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].neurons = this.selectedNumberOfNeurons;
+ this.updateGraph();
+ }
+ }
+
+
+
+}
diff --git a/frontend/src/app/_elements/gradient-background/gradient-background.component.css b/frontend/src/app/_elements/gradient-background/gradient-background.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_elements/gradient-background/gradient-background.component.css
diff --git a/frontend/src/app/_elements/gradient-background/gradient-background.component.html b/frontend/src/app/_elements/gradient-background/gradient-background.component.html
new file mode 100644
index 00000000..3f30c35e
--- /dev/null
+++ b/frontend/src/app/_elements/gradient-background/gradient-background.component.html
@@ -0,0 +1 @@
+<div #holder style="position: fixed; z-index: -1;" [ngStyle]="{'background': color}"></div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.spec.ts b/frontend/src/app/_elements/gradient-background/gradient-background.component.spec.ts
index a9ea25b4..969c73b7 100644
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.spec.ts
+++ b/frontend/src/app/_elements/gradient-background/gradient-background.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { AddNewDatasetComponent } from './add-new-dataset.component';
+import { GradientBackgroundComponent } from './gradient-background.component';
-describe('AddNewDatasetComponent', () => {
- let component: AddNewDatasetComponent;
- let fixture: ComponentFixture<AddNewDatasetComponent>;
+describe('GradientBackgroundComponent', () => {
+ let component: GradientBackgroundComponent;
+ let fixture: ComponentFixture<GradientBackgroundComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ AddNewDatasetComponent ]
+ declarations: [ GradientBackgroundComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(AddNewDatasetComponent);
+ fixture = TestBed.createComponent(GradientBackgroundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/gradient-background/gradient-background.component.ts b/frontend/src/app/_elements/gradient-background/gradient-background.component.ts
new file mode 100644
index 00000000..1414bc60
--- /dev/null
+++ b/frontend/src/app/_elements/gradient-background/gradient-background.component.ts
@@ -0,0 +1,47 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+
+@Component({
+ selector: 'app-gradient-background',
+ templateUrl: './gradient-background.component.html',
+ styleUrls: ['./gradient-background.component.css']
+})
+export class GradientBackgroundComponent implements AfterViewInit {
+
+ @ViewChild('holder') holderRef!: ElementRef;
+ private holder!: HTMLDivElement;
+
+
+ @Input() colorHorizontal1 = 'rgba(0, 8, 45, 0.5)';
+ @Input() colorHorizontal2 = 'rgba(0, 52, 89, 0.5)';
+
+ @Input() colorVertical1 = 'rgba(0, 52, 89, 0.5)';
+ @Input() colorVertical2 = 'rgba(0, 152, 189, 0.5)';
+
+ constructor() { }
+
+ color: string = this.gradientHorizontal();
+
+ private width = 0;
+ private height = 0;
+
+ gradientHorizontal(): string {
+ return `linear-gradient(90deg, ${this.colorHorizontal1} 0%, ${this.colorHorizontal2} 50%, ${this.colorHorizontal1} 100%), linear-gradient(0deg, ${this.colorVertical1} 0%, ${this.colorVertical2} 100%)`;
+ }
+
+ resize() {
+ this.width = window.innerWidth;
+ this.height = window.innerHeight;
+
+ this.holder.style.width = this.width + 'px';
+ this.holder.style.height = this.height + 'px';
+ }
+
+ ngAfterViewInit(): void {
+ this.holder = <HTMLDivElement>this.holderRef.nativeElement;
+
+ window.addEventListener('resize', () => this.resize());
+ this.resize();
+
+ }
+
+}
diff --git a/frontend/src/app/_elements/graph/graph.component.css b/frontend/src/app/_elements/graph/graph.component.css
index e69de29b..361e7249 100644
--- a/frontend/src/app/_elements/graph/graph.component.css
+++ b/frontend/src/app/_elements/graph/graph.component.css
@@ -0,0 +1,17 @@
+.node-text {
+ position: absolute;
+ color: transparent;
+ width: 100px;
+ height: 40px;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ /*border: 1px solid red;*/
+ transform: translate(-50%, -50%);
+}
+
+.node-text:hover {
+ color: var(--offwhite);
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/graph/graph.component.html b/frontend/src/app/_elements/graph/graph.component.html
index 1c21fb6c..19e5c14a 100644
--- a/frontend/src/app/_elements/graph/graph.component.html
+++ b/frontend/src/app/_elements/graph/graph.component.html
@@ -1,3 +1,8 @@
-<div #graphWrapper class="w-100" style="height: 16rem;">
- <canvas #graphCanvas class="border"></canvas>
-</div> \ No newline at end of file
+<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">
+ {{ 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>
+ <canvas #graphCanvas></canvas>
+ </div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/graph/graph.component.ts b/frontend/src/app/_elements/graph/graph.component.ts
index 8051acc3..31814c2c 100644
--- a/frontend/src/app/_elements/graph/graph.component.ts
+++ b/frontend/src/app/_elements/graph/graph.component.ts
@@ -1,6 +1,7 @@
import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
-import Dataset from 'src/app/_data/Dataset';
-import Model from 'src/app/_data/Model';
+import { RgbColor } from '@syncfusion/ej2-angular-heatmap';
+import Dataset, { ColumnInfo } from 'src/app/_data/Dataset';
+import Model, { Layer } from 'src/app/_data/Model';
@Component({
selector: 'app-graph',
@@ -15,17 +16,19 @@ export class GraphComponent implements AfterViewInit {
canvas!: ElementRef;
@Input() model?: Model;
- @Input() inputCols: number = 1;
+ //@Input() inputCols: number = 1;
@Input() lineThickness: number = 2;
@Input() nodeRadius: number = 15;
- @Input() lineColor: string = '#00a8e8';
+ @Input() lineColor1: RgbColor = new RgbColor(0, 168, 232);
+ @Input() lineColor2: RgbColor = new RgbColor(0, 70, 151);
@Input() nodeColor: string = '#222277';
@Input() borderColor: string = '#00a8e8';
- @Input() inputNodeColor: string = '#ffdd11';
- @Input() outputNodeColor: string = '#44ee22';
+ @Input() inputNodeColor: string = '#00a8e8';
+ @Input() outputNodeColor: string = '#dfd7d7';
- private ctx?: CanvasRenderingContext2D;
+ private ctx!: CanvasRenderingContext2D;
+ @Input() inputColumns?: string[] = [];
constructor() { }
@@ -42,16 +45,16 @@ export class GraphComponent implements AfterViewInit {
this.resize();
}
- layers?: Node[][];
+ layers: Node[][] = [];
update() {
- this.layers = [];
+ this.layers.length = 0;
let inputNodeIndex = 0;
const inputLayer: Node[] = [];
- while (inputNodeIndex < this.inputCols) {
+ while (this.inputColumns && inputNodeIndex < this.inputColumns.length) {
const x = 0.5 / (this.model!.hiddenLayers + 2);
- const y = (inputNodeIndex + 0.5) / this.inputCols;
+ const y = (inputNodeIndex + 0.5) / this.inputColumns.length;
const node = new Node(x, y, this.inputNodeColor);
inputLayer.push(node);
inputNodeIndex += 1;
@@ -62,9 +65,9 @@ export class GraphComponent implements AfterViewInit {
while (layerIndex < this.model!.hiddenLayers + 1) {
const newLayer: Node[] = [];
let nodeIndex = 0;
- while (nodeIndex < this.model!.hiddenLayerNeurons) {
+ while (nodeIndex < this.model!.layers[layerIndex - 1].neurons) {
const x = (layerIndex + 0.5) / (this.model!.hiddenLayers + 2);
- const y = (nodeIndex + 0.5) / this.model!.hiddenLayerNeurons;
+ const y = (nodeIndex + 0.5) / this.model!.layers[layerIndex - 1].neurons;
const node = new Node(x, y, this.nodeColor);
newLayer.push(node);
nodeIndex += 1;
@@ -80,7 +83,7 @@ export class GraphComponent implements AfterViewInit {
}
draw() {
- this.ctx!.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
+ this.ctx.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
let index = 0;
while (index < this.layers!.length - 1) {
@@ -93,29 +96,38 @@ export class GraphComponent implements AfterViewInit {
}
for (let layer of this.layers!) {
- for (let node of layer) {
- this.drawNode(node);
- }
+ layer.forEach((node, index) => {
+ this.drawNode(node, 0.5 / layer.length + 0.5);
+ });
}
}
+ bezierOffset = 5;
+
drawLine(node1: Node, node2: Node) {
- this.ctx!.strokeStyle = this.lineColor;
- this.ctx!.lineWidth = this.lineThickness;
- this.ctx!.beginPath();
- this.ctx!.moveTo(node1.x * this.width, node1.y * this.height);
- this.ctx!.lineTo(node2.x * this.width, node2.y * this.height);
- this.ctx!.stroke();
+ const lineColor: RgbColor = this.lerpColor(this.lineColor1, this.lineColor2, node1.y);
+ this.ctx.strokeStyle = `rgb(${lineColor.R}, ${lineColor.G}, ${lineColor.B})`;
+ this.ctx.lineWidth = this.lineThickness;
+ this.ctx.beginPath();
+ this.ctx.moveTo(node1.x * this.width, node1.y * this.height);
+ //this.ctx.lineTo(node2.x * this.width, node2.y * this.height);
+ const middle = (node1.x + (node2.x - node1.x) / 2) * this.width;
+ this.ctx.bezierCurveTo(
+ middle, node1.y * this.height,
+ middle, node2.y * this.height,
+ node2.x * this.width, node2.y * this.height);
+ this.ctx.stroke();
}
- drawNode(node: Node) {
- this.ctx!.fillStyle = node.color;
- this.ctx!.strokeStyle = this.borderColor;
- this.ctx!.lineWidth = this.lineThickness;
- this.ctx!.beginPath();
- this.ctx!.arc(node.x * this.width, node.y * this.height, this.nodeRadius, 0, 2 * Math.PI);
- this.ctx!.fill();
- this.ctx!.stroke();
+ drawNode(node: Node, sizeMult: number) {
+ const lineColor: RgbColor = this.lerpColor(this.lineColor1, this.lineColor2, node.y);
+ this.ctx.strokeStyle = `rgb(${lineColor.R}, ${lineColor.G}, ${lineColor.B})`;
+ this.ctx.fillStyle = node.color;
+ this.ctx.lineWidth = this.lineThickness;
+ this.ctx.beginPath();
+ this.ctx.arc(node.x * this.width, node.y * this.height, this.nodeRadius * sizeMult, 0, 2 * Math.PI);
+ this.ctx.fill();
+ this.ctx.stroke();
}
width = 200;
@@ -134,6 +146,16 @@ export class GraphComponent implements AfterViewInit {
this.draw();
}
+
+ lerpColor(value1: RgbColor, value2: RgbColor, amount: number): RgbColor {
+ const newColor = new RgbColor(0, 0, 0);
+ amount = amount < 0 ? 0 : amount;
+ amount = amount > 1 ? 1 : amount;
+ newColor.R = value1.R + (value2.R - value1.R) * amount;
+ newColor.G = value1.G + (value2.G - value1.G) * amount;
+ newColor.B = value1.B + (value2.B - value1.B) * amount;
+ return newColor;
+ };
}
class Node {
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.css b/frontend/src/app/_elements/item-dataset/item-dataset.component.css
deleted file mode 100644
index dc851671..00000000
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.css
+++ /dev/null
@@ -1,23 +0,0 @@
-.card{
- margin-top:0;
- padding: 0;
-}
-.p-2{
- margin: 0;
- padding: 0;
-}
-hr{
- margin: 0;
- padding: 0;
-}
-b{
- margin-left: 5px;
- margin-right: 10px;
-}
-th{
- margin: 10px;
- padding: 10px;
-}
-p{
- text-align: justify;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.html b/frontend/src/app/_elements/item-dataset/item-dataset.component.html
deleted file mode 100644
index 11ff61c3..00000000
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<div class="card" style="min-width: 12rem;">
-<div class="card-header d-flex mb-2 justify-content-" style="padding: 0;margin: 0;">
-
- <div class=" p-2 float-left "><b style="color: gray;">Naziv</b></div>
- <div class=" p-2 float-left"><b>{{dataset.name}}</b></div>
-</div>
-<div class="card-body overflow-hidden">
- <b style="color: gray;">Opis</b>
- <hr style="width: 20%;"> <p> {{dataset.description}}</p>
- <hr>
- <div class="d-flex justify-content-center">
- <div class=" p-2" >
- <h4><span class="badge bg-secondary">{{dataset.extension}}</span></h4>
- </div>
- <div class="p-2">
- <span class="material-icons">{{visibleicon}}</span>
- </div>
- <div class="p-2">
- <span class="material-icons">{{accessibleicon}}</span>
- </div>
- </div>
- <hr>
- <div class="col text-center">
-<button (click)=toggleDisplayDiv() class="btn btn-primary btn-sm active " mat-raised-button color="primary" style="margin: 0.5rem;">Kolone</button>
- <div [hidden]="isShowDiv" style="overflow: scroll; overflow-y: hidden;">
- <table class="table table-bordered table-md" >
- <thead>
- <th scope="col" *ngFor="let column of dataset.columnInfo" >{{column.columnName}}</th>
- </thead>
- </table>
- </div>
-</div>
- <table>
- <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{dataset.dateCreated |date}}</td></tr>
- <tr><td><span class="material-icons">edit_calendar</span></td><td><span style="color: grey;"> <b> Poslednja izmena</b></span></td><td>{{dataset.lastUpdated |date}}</td></tr>
- </table>
-
-</div>
-<div class="card-footer">
-
- </div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.ts b/frontend/src/app/_elements/item-dataset/item-dataset.component.ts
deleted file mode 100644
index 44b95310..00000000
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import Dataset from 'src/app/_data/Dataset';
-
-@Component({
- selector: 'app-item-dataset',
- templateUrl: './item-dataset.component.html',
- styleUrls: ['./item-dataset.component.css']
-})
-export class ItemDatasetComponent {
-
- @Input() dataset: Dataset = new Dataset();
- visibleicon='';
- accessibleicon='';
- isShowDiv = true;
- toggleDisplayDiv() {
- this.isShowDiv = !this.isShowDiv;
- }
- constructor() {
- }
- ngOnInit(): void {
- if(this.dataset.isPublic==true)
- {
- this.visibleicon='visibility'
- }
- else
- {
- this.visibleicon='visibility_off';
- }
-
- if(this.dataset.accessibleByLink==true)
- {
- this.accessibleicon='link'
- }
- else
- {
- this.accessibleicon='link_off';
- }
- }
-}
-
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.html b/frontend/src/app/_elements/item-experiment/item-experiment.component.html
deleted file mode 100644
index 51fbfef3..00000000
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<div class="card" style="min-width: 12rem;">
- <div class="card-header">
- Naziv eksperimenta: <b>{{experiment.name}}</b>
- </div>
- <div class="card-body overflow-hidden">
- <p class="card-text">
- Opis: {{experiment.description}}
- </p>
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.ts b/frontend/src/app/_elements/item-experiment/item-experiment.component.ts
deleted file mode 100644
index 31900d35..00000000
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import Experiment from 'src/app/_data/Experiment';
-
-@Component({
- selector: 'app-item-experiment',
- templateUrl: './item-experiment.component.html',
- styleUrls: ['./item-experiment.component.css']
-})
-export class ItemExperimentComponent{
-
- @Input() experiment: Experiment = new Experiment();
-
- constructor() { }
-
-}
diff --git a/frontend/src/app/_elements/item-model/item-model.component.css b/frontend/src/app/_elements/item-model/item-model.component.css
deleted file mode 100644
index 5ea24c72..00000000
--- a/frontend/src/app/_elements/item-model/item-model.component.css
+++ /dev/null
@@ -1,23 +0,0 @@
-.card{
- margin: 0.5rem;
- padding: 0;
-}
-.p-2{
- margin: 0;
- padding: 0;
-}
-hr{
- margin: 0;
- padding: 0;
-}
-b{
- margin-left: 5px;
- margin-right: 10px;
-}
-th{
- margin: 10px;
- padding: 10px;
-}
-p{
- text-align: justify;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-model/item-model.component.html b/frontend/src/app/_elements/item-model/item-model.component.html
deleted file mode 100644
index 447f023e..00000000
--- a/frontend/src/app/_elements/item-model/item-model.component.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<div class="card" style="min-width: 12rem;">
- <div class="card-header d-flex mb-2 justify-content-" style="padding: 0;margin: 0;">
-
- <div class=" p-2 float-left "><b style="color: gray;">Naziv</b></div>
- <div class=" p-2 float-left"><b>{{model.name}}</b></div>
- </div>
- <div class="card-body overflow-hidden">
- <app-graph [model]="model"></app-graph>
- <br>
- <b style="color: gray;">Opis</b><hr style="width: 20%;">
- <p class="card-text">
- {{model.description}}
- </p>
- <hr>
-
- <div>
- <table>
- <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{model.dateCreated |date}}</td></tr>
- <tr><td><span class="material-icons">edit_calendar</span></td><td><span style="color: grey;"> <b> Poslednja izmena</b></span></td><td>{{model.lastUpdated |date}}</td></tr>
- </table>
- </div>
-
- </div>
- <button (click)=toggleDisplayDiv() class="btn btn-default btn-lg " mat-raised-button color="primary" style="margin: 0.5rem;">Parametri</button>
- <div [hidden]="isShowDiv">
- <!-- <table>
- <tr>
- <td><span style="color: grey;"> <b> Nasumično raspoređivanje podataka</b></span></td><td>{{randomOrd}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Podela podataka na trening i test skup</b></span></td><td>{{randomOrd}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Veličina skupa za treniranje</b></span></td><td>{{randomOrd}}</td>
- </tr>
- </table>-->
- <hr>
- <table>
- <tr>
- <td><span style="color: grey;"> <b> Tip problema</b></span></td><td>{{model.type}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Optimizator</b></span></td><td>{{model.optimizer}}</td>
- </tr>
- <tr>
- <td> <span style="color: grey;"> <b> Funkcija gubitka</b></span></td><td>{{model.lossFunction}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Batch size</b></span></td><td>{{model.batchSize}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Broj epoha</b></span></td><td>{{model.epochs}}</td>
- </tr>
-
- </table>
-
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-model/item-model.component.ts b/frontend/src/app/_elements/item-model/item-model.component.ts
deleted file mode 100644
index b837667b..00000000
--- a/frontend/src/app/_elements/item-model/item-model.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import Model from 'src/app/_data/Model';
-
-@Component({
- selector: 'app-item-model',
- templateUrl: './item-model.component.html',
- styleUrls: ['./item-model.component.css']
-})
-export class ItemModelComponent implements OnInit {
-
- @Input() model: Model = new Model();
- isShowDiv = true;
- randomOrd='';
-
- toggleDisplayDiv() {
- this.isShowDiv = !this.isShowDiv;
- }
-
- constructor() { }
-
- ngOnInit(): void {
- /*if(this.model.randomOrder)
- {
- this.randomOrd='Da';
- }
- else
- {
- this.randomOrd='Ne';
- }
-*/
- }
-
-}
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.html b/frontend/src/app/_elements/item-predictor/item-predictor.component.html
deleted file mode 100644
index 3199dcc8..00000000
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<div class="card" style="min-width: 12rem;">
- <div class="card-header d-flex mb-2 justify-content-" style="padding: 0;margin: 0;">
-
- <div class=" p-2 float-left "><b style="color: gray;">Prediktor</b></div>
-
- </div>
- <div class="card-body overflow-hidden">
- <b style="color: gray;">Opis</b><hr style="width: 20%;">
- <p class="card-text">
- {{predictor.description}}
- </p>
-
- <b style="color: gray;">Ulazne kolone</b>
- <div style="overflow: scroll; overflow-y: hidden;">
-
- <table class="table table-bordered table-md" >
- <thead>
- <th scope="col" *ngFor="let column of predictor.inputs" >{{column}}</th>
- </thead>
- </table>
- </div>
- <b style="color: gray;">Izlazna kolona: </b><b>{{predictor.output}}</b>
- <hr>
- <div>
- <table>
- <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{predictor.dateCreated |date}}</td></tr>
- </table>
- </div>
- </div>
- <div class="card-footer text-center">
- <button class="btn btn-md col-4" style="background-color:#003459; color:white;"
- (click)="openPredictor();">Iskoristi</button>
-
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.ts b/frontend/src/app/_elements/item-predictor/item-predictor.component.ts
deleted file mode 100644
index 246032e0..00000000
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import { Router } from '@angular/router';
-import Predictor from 'src/app/_data/Predictor';
-
-@Component({
- selector: 'app-item-predictor',
- templateUrl: './item-predictor.component.html',
- styleUrls: ['./item-predictor.component.css']
-})
-export class ItemPredictorComponent implements OnInit {
-
- @Input() predictor: Predictor = new Predictor();
-
- constructor(private router: Router) { }
-
- ngOnInit(): void {
- }
-
- openPredictor() {
- this.router.navigate(['predict/'+ this.predictor._id]);
- }
-
-}
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.css b/frontend/src/app/_elements/metric-view/metric-view.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.css
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.html b/frontend/src/app/_elements/metric-view/metric-view.component.html
new file mode 100644
index 00000000..3a6cce8d
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.html
@@ -0,0 +1,3 @@
+<div>
+
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.spec.ts b/frontend/src/app/_elements/metric-view/metric-view.component.spec.ts
new file mode 100644
index 00000000..c3ecc67f
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MetricViewComponent } from './metric-view.component';
+
+describe('MetricViewComponent', () => {
+ let component: MetricViewComponent;
+ let fixture: ComponentFixture<MetricViewComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ MetricViewComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MetricViewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.ts b/frontend/src/app/_elements/metric-view/metric-view.component.ts
new file mode 100644
index 00000000..3840692a
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.ts
@@ -0,0 +1,49 @@
+import { Component, Input, OnInit, ViewChild } from '@angular/core';
+import { LineChartComponent } from '../_charts/line-chart/line-chart.component';
+
+@Component({
+ selector: 'app-metric-view',
+ templateUrl: './metric-view.component.html',
+ styleUrls: ['./metric-view.component.css']
+})
+export class MetricViewComponent implements OnInit {
+ @ViewChild(LineChartComponent) linechartComponent!: LineChartComponent;
+
+ constructor() { }
+
+ ngOnInit(): void {
+ }
+
+ history: any[] = [];
+
+ update(history: any[]) {
+ const myAcc: number[] = [];
+ const myMae: number[] = [];
+ const myMse: number[] = [];
+ const myLoss: number[] = [];
+
+ const myEpochs: number[] = [];
+ this.history = history;
+ this.history.forEach((metrics, epoch) => {
+ myEpochs.push(epoch + 1);
+ for (let key in metrics) {
+ let value = metrics[key];
+ console.log(key, ':::', value, epoch);
+ if (key === 'accuracy') {
+ myAcc.push(parseFloat(value));
+ }
+ else if (key === 'loss') {
+ myLoss.push(parseFloat(value));
+ }
+ else if (key === 'mae') {
+ myMae.push(parseFloat(value));
+ }
+ else if (key === 'mse') {
+ myMse.push(parseFloat(value));
+ }
+ }
+ });
+
+ this.linechartComponent.update(myEpochs, myAcc, myLoss, myMae, myMse);
+ }
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/model-load/model-load.component.css b/frontend/src/app/_elements/model-load/model-load.component.css
deleted file mode 100644
index c716f964..00000000
--- a/frontend/src/app/_elements/model-load/model-load.component.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.btnType1 {
- background-color: #003459;
- color: white;
- padding-top: 2vh;
- padding-bottom: 2vh;
-}
-.btnType2 {
- background-color: white;
- color: #003459;
- border-color: #003459;
- padding-top: 2vh;
- padding-bottom: 2vh;
-}
-.selectedModelClass {
- /*border-color: 2px solid #003459;*/
- background-color: lightblue;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/navbar/navbar.component.css b/frontend/src/app/_elements/navbar/navbar.component.css
index e69de29b..fcfad876 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.css
+++ b/frontend/src/app/_elements/navbar/navbar.component.css
@@ -0,0 +1,8 @@
+.dropdown-item:hover {
+ background-color: var(--ns-primary);
+}
+
+h4 {
+ margin-top: 0.82rem;
+ margin-right: 10px;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/navbar/navbar.component.html b/frontend/src/app/_elements/navbar/navbar.component.html
index 7d0c4cd8..105151aa 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.html
+++ b/frontend/src/app/_elements/navbar/navbar.component.html
@@ -1,35 +1,23 @@
-<header class="sticky-top p-3 bg-dark text-white">
+<header class="text-offwhite" style="background-color: #002b49;">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a routerLink="" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
- <img src="../../../assets/svg/logo_no_text.svg" class="bi me-2" width="64" height="40">
+ <img src="../../../assets/images/logo.png" class="bi me-2" width="64" height="64">
+ <h4>Igr<span class="highlight">ann</span>onica</h4>
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
- <li><a routerLink="" class="nav-link px-2"
- [class]="(currentUrl === '') ? 'text-secondary' : 'text-white'">Početna</a></li>
- <li><a routerLink="experiment" class="nav-link px-2"
- [class]="(currentUrl === '/experiment') ? 'text-secondary' : 'text-white'">Napravi eksperiment</a>
+ <li><a routerLink="experiment" class="nav-link px-2" [class]="(currentUrl === '/experiment') ? 'text-primary' : 'text-offwhite'">Napravi eksperiment</a>
</li>
- <li><a routerLink="training" class="nav-link px-2"
- [class]="(currentUrl === '/training') ? 'text-secondary' : 'text-white'">Treniraj model</a>
- </li>
- <li><a routerLink="my-predictors" class="nav-link px-2"
- [class]="(currentUrl === '/my-predictors') ? 'text-secondary' : 'text-white' + (shared.loggedIn) ? '' : 'disabled'">Predvidi</a>
+ <li><a routerLink="archive" class="nav-link px-2" [class]="(currentUrl === '/archive') ? 'text-primary' : 'text-offwhite'">Arhiva</a>
</li>
</ul>
<div *ngIf="shared.loggedIn" class="dropdown text-end">
- <a href="#" class="d-block link-light text-decoration-none dropdown-toggle" id="dropdownUser1"
- data-bs-toggle="dropdown" aria-expanded="false">
- <img [src]="'/assets/profilePictures/'+ shared.photoId +'.png'" alt="mdo" width="32" height="32"
- class="rounded-circle">
+ <a href="#" class="d-block link-light text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
+ <img [src]="'/assets/profilePictures/'+ shared.photoId +'.png'" alt="mdo" width="32" height="32" class="rounded-circle">
</a>
- <ul class="dropdown-menu text-small" aria-labelledby="dropdownUser1"
- style="position: absolute; inset: 0px 0px auto auto; margin: 0px; transform: translate(0px, 34px);"
- data-popper-placement="bottom-end">
- <li><a class="dropdown-item" routerLink="add-model">Nov model...</a></li>
- <li><a class="dropdown-item" routerLink="settings">Podešavanja</a></li>
+ <ul class="dropdown-menu text-small ns-bg-dark-100" aria-labelledby="dropdownUser1" style="position: absolute; inset: 0px 0px auto auto; margin: 0px; transform: translate(0px, 34px);" data-popper-placement="bottom-end">
<li><a class="dropdown-item" routerLink="profile">Moj profil</a></li>
<li>
<hr class="dropdown-divider">
@@ -38,12 +26,10 @@
</ul>
</div>
<div *ngIf="!shared.loggedIn" class="dropdown text-end">
- <button type="button" mat-raised-button color="primary" class="mx-2" data-bs-toggle="modal"
- data-bs-target="#modalForLogin">
+ <button type="button" mat-raised-button color="accent" class="mx-2" data-bs-toggle="modal" data-bs-target="#modalForLogin">
Prijavi se
</button>
- <button type="button" mat-raised-button color="primary" data-bs-toggle="modal"
- data-bs-target="#modalForRegister">
+ <button type="button" mat-raised-button color="accent" data-bs-toggle="modal" data-bs-target="#modalForRegister">
Registruj se
</button>
</div>
diff --git a/frontend/src/app/_elements/navbar/navbar.component.ts b/frontend/src/app/_elements/navbar/navbar.component.ts
index 368508ed..e2551f7a 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.ts
+++ b/frontend/src/app/_elements/navbar/navbar.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Location } from '@angular/common';
import { AuthService } from '../../_services/auth.service';
import shared from 'src/app/Shared';
@@ -8,7 +8,8 @@ import { MatDialog } from '@angular/material/dialog';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
- styleUrls: ['./navbar.component.css']
+ styleUrls: ['./navbar.component.css'],
+ encapsulation: ViewEncapsulation.Emulated
})
export class NavbarComponent implements OnInit {
diff --git a/frontend/src/app/_elements/notifications/notifications.component.ts b/frontend/src/app/_elements/notifications/notifications.component.ts
index 9b460240..d64530b9 100644
--- a/frontend/src/app/_elements/notifications/notifications.component.ts
+++ b/frontend/src/app/_elements/notifications/notifications.component.ts
@@ -25,6 +25,7 @@ export class NotificationsComponent implements OnInit {
const existingNotification = this.notifications.find(x => x.id === mId)
const progress = ((currentEpoch + 1) / totalEpochs);
console.log("Ukupno epoha", totalEpochs, "Trenutna epoha:", currentEpoch);
+ console.log("stat:", stat);
if (!existingNotification)
this.notifications.push(new Notification(`Treniranje modela: ${mName}`, mId, progress, true));
else {
diff --git a/frontend/src/app/_elements/playlist/playlist.component.css b/frontend/src/app/_elements/playlist/playlist.component.css
new file mode 100644
index 00000000..353a094c
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.css
@@ -0,0 +1,61 @@
+.ns-wrapper {
+ width: 100%;
+ max-width: 800px;
+ max-height: 600px;
+ height: 100%;
+ transform-style: preserve-3d;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+}
+
+.ns-cards {
+ position: relative;
+ width: 300%;
+ height: 25rem;
+ margin-bottom: 20px;
+}
+
+.ns-card {
+ position: absolute;
+ width: 60%;
+ height: 100%;
+ left: 0;
+ right: 0;
+ margin: auto;
+ transition: transform 0.4s ease;
+ cursor: pointer;
+}
+
+.ns-card:hover {
+ opacity: 1 !important;
+}
+
+input[type=radio] {
+ display: none;
+}
+
+#item-1:checked~.ns-cards #view-item-3,
+#item-2:checked~.ns-cards #view-item-1,
+#item-3:checked~.ns-cards #view-item-2 {
+ transform: translatex(-40%) scale(0.8);
+ opacity: 0.5;
+ z-index: 0;
+}
+
+#item-1:checked~.ns-cards #view-item-2,
+#item-2:checked~.ns-cards #view-item-3,
+#item-3:checked~.ns-cards #view-item-1 {
+ transform: translatex(40%) scale(0.8);
+ opacity: 0.5;
+ z-index: 0;
+}
+
+#item-1:checked~.ns-cards #view-item-1,
+#item-2:checked~.ns-cards #view-item-2,
+#item-3:checked~.ns-cards #view-item-3 {
+ transform: translatex(0) scale(1);
+ opacity: 1;
+ z-index: 1;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/playlist/playlist.component.html b/frontend/src/app/_elements/playlist/playlist.component.html
new file mode 100644
index 00000000..b82de163
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.html
@@ -0,0 +1,19 @@
+<div class="ns-wrapper" *ngIf="tableDatas && tableDatas.length==3">
+ <input type="radio" name="slider" id="item-1" value="0" [(ngModel)]="selectedId">
+ <input type="radio" name="slider" id="item-2" value="1" [(ngModel)]="selectedId">
+ <input type="radio" name="slider" id="item-3" value="2" [(ngModel)]="selectedId">
+ <div class="ns-cards">
+ <label class="ns-card ns-bg-dark-100 ns-border-primary rounded" for="item-1" id="view-item-1">
+ <app-datatable [tableData]="tableDatas[0]"></app-datatable>
+ </label>
+ <label class="ns-card ns-bg-dark-100 ns-border-primary rounded" for="item-2" id="view-item-2">
+ <app-datatable [tableData]="tableDatas[1]"></app-datatable>
+ </label>
+ <label class="ns-card ns-bg-dark-100 ns-border-primary rounded" for="item-3" id="view-item-3">
+ <app-datatable [tableData]="tableDatas[2]"></app-datatable>
+ </label>
+ </div>
+ <div class="ns-infobox text-offwhite">
+ <h2>{{datasets[getIndex(selectedId)].name}}</h2>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/playlist/playlist.component.spec.ts b/frontend/src/app/_elements/playlist/playlist.component.spec.ts
new file mode 100644
index 00000000..0afe8041
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PlaylistComponent } from './playlist.component';
+
+describe('PlaylistComponent', () => {
+ let component: PlaylistComponent;
+ let fixture: ComponentFixture<PlaylistComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PlaylistComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PlaylistComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/playlist/playlist.component.ts b/frontend/src/app/_elements/playlist/playlist.component.ts
new file mode 100644
index 00000000..7529b36b
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.ts
@@ -0,0 +1,49 @@
+import { Component, Input, OnInit } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import { TableData } from 'src/app/_elements/datatable/datatable.component';
+import { CsvParseService } from 'src/app/_services/csv-parse.service';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+
+@Component({
+ selector: 'app-playlist',
+ templateUrl: './playlist.component.html',
+ styleUrls: ['./playlist.component.css']
+})
+export class PlaylistComponent implements OnInit {
+
+ selectedId: string = "0";
+
+ @Input() datasets!: Dataset[];
+
+ tableDatas?: TableData[];
+
+ constructor(private datasetService: DatasetsService, private csv: CsvParseService) {
+
+ }
+
+ getIndex(str: string) {
+ return parseInt(str);
+ }
+
+ ngOnInit(): void {
+ this.tableDatas = [];
+
+ this.datasets.forEach((dataset, index) => {
+ if (index < 3) {
+ this.datasetService.getDatasetFile(dataset.fileId).subscribe((file: string | undefined) => {
+ if (file) {
+ const tableData = new TableData();
+ tableData.hasInput = true;
+ tableData.loaded = true;
+ tableData.numRows = dataset.rowCount;
+ tableData.numCols = dataset.columnInfo.length;
+ tableData.data = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter.toString() == "") ? "," : dataset.delimiter);
+ this.tableDatas!.push(tableData);
+ }
+ });
+ }
+ });
+
+ console.log(this.tableDatas);
+ }
+}
diff --git a/frontend/src/app/_elements/reactive-background/reactive-background.component.html b/frontend/src/app/_elements/reactive-background/reactive-background.component.html
index 756952fb..c63dd6ac 100644
--- a/frontend/src/app/_elements/reactive-background/reactive-background.component.html
+++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.html
@@ -1 +1 @@
-<canvas id="bgCanvas" width="200" height="200" style="position: fixed; z-index: -1;"></canvas> \ No newline at end of file
+<canvas #bgCanvas width="200" height="200" style="position: fixed; z-index: -1;"></canvas> \ No newline at end of file
diff --git a/frontend/src/app/_elements/reactive-background/reactive-background.component.ts b/frontend/src/app/_elements/reactive-background/reactive-background.component.ts
index 980e3e6f..1a6157e3 100644
--- a/frontend/src/app/_elements/reactive-background/reactive-background.component.ts
+++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.ts
@@ -1,14 +1,19 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { CookieService } from 'ngx-cookie-service';
+import Shared from 'src/app/Shared';
@Component({
selector: 'app-reactive-background',
templateUrl: './reactive-background.component.html',
styleUrls: ['./reactive-background.component.css']
})
-export class ReactiveBackgroundComponent implements OnInit {
+export class ReactiveBackgroundComponent implements AfterViewInit {
+
+ @ViewChild('bgCanvas') canvasRef!: ElementRef;
@Input() numPoints: number = 450;
@Input() speed: number = 0.001; // 0-1
+ @Input() scrollSpeed: number = 1;
@Input() maxSize: number = 6;
@Input() minDistance: number = 0.07; //0-1
@@ -19,30 +24,43 @@ export class ReactiveBackgroundComponent implements OnInit {
@Input() pointColor: string = '#ffffff';
@Input() cursorLineColor: string = '#ff0000';
+ @Input() animate: boolean = true;
+ @Input() fill: number = 1.0;
+
+ private fleeSpeed = 0.005;
+
private points: Point[] = [];
private width = 200;
private height = 200;
private ratio = 1;
- private canvas?: HTMLCanvasElement;
- private ctx?: CanvasRenderingContext2D;
+ private canvas!: HTMLCanvasElement;
+ private ctx!: CanvasRenderingContext2D;
private time: number = 0;
- constructor() { }
+ constructor(private cookie: CookieService) { }
private mouseX = 0;
private mouseY = 0;
- ngOnInit(): void {
-
+ ngAfterViewInit(): void {
document.addEventListener('mousemove', (e) => {
this.mouseX = e.clientX / this.width;
this.mouseY = e.clientY / this.height;
})
- this.canvas = (<HTMLCanvasElement>document.getElementById('bgCanvas'));
+ document.addEventListener('mouseleave', _ => {
+ this.mouseX = -1;
+ this.mouseY = -1;
+ })
+
+ document.addEventListener('scroll', (e) => {
+ this.scrollBackground(e);
+ })
+
+ this.canvas = (<HTMLCanvasElement>this.canvasRef.nativeElement);
const ctx = this.canvas.getContext('2d');
if (ctx) {
this.ctx = ctx;
@@ -64,41 +82,77 @@ export class ReactiveBackgroundComponent implements OnInit {
this.resize();
setInterval(() => {
+ if (this.cookie.check('animateBackground')) {
+ this.animate = this.cookie.get('animateBackground') == 'true';
+ }
+ if (this.cookie.check('backgroundFill')) {
+ this.fill = parseFloat(this.cookie.get('backgroundFill'));
+ }
+
this.drawBackground();
}, 1000 / 60);
+
+ Shared.bgScroll.subscribe((amount) => {
+ this.scrollBackgroundFromSharedEvent(amount);
+ })
+ }
+
+ private lastScrollY: number = 0;
+
+ scrollBackgroundFromSharedEvent(amount: number) {
+ const scrolledAmount = amount - this.lastScrollY;
+ this.scrollPoints(scrolledAmount);
+ this.lastScrollY = amount;
+ }
+
+ scrollBackground(e: Event) {
+ const scrolledAmount = window.scrollY - this.lastScrollY;
+ this.scrollPoints(scrolledAmount);
+ this.lastScrollY = window.scrollY;
+ }
+
+ scrollPoints(amount: number) {
+ this.points.forEach((point, index) => {
+ if (index > this.numPoints * this.fill) return;
+ point.y = point.y - (amount / this.height) * this.scrollSpeed;
+ this.keepPointWithinBounds(point);
+ })
}
drawBackground() {
if (!this.ctx || !this.canvas) return;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.ctx.fillStyle = this.bgColor;
- this.ctx.fillRect(0, 0, this.width, this.height);
+ //this.ctx.fillStyle = this.bgColor;
+ //this.ctx.fillRect(0, 0, this.width, this.height);
this.points.forEach((point, index) => {
+ if (index > this.numPoints * this.fill) return;
+
this.drawLines(point, index);
this.drawPoint(point);
- this.updatePoint(point);
+
+ if (this.animate)
+ this.updatePoint(point);
});
//this.drawPoint(new Point(this.mouseX, this.mouseY, 12, 0));
-
- this.time += 1;
}
drawLines(p: Point, index: number) {
let i = index + 1;
- while (i < this.points.length) {
+ while (i < this.points.length * this.fill) {
const otherPoint = this.points[i];
const dist = this.distance(p.x, p.y, otherPoint.x, otherPoint.y);
if (dist < this.minDistance) {
const h = HEX[Math.round((1 - dist / this.minDistance) * 16)]
- this.ctx!.strokeStyle = this.lineColor + h + h;
- this.ctx!.beginPath();
- this.ctx!.moveTo(p.x * this.width, p.y * this.height);
- this.ctx!.lineTo(otherPoint.x * this.width, otherPoint.y * this.height);
- this.ctx!.stroke();
+ this.ctx.strokeStyle = this.lineColor + h;
+ this.ctx.lineWidth = this.maxSize / 2;
+ this.ctx.beginPath();
+ this.ctx.moveTo(p.x * this.width, p.y * this.height);
+ this.ctx.lineTo(otherPoint.x * this.width, otherPoint.y * this.height);
+ this.ctx.stroke();
}
i++;
@@ -106,10 +160,10 @@ export class ReactiveBackgroundComponent implements OnInit {
}
drawPoint(p: Point) {
- this.ctx!.fillStyle = this.pointColor;
- this.ctx!.beginPath();
- this.ctx!.arc(p.x * this.width, p.y * this.height, p.size, 0, 2 * Math.PI);
- this.ctx!.fill();
+ this.ctx.fillStyle = this.pointColor;
+ this.ctx.beginPath();
+ this.ctx.arc(p.x * this.width, p.y * this.height, p.size * this.screenDepth(p.x), 0, 2 * Math.PI);
+ this.ctx.fill();
}
resize() {
@@ -122,40 +176,66 @@ export class ReactiveBackgroundComponent implements OnInit {
this.canvas.height = this.height;
}
+ if (this.cookie.check('animateBackground')) {
+ this.animate = this.cookie.get('animateBackground') == 'true';
+ }
+ if (this.cookie.check('backgroundFill')) {
+ this.fill = parseFloat(this.cookie.get('backgroundFill'));
+ }
+
this.drawBackground();
}
updatePoint(p: Point) {
+ const mx = this.mouseX;
+ const my = this.mouseY;
+ const distToCursor = this.distance(p.x, p.y, mx, my);
+
+ if (distToCursor < this.cursorDistance) {
+
+ const t = (distToCursor / this.cursorDistance);
+ p.x -= ((mx - p.x) / distToCursor) * this.speed * (1 + t * 2);
+ p.y -= ((my - p.y) / distToCursor) * this.speed * (1 + t * 2);
+
+ p.direction = this.lerp(p.direction, Math.atan2(my - p.y, mx - p.x) * 180 / Math.PI, t);
+
+ const grd = this.ctx.createLinearGradient(p.x * this.width, p.y * this.height, mx * this.width, my * this.height);
+ const alpha = HEX[Math.round(p.size / this.maxSize * (HEX.length - 1))];
+ grd.addColorStop(0, this.cursorLineColor + alpha);
+ grd.addColorStop(0.5, this.cursorLineColor + '00');
+ this.ctx.strokeStyle = grd;
+ this.ctx.beginPath();
+ this.ctx.moveTo(p.x * this.width, p.y * this.height);
+ this.ctx.lineTo(mx * this.width, my * this.height);
+ this.ctx.stroke();
+ }
+
const vx = Math.sin(p.direction);
const vy = Math.cos(p.direction);
p.x = p.x + vx * this.speed;
p.y = p.y + vy * this.speed;
- const mx = this.mouseX;
- const my = this.mouseY;
- const distToCursor = this.distance(p.x, p.y, mx, my);
- if (distToCursor < this.cursorDistance) {
+ this.keepPointWithinBounds(p);
+ }
- p.x -= ((mx - p.x) / distToCursor) / 500;
- p.y -= ((my - p.y) / distToCursor) / 500;
-
- const grd = this.ctx!.createLinearGradient(p.x * this.width, p.y * this.height, mx * this.width, my * this.height);
- grd.addColorStop(0, this.cursorLineColor + 'ff');
- grd.addColorStop(1, this.cursorLineColor + '00');
- this.ctx!.strokeStyle = grd;
- this.ctx!.beginPath();
- this.ctx!.moveTo(p.x * this.width, p.y * this.height);
- this.ctx!.lineTo(mx * this.width, my * this.height);
- this.ctx!.stroke();
- }
+ lerp(start: number, end: number, amt: number) {
+ return (1 - amt) * start + amt * end
+ }
- p.x %= 1;
- p.y %= 1;
+ keepPointWithinBounds(p: Point) {
+ p.x = p.x % 1.0;
+ p.y = p.y % 1.0;
+ p.x = ((1 - Math.sign(p.x)) / 2) + p.x;
+ p.y = ((1 - Math.sign(p.y)) / 2) + p.y;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
- return Math.sqrt(((x2 - x1) ** 2) + ((y2 / this.ratio - y1 / this.ratio) ** 2));
+ return Math.sqrt(((x2 - x1) ** 2) + ((y2 / this.ratio - y1 / this.ratio) ** 2) / this.screenDepth(x1)) * this.ratio;
+ }
+
+ screenDepth(x: number): number {
+ return (1.5 - Math.sin(x * Math.PI));
}
}
@@ -168,4 +248,4 @@ class Point {
) { }
}
-const HEX = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; \ No newline at end of file
+const HEX = ['00', '11', '22', '33', '44', '55', '66', '77', '88', '99', 'aa', 'bb', 'cc', 'dd', 'ee', 'ff']; \ No newline at end of file