aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/app/_elements
diff options
context:
space:
mode:
authorDanijel Andjelkovic <adanijel99@gmail.com>2022-05-05 00:46:39 +0000
committerDanijel Andjelkovic <adanijel99@gmail.com>2022-05-05 00:46:39 +0000
commitc77c5289d01f1f02a57a060dc2166b449e597881 (patch)
treecb64f2775335cdd856e81ec9e8ba0bed93fa0985 /frontend/src/app/_elements
parent6f48458e058d3e5a8d559adc22adbe78cba9a253 (diff)
parent15c60cb0c179d2d3c353ab3e19370e16d02176eb (diff)
Merge branch 'redesign' into 'master'
merge See merge request igrannonica/neuronstellar!29
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/line-chart/line-chart.component.css)0
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.html3
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.spec.ts (renamed from frontend/src/app/_elements/line-chart/line-chart.component.spec.ts)0
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts (renamed from frontend/src/app/_elements/line-chart/line-chart.component.ts)25
-rw-r--r--frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.css (renamed from frontend/src/app/_elements/item-predictor/item-predictor.component.css)0
-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.ts (renamed from frontend/src/app/_elements/model-load/model-load.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts46
-rw-r--r--frontend/src/app/_elements/_charts/point-linechart/point-linechart.component.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.css4
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html4
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts56
-rw-r--r--frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html55
-rw-r--r--frontend/src/app/_elements/annvisual/annvisual.component.css4
-rw-r--r--frontend/src/app/_elements/annvisual/annvisual.component.html5
-rw-r--r--frontend/src/app/_elements/annvisual/annvisual.component.ts45
-rw-r--r--frontend/src/app/_elements/carousel/carousel.component.html17
-rw-r--r--frontend/src/app/_elements/carousel/carousel.component.ts18
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.css266
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.html249
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.spec.ts (renamed from frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.ts346
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.css18
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.html34
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.ts31
-rw-r--r--frontend/src/app/_elements/datatable/datatable.component.html44
-rw-r--r--frontend/src/app/_elements/datatable/datatable.component.ts1
-rw-r--r--frontend/src/app/_elements/folder/folder.component.css213
-rw-r--r--frontend/src/app/_elements/folder/folder.component.html111
-rw-r--r--frontend/src/app/_elements/folder/folder.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/folder/folder.component.ts379
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.css64
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.html65
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.spec.ts (renamed from frontend/src/app/_elements/dataset-load/dataset-load.component.spec.ts)12
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.ts (renamed from frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts)78
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.css104
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.html228
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.spec.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.css20
-rw-r--r--frontend/src/app/_elements/graph/graph.component.html9
-rw-r--r--frontend/src/app/_elements/graph/graph.component.ts85
-rw-r--r--frontend/src/app/_elements/item-dataset/item-dataset.component.css23
-rw-r--r--frontend/src/app/_elements/item-dataset/item-dataset.component.html41
-rw-r--r--frontend/src/app/_elements/item-dataset/item-dataset.component.ts40
-rw-r--r--frontend/src/app/_elements/item-experiment/item-experiment.component.html10
-rw-r--r--frontend/src/app/_elements/item-experiment/item-experiment.component.ts15
-rw-r--r--frontend/src/app/_elements/item-model/item-model.component.css23
-rw-r--r--frontend/src/app/_elements/item-model/item-model.component.html58
-rw-r--r--frontend/src/app/_elements/item-model/item-model.component.ts33
-rw-r--r--frontend/src/app/_elements/item-predictor/item-predictor.component.html35
-rw-r--r--frontend/src/app/_elements/item-predictor/item-predictor.component.ts23
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.html5
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.css10
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.html11
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.ts12
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.css17
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.html68
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.ts47
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.css8
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.html22
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.ts13
-rw-r--r--frontend/src/app/_elements/notifications/notifications.component.ts1
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.css61
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.html19
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/playlist/playlist.component.ts49
-rw-r--r--frontend/src/app/_elements/reactive-background/reactive-background.component.html2
-rw-r--r--frontend/src/app/_elements/reactive-background/reactive-background.component.ts166
97 files changed, 3484 insertions, 813 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..d6f4b6ec
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.ts
@@ -0,0 +1,102 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { Chart, LinearScale, CategoryScale } from 'chart.js';
+import { BoxPlotController, BoxAndWiskers } from '@sgratzl/chartjs-chart-boxplot';
+
+function randomValues(count: number, min: number, max: number) {
+ const delta = max - min;
+ return Array.from({ length: count }).map(() => Math.random() * delta + min);
+}
+
+Chart.register(BoxPlotController, BoxAndWiskers, LinearScale, CategoryScale);
+
+@Component({
+ selector: 'app-box-plot',
+ templateUrl: './box-plot.component.html',
+ styleUrls: ['./box-plot.component.css']
+})
+export class BoxPlotComponent implements AfterViewInit {
+
+ @Input()width?: number;
+ @Input()height?: number;
+
+ @ViewChild('boxplot') chartRef!: ElementRef;
+ constructor() { }
+
+ boxplotData = {
+ // define label tree
+ labels: ['January'/*, 'February', 'March', 'April', 'May', 'June', 'July'*/],
+ datasets: [{
+ label: 'Dataset 1',
+ backgroundColor: '#0063AB',
+ borderColor: '#dfd7d7',
+ borderWidth: 1,
+ outlierColor: '#999999',
+ scaleFontColor: '#0063AB',
+ padding: 10,
+ itemRadius: 0,
+ data: [
+ randomValues(100, 0, 100),
+ /*randomValues(100, 0, 20),
+ randomValues(100, 20, 70),
+ randomValues(100, 60, 100),
+ randomValues(40, 50, 100),
+ randomValues(100, 60, 120),
+ randomValues(100, 80, 100)*/
+ ]}/*, {
+ label: 'Dataset 2',
+ backgroundColor: 'rgba(0,0,255,0.5)',
+ borderColor: 'blue',
+ borderWidth: 1,
+ outlierColor: '#999999',
+ padding: 10,
+ itemRadius: 0,
+ data: [
+ randomValues(100, 60, 100),
+ randomValues(100, 0, 100),
+ randomValues(100, 0, 20),
+ randomValues(100, 20, 70),
+ randomValues(40, 60, 120),
+ randomValues(100, 20, 100),
+ randomValues(100, 80, 100)
+ ]
+ }*/]
+ };
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: "boxplot",
+ data: this.boxplotData,
+ options: {
+ /*title: {
+ display: true,
+ text: 'Predicted world population (millions) in 2050'
+ }*/
+ plugins:{
+ legend: {
+ display: false
+ },
+ },
+ scales : {
+ x: {
+ ticks: {
+ color: '#dfd7d7'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ },
+ y : {
+ min: -50,
+ max: 200,
+ ticks: {
+ color: '#dfd7d7'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ }
+ }
+ }
+ });
+}
+
+}
diff --git a/frontend/src/app/_elements/carousel/carousel.component.css b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/carousel/carousel.component.css
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.css
diff --git a/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html
new file mode 100644
index 00000000..9c464534
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.html
@@ -0,0 +1 @@
+<canvas #doughnut [width]="width" [height]="height"></canvas>
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.spec.ts
index b5c2d91c..67309670 100644
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemPredictorComponent } from './item-predictor.component';
+import { DoughnutChartComponent } from './doughnut-chart.component';
-describe('ItemPredictorComponent', () => {
- let component: ItemPredictorComponent;
- let fixture: ComponentFixture<ItemPredictorComponent>;
+describe('DoughnutChartComponent', () => {
+ let component: DoughnutChartComponent;
+ let fixture: ComponentFixture<DoughnutChartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemPredictorComponent ]
+ declarations: [ DoughnutChartComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemPredictorComponent);
+ fixture = TestBed.createComponent(DoughnutChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts
new file mode 100644
index 00000000..fc13289c
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/doughnut-chart/doughnut-chart.component.ts
@@ -0,0 +1,37 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'node_modules/chart.js';
+
+@Component({
+ selector: 'app-doughnut-chart',
+ templateUrl: './doughnut-chart.component.html',
+ styleUrls: ['./doughnut-chart.component.css']
+})
+export class DoughnutChartComponent implements AfterViewInit {
+
+ @Input()width: number = 800;
+ @Input()height: number = 450;
+
+ @ViewChild('doughnut') chartRef!: ElementRef;
+ constructor() { }
+
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'doughnut',
+ data: {
+ labels: ["Africa", "Asia", "Europe", "Latin America", "North America"],
+ datasets: [{
+ label: "Population (millions)",
+ backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
+ data: [2478,5267,734,784,433]
+ }]
+ },
+ /*options: {
+ title: {
+ display: true,
+ text: 'Predicted world population (millions) in 2050'
+ }
+ }*/
+ });
+ }
+
+}
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.css b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.css
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.css
diff --git a/frontend/src/app/_elements/_charts/heatmap/heatmap.component.html b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.html
new file mode 100644
index 00000000..52d95516
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/heatmap/heatmap.component.html
@@ -0,0 +1,3 @@
+<div style="width:800px; height: 400px; background-color: red;">
+ <ejs-heatmap [dataSource]='dataSource' [xAxis]='xAxis' [yAxis]='yAxis'></ejs-heatmap>
+</div>
diff --git a/frontend/src/app/_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/line-chart/line-chart.component.css b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/line-chart/line-chart.component.css
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
new file mode 100644
index 00000000..7f18256a
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
@@ -0,0 +1,3 @@
+
+ <canvas id="myChart" style="width: 100%; height: 530px;">
+ </canvas> \ No newline at end of file
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.spec.ts b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.spec.ts
index 0c5e7ef5..0c5e7ef5 100644
--- a/frontend/src/app/_elements/line-chart/line-chart.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.spec.ts
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.ts b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts
index 1a8579a0..655db9ec 100644
--- a/frontend/src/app/_elements/line-chart/line-chart.component.ts
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, Input } from '@angular/core';
+import { Component, AfterViewInit, ViewChild } from '@angular/core';
import { Chart } from 'chart.js';
@Component({
@@ -7,7 +7,7 @@ import { Chart } from 'chart.js';
styleUrls: ['./line-chart.component.css']
})
-export class LineChartComponent implements OnInit {
+export class LineChartComponent implements AfterViewInit {
dataAcc: number[] = [];
dataMAE: number[] = [];
@@ -47,7 +47,7 @@ export class LineChartComponent implements OnInit {
this.myChart.update();
}
- ngOnInit(): void {
+ ngAfterViewInit(): void {
this.myChart = new Chart("myChart",
{
type: 'line',
@@ -77,10 +77,27 @@ export class LineChartComponent implements OnInit {
},
options: {
scales: {
+ x: {
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ },
y: {
- beginAtZero: true
+ beginAtZero: true,
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
}
+
}
+
+
}
}
);
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.css b/frontend/src/app/_elements/_charts/mixed-chart/mixed-chart.component.css
index e69de29b..e69de29b 100644
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.css
+++ 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/model-load/model-load.component.spec.ts b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.spec.ts
index 1dafd966..64f36b7d 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.spec.ts
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ModelLoadComponent } from './model-load.component';
+import { PieChartComponent } from './pie-chart.component';
-describe('ModelLoadComponent', () => {
- let component: ModelLoadComponent;
- let fixture: ComponentFixture<ModelLoadComponent>;
+describe('PieChartComponent', () => {
+ let component: PieChartComponent;
+ let fixture: ComponentFixture<PieChartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ModelLoadComponent ]
+ declarations: [ PieChartComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ModelLoadComponent);
+ fixture = TestBed.createComponent(PieChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts
new file mode 100644
index 00000000..f141f522
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.ts
@@ -0,0 +1,46 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'chart.js';
+
+@Component({
+ selector: 'app-pie-chart',
+ templateUrl: './pie-chart.component.html',
+ styleUrls: ['./pie-chart.component.css']
+})
+export class PieChartComponent implements AfterViewInit {
+
+ @Input()width?: number;
+ @Input()height?: number;
+
+ @ViewChild('piechart') chartRef!: ElementRef;
+ constructor() { }
+
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'pie',
+ data: {
+ labels: ["Africa", "Asia", "Europe", "Latin America", "North America"],
+ datasets: [{
+ label: "Population (millions)",
+ backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
+ data: [2478,5267,734,784,433],
+ }]
+ },
+ options: {
+ /*title: {
+ display: true,
+ text: 'Predicted world population (millions) in 2050'
+ }*/
+ plugins:{
+ legend: {
+ display: false
+ },
+ },
+ layout: {
+ padding: 15}
+ }
+});
+
+ }
+
+
+}
diff --git a/frontend/src/app/_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..005cb692
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.css
@@ -0,0 +1,4 @@
+#divScatterChart{
+
+ display: block;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
new file mode 100644
index 00000000..ef41775a
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.html
@@ -0,0 +1,4 @@
+
+<div id="divScatterChart" style="width: 100%;height: 100%;">
+ <canvas id="ScatterCharts" style="width: 100%;height: 280px;"> </canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_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..12795c70
--- /dev/null
+++ b/frontend/src/app/_elements/_charts/scatterchart/scatterchart.component.ts
@@ -0,0 +1,56 @@
+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}],
+ borderColor: 'white',
+ }]
+ },
+ options: {
+ scales: {
+ x:{
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ },
+ y: {
+ beginAtZero: true,
+ ticks: {
+ color: 'white'
+ },
+ grid: {
+ color: "rgba(0, 99, 171, 0.5)"
+ }
+ }
+
+ }
+
+
+ }
+ });
+ }
+}
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html
deleted file mode 100644
index e5d4cd23..00000000
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<div class="row mb-4">
- <div class="col-2">
- </div>
- <div class="col-3">
- <label for="name" class="col-form-label">Naziv dataseta:</label>
- <input type="text" class="form-control mb-1" name="name" placeholder="Naziv..." [(ngModel)]="dataset.name">
-
- <label for="desc" class="col-sm-2 col-form-label">Opis:</label>
- <div>
- <textarea class="form-control" name="desc" rows="3" [(ngModel)]="dataset.description"></textarea>
- </div>
-
- <label for="checkboxIsPublic" class="form-check-label mt-3 mb-1">Želite li da dataset bude javan?
- <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="dataset.isPublic" (change)="checkAccessible()" type="checkbox"
- value="" id="checkboxIsPublic">
- </label>
-
- <label for="checkboxAccessibleByLink" class="form-check-label">Želite li da bude deljiv linkom? &nbsp;
- <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="dataset.accessibleByLink" type="checkbox"
- value="" id="checkboxAccessibleByLink">
- </label>
- </div>
- <div class="col-1">
- </div>
- <div class="col-3 mt-4">
-
- <label for="type" class="col-form-label">Izaberite delimiter za .csv fajl</label>
- <select id="delimiterOptions" class="form-select" name="type" [(ngModel)]="dataset.delimiter" (change)="update()">
- <option
- *ngFor="let option of delimiterOptions">
- {{ option }}
- </option>
- </select>
- <!--
- <input list="delimiterOptions" placeholder="Izaberite ili ukucajte delimiter za .csv fajl" class="form-control mt-2" [(ngModel)]="dataset.delimiter" (input)="update()">
- <datalist id="delimiterOptions">
- <option *ngFor="let option of delimiterOptions">{{option}}</option>
- </datalist>
--->
- <label for="type" class="form-check-label my-5">Da li .csv ima header?
- <input class="mx-3 form-check-input" type="checkbox" (input)="update()" [(ngModel)]="dataset.hasHeader" type="checkbox"
- value="" id="checkboxHeader" checked>
- </label>
- <br>
- <input id="fileInput" class="form-control btn-lg" type="file" class="upload" (change)="changeListener($event)" accept=".csv">
- </div>
-</div>
-
-<div class="px-5 mt-5">
- <app-datatable [tableData]="tableData"></app-datatable>
-</div>
-
-<div class="d-flex flex-row align-items-center justify-content-center w-100 my-2">
- <button (click)="uploadDataset()" class="btn btn-lg col-4" style="background-color:#003459; color:white;">Dodaj izvor podataka</button>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.css b/frontend/src/app/_elements/annvisual/annvisual.component.css
deleted file mode 100644
index 857a3390..00000000
--- a/frontend/src/app/_elements/annvisual/annvisual.component.css
+++ /dev/null
@@ -1,4 +0,0 @@
-#graph{
- width: 100%;
- text-align: center;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.html b/frontend/src/app/_elements/annvisual/annvisual.component.html
deleted file mode 100644
index 09251398..00000000
--- a/frontend/src/app/_elements/annvisual/annvisual.component.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<div style="text-align: center; " >
- <button (click)="d3()" mat-raised-button color="primary">Prikaz veštačke neuronske mreže</button>
- <div id="graph" align-items-center style="width: 12rem;"></div>
- </div>
-
diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.ts b/frontend/src/app/_elements/annvisual/annvisual.component.ts
deleted file mode 100644
index df0a3898..00000000
--- a/frontend/src/app/_elements/annvisual/annvisual.component.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Component, OnInit, Input } from '@angular/core';
-import Model from 'src/app/_data/Model';
-import { graphviz } from 'd3-graphviz';
-
-@Component({
- selector: 'app-annvisual',
- templateUrl: './annvisual.component.html',
- styleUrls: ['./annvisual.component.css']
-})
-export class AnnvisualComponent implements OnInit {
- ngOnInit(): void {
- }
-
- @Input() model: Model = new Model();
-
- d3() {
- let inputlayerstring: string = '';
- let hiddenlayerstring: string = '';
- let digraphstring: string = 'digraph {';
-
- for (let i = 0; i < /*this.model.inputColumns.length*/ 10; i++) {
- inputlayerstring = inputlayerstring + 'i' + i + ',';
- }
- inputlayerstring = inputlayerstring.slice(0, -1);
-
- digraphstring = digraphstring + inputlayerstring + '->';
-
- for (let j = 0; j < this.model.hiddenLayers; j++) {
- for (let i = 0; i < this.model.hiddenLayerNeurons; i++) {
- hiddenlayerstring = hiddenlayerstring + 'h' + j + '_' + i + ',';
- }
- hiddenlayerstring = hiddenlayerstring.slice(0, -1);
- digraphstring = digraphstring + hiddenlayerstring + '->';
- hiddenlayerstring = '';
- }
- digraphstring = digraphstring + 'o}';
-
- graphviz('#graph').renderDot(digraphstring);
- }
-
- //'digraph {i0,i1,i2->h1,h2,h3->h21,h22,h23->o}'
-}
-
-
-
diff --git a/frontend/src/app/_elements/carousel/carousel.component.html b/frontend/src/app/_elements/carousel/carousel.component.html
deleted file mode 100644
index eb1041ce..00000000
--- a/frontend/src/app/_elements/carousel/carousel.component.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<div class="container">
- <div class="row d-flex align-items-stretch flex-row mx-5 align-items-stretch">
- <div class="col my-1" *ngFor="let item of items" [ngSwitch]="type">
- <ng-template ngSwitchCase="Object">
- Unknown item type
- </ng-template>
- <ng-template ngSwitchCase="Dataset">
- <app-item-dataset [dataset]="item">
- </app-item-dataset>
- </ng-template>
- <ng-template ngSwitchCase="Predictor">
- <app-item-predictor [predictor]="item">
- </app-item-predictor>
- </ng-template>
- </div>
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/carousel/carousel.component.ts b/frontend/src/app/_elements/carousel/carousel.component.ts
deleted file mode 100644
index e0112121..00000000
--- a/frontend/src/app/_elements/carousel/carousel.component.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-
-@Component({
- selector: 'app-carousel',
- templateUrl: './carousel.component.html',
- styleUrls: ['./carousel.component.css']
-})
-export class CarouselComponent {
-
- @Input() items: any[] = [];
- @Input() type: string = "Object";
-
- constructor() { }
-
- ngOnInit(): void {
- }
-
-}
diff --git a/frontend/src/app/_elements/column-table/column-table.component.css b/frontend/src/app/_elements/column-table/column-table.component.css
new file mode 100644
index 00000000..0477b7be
--- /dev/null
+++ b/frontend/src/app/_elements/column-table/column-table.component.css
@@ -0,0 +1,266 @@
+table.fixed {
+ table-layout: fixed;
+ display: block;
+ overflow-x: auto;
+ white-space: nowrap;
+ font-size: 12px;
+ border-radius: 4px;
+}
+
+#divTable {
+ height: 100%;
+ overflow-y: auto;
+}
+
+table.fixed td {
+ overflow: hidden;
+ max-width: 200px;
+ min-width: 200px;
+ vertical-align: middle;
+ margin: 4px;
+}
+
+table.fixed th {
+ overflow: hidden;
+ max-width: 120px;
+ min-width: 120px;
+ vertical-align: middle;
+ background-color: var(--ns-primary-50);
+ font-size: 14px;
+}
+
+table.fixed th:first-child {
+ text-align: center;
+}
+
+.columnNames {
+ background-color: var(--ns-primary-50) !important;
+}
+
+.brighter {
+ background-color: var(--ns-primary) !important;
+ border-color: var(--offwhite);
+}
+
+.border-bottom {
+ border-bottom-color: var(--offwhite) !important;
+}
+
+mat-slider {
+ width: 300px;
+}
+
+.slider {
+ background-color: var(--ns-bg-dark-100);
+}
+
+#missingValuesHeader {
+ font-size: 13px;
+ line-height: 140% !important;
+}
+
+.verticalAlign {
+ vertical-align: center;
+}
+
+.cell-align {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ vertical-align: middle;
+ height: 100%;
+}
+
+.text-left {
+ text-align: left !important;
+}
+
+table ::ng-deep .mat-form-field-wrapper {
+ margin-top: -2rem;
+}
+
+.graphics-row {
+ height: 100px;
+ padding: 1px;
+ margin: 0;
+}
+
+.no-pad {
+ padding: 2px;
+ margin: 0;
+}
+
+.text-overflow {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.row-height {
+ height: 30px;
+ border: none;
+ outline: none;
+}
+
+.graphic-class {
+ opacity: 0.5;
+}
+
+
+/* TABS STYLE */
+
+#folder-table {
+ border: 1px solid var(--ns-primary);
+ border-radius: 4px;
+ height: 70%;
+}
+
+#tabs {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ height: 3.2rem;
+}
+
+#tabs>.folder-tab:not(:first-child) {
+ margin-left: -5px;
+}
+
+.folder-tab-end {
+ margin-left: auto;
+ color: var(--offwhite) !important;
+ overflow: hidden;
+}
+
+.folder-tab,
+.folder-tab-end {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ position: relative;
+ overflow-x: hidden;
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-color: var(--ns-primary);
+ color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 0 1px;
+}
+
+.folder-tab:not(:first-child) {
+ margin-block-start: auto;
+}
+
+.folder-tab {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.selected-tab {
+ height: 3rem;
+ background-color: var(--ns-primary);
+ color: var(--offwhite);
+}
+
+.hover-tab {
+ height: 3.2rem;
+}
+
+.selected-tab,
+.hover-tab {
+ width: fit-content !important;
+}
+
+.tab-link {
+ color: var(--offwhite) !important;
+ text-decoration: none !important;
+ cursor: pointer;
+}
+
+.tab-link:active {
+ text-decoration: underline !important;
+}
+
+.selected-tab {
+ background-color: var(--ns-primary);
+}
+
+.hidden {
+ /*visibility: hidden;
+ height: 1px;*/
+ display: none;
+}
+
+.bottom-button {
+ font-size: large;
+ position: relative;
+ background-color: var(--ns-primary);
+ width: 10rem;
+ height: 2.3rem;
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 0px 1px 1px 1px;
+}
+
+#footer {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+}
+
+.pad-fix {
+ padding-top: 0.6rem;
+ padding-bottom: 0;
+}
+
+.long {
+ height: 3rem;
+}
+
+.col-disabled {
+ background-color: rgb(0, 45, 69);
+}
+
+.text-disabled {
+ color: gray;
+}
+
+.header-disabled {
+ color: gray;
+}
+
+.menu-disabled {
+ pointer-events: none;
+ opacity: .5;
+}
+
+col:not(.col-disabled) {
+ background-color: var(--ns-bg-dark-100);
+}
+
+.col-first {
+ background-color: rgb(1, 56, 86) !important;
+}
+
+
+/* mat-icon rotate */
+
+.rotate {
+ animation: rotation 3s infinite linear;
+}
+
+.rotate:hover {
+ cursor: pointer;
+}
+
+@keyframes rotation {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(359deg);
+ }
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/column-table/column-table.component.html b/frontend/src/app/_elements/column-table/column-table.component.html
new file mode 100644
index 00000000..efc093d2
--- /dev/null
+++ b/frontend/src/app/_elements/column-table/column-table.component.html
@@ -0,0 +1,249 @@
+<div *ngIf="loaded">
+ <div id="tabs">
+ <div class="folder-tab p-1 rounded-top" *ngFor="let tab of tabs; let i = index" [style]="'z-index:' + calcZIndex(i) + ' ;'" [ngClass]="{'selected-tab' : selectedTab.index == i, 'hover-tab' : hoveringOverTab?.index == i}">
+ <a class="m-1 stretched-link tab-link" (click)="selectTab(i)" (mouseenter)="hoverOverTab(i)" (mouseleave)="hoverOverTab(-1)">
+ {{tab.name}}
+ </a>
+ </div>
+ <!--
+ <button mat-button class="p-1 folder-tab-end rounded-top">
+ Kolone
+ <mat-icon>keyboard_double_arrow_down</mat-icon>
+ </button>
+ -->
+ </div>
+ <div id="folder-table" *ngIf="dataset && experiment">
+ <!--<div [ngSwitch]="tabToDisplay">-->
+ <div id="divTable">
+
+ <div [ngClass]="{'hidden': tabToDisplay != Table.Data}">
+ <table class="table text-offwhite fixed bg-blur">
+ <colgroup>
+ <col class="col-first">
+ <col *ngFor="let column of dataset.columnInfo; let i = index" [ngClass]="{'col-disabled' : !experiment.inputColumns.includes(column.columnName)}">
+ </colgroup>
+ <thead>
+ <tr>
+ <th>#</th>
+ <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'header-disabled' : !columnsChecked[i]}">
+ <div class="cell-align">
+ #{{i + 1}}&nbsp;&nbsp;{{colInfo.columnName}}
+ <mat-checkbox color="primary" [(ngModel)]="columnsChecked[i]" (change)="changeInputColumns($event, colInfo.columnName)"></mat-checkbox>
+ </div>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let row of tableData; let i = index">
+ <th>#{{i}}</th>
+ <td *ngFor="let col of row; let j = index" [ngClass]="{'text-disabled' : !columnsChecked[j]}">
+ <div class="text-overflow">
+ {{col}}
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div [ngClass]="{'hidden': tabToDisplay != Table.CorrelationMatrix}">
+ <table class="table text-offwhite fixed bg-blur">
+ <colgroup>
+ <col class="col-first">
+ <col *ngFor="let column of dataset.columnInfo; let i = index" [ngClass]="{'col-disabled' : !experiment.inputColumns.includes(column.columnName)}">
+ </colgroup>
+ <thead>
+ <tr>
+ <th>Kolona</th>
+ <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'header-disabled' : !columnsChecked[i]}">
+ <div class="cell-align">
+ #{{i + 1}}&nbsp;&nbsp;{{colInfo.columnName}}
+ <mat-checkbox color="primary" [(ngModel)]="columnsChecked[i]" (change)="changeInputColumns($event, colInfo.columnName)"></mat-checkbox>
+ </div>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr *ngFor="let row of dataset.cMatrix; let i = index">
+ <th [ngClass]="{'header-disabled col-disabled' : !columnsChecked[i]}">
+ <div class="text-left">
+ {{dataset.columnInfo[i].columnName}}
+ </div>
+ </th>
+ <td *ngFor="let corrVal of row; let j = index" [ngClass]="{'text-disabled col-disabled' : !columnsChecked[j] || !columnsChecked[i]}">
+ <div class="text-overflow">
+ {{corrVal}}
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div [ngClass]="{'hidden': tabToDisplay != Table.Columns}">
+ <table class="table text-offwhite fixed bg-blur">
+ <colgroup>
+ <col class="col-first">
+ <col *ngFor="let column of dataset.columnInfo; let i = index" [ngClass]="{'col-disabled' : !experiment.inputColumns.includes(column.columnName)}">
+ </colgroup>
+ <thead>
+ <tr>
+ <th>Naziv</th>
+ <th class="columnNames" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'header-disabled' : !columnsChecked[i]}">
+ <div class="cell-align">
+ #{{i + 1}}&nbsp;&nbsp;{{colInfo.columnName}}
+ <mat-checkbox color="primary" [(ngModel)]="columnsChecked[i]" (change)="changeInputColumns($event, colInfo.columnName)"></mat-checkbox>
+ </div>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th>Tip</th>
+ <td *ngFor="let colInfo of dataset.columnInfo; let i = index" class="pad-fix" [ngClass]="{'text-disabled' : !columnsChecked[i]}">
+ <p class="verticalAlign text-left" style="font-size:13px;" *ngIf="!colInfo.isNumber">Kategorijski</p>
+ <mat-form-field *ngIf="colInfo.isNumber">
+ <mat-select matNativeControl [(value)]="this.experiment.columnTypes[i]" [disabled]="!columnsChecked[i]" (selectionChange)="columnTypeChanged(colInfo.columnName);">
+ <mat-option [value]="ColumnType.categorical">Kategorijski</mat-option>
+ <mat-option [value]="ColumnType.numerical">Numerički</mat-option>
+ </mat-select>
+ </mat-form-field>
+ </td>
+ </tr>
+ <tr class="graphics-row">
+ <th class="no-pad">Grafik</th>
+ <td class="no-pad" *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'graphic-class' : !columnsChecked[i]}">
+ <app-box-plot *ngIf="this.experiment.columnTypes[i] == ColumnType.numerical" [width]="150" [height]="150"></app-box-plot>
+ <app-pie-chart *ngIf="this.experiment.columnTypes[i] == ColumnType.categorical" [width]="150" [height]="150"></app-pie-chart>
+ </td>
+ </tr>
+ <tr>
+ <th class="border-bottom">Statistika</th>
+ <td *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'text-disabled' : !columnsChecked[i]}" class="text-left">
+ <span *ngIf="this.experiment.columnTypes[i] == ColumnType.numerical">
+ Mean: {{colInfo.mean}}<br>
+ Median: {{colInfo.median}}<br>
+ Min: {{colInfo.min}}<br>
+ Max: {{colInfo.max}}<br>
+ Q1: {{colInfo.q1}}<br>
+ Q3: {{colInfo.q3}}<br>
+ </span>
+ <div class="text-overflow" *ngIf="this.experiment.columnTypes[i] == ColumnType.categorical && colInfo.uniqueValuesPercent">
+ <span *ngFor="let uniqueValue of colInfo.uniqueValues | slice:0:6; let i = index">
+ ({{(colInfo.uniqueValuesPercent[i] * 100).toFixed(2)}}%) {{uniqueValue}}<br>
+ </span>
+ </div>
+ </td>
+ </tr>
+ <tr style="padding: 0">
+ <th class="brighter cell-align long" (click)="openEncodingDialog()">
+ <span class="verticalAlign">Enkodiranje</span>&nbsp;
+ <span class="material-icons-round verticalAlign rotate">settings</span>
+ </th>
+ <td *ngFor="let colInfo of dataset.columnInfo; let i = index" class="pad-fix" [ngClass]="{'text-disabled' : !columnsChecked[i]}">
+ <mat-form-field>
+ <mat-select matNativeControl [(value)]="experiment.encodings[i].encoding" [disabled]="!columnsChecked[i]" (selectionChange)="columnTableChangeDetected()">
+ <mat-option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </td>
+ </tr>
+ <tr>
+ <th class="brighter cell-align" (click)="openMissingValuesDialog()">
+ <div id="missingValuesHeader">Nedostajuće<br>vrednosti<br></div>
+ <span class="material-icons-round rotate">settings</span>
+ </th>
+ <td *ngFor="let colInfo of dataset.columnInfo; let i = index" [ngClass]="{'text-disabled' : !columnsChecked[i]}">
+
+ <div *ngIf="colInfo.numNulls > 0">
+ <button class="w-100" mat-raised-button [ngClass]="{ 'menu-disabled' : !columnsChecked[i]}" [matMenuTriggerFor]="menu" id="main_{{colInfo.columnName}}" #nullValMenu>
+ <div class="cell-align">
+ {{nullValOption[i]}}
+ <mat-icon>arrow_drop_down</mat-icon>
+ </div>
+ </button>
+ <mat-menu #menu="matMenu">
+ <!--<button mat-menu-item (click)="MissValsDeleteClicked($event, NullValueOptions.DeleteColumns, i)" value={{colInfo.columnName}}>Obriši kolonu</button>-->
+ <button mat-menu-item (click)="MissValsDeleteClicked($event, NullValueOptions.DeleteRows, i)" value={{colInfo.columnName}}>Obriši redove ({{colInfo.numNulls}})</button>
+ <button mat-menu-item [matMenuTriggerFor]="fillWith">Popuni sa</button>
+ </mat-menu>
+
+ <mat-menu #fillWith="matMenu">
+ <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.mean}}>Mean ({{colInfo.mean}})</button>
+ <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.median}}>Median ({{colInfo.median}})</button>
+ <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.max}}>Max ({{colInfo.max}})</button>
+ <button *ngIf="colInfo.isNumber" mat-menu-item (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{colInfo.min}}>Min ({{colInfo.min}})</button>
+
+ <button *ngIf="!colInfo.isNumber" mat-menu-item [matMenuTriggerFor]="uniques">Najčešće vrednosti</button>
+
+ <button mat-menu-item [matMenuTriggerFor]="replaceWith">Unesi vrednost...</button>
+ </mat-menu>
+
+ <mat-menu #uniques="matMenu">
+ <button mat-menu-item *ngFor="let uniqueValue of colInfo.uniqueValues" (click)="MissValsReplaceClicked($event, colInfo.columnName, i)" value={{uniqueValue}}>{{uniqueValue}}</button>
+ </mat-menu>
+
+ <mat-menu #replaceWith="matMenu">
+ <input type="text" id={{colInfo.columnName}} mat-menu-item placeholder="Unesi vrednost..." [value] #enterAValue>
+ <button [disabled]="getValue(colInfo.columnName) == ''" mat-menu-item value={{getValue(colInfo.columnName)}} (click)="MissValsReplaceClicked($event, colInfo.columnName, i)">Potvrdi unos</button>
+ </mat-menu>
+ </div>
+ <div *ngIf="colInfo.numNulls == 0" class="text-left">
+ Nema nedostajućih vrednosti.
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <div *ngIf="dataset && experiment" class="container-fluid text-offwhite belowColumn mt-3">
+ <div class="ns-row">
+ <div class="break-2"></div>
+
+ <div class="ns-col rounded">
+ <mat-form-field appearance="fill" class="align-items-center justify-content-center pt-3 w-100">
+ <mat-label>Izlazna kolona</mat-label>
+ <mat-select [(value)]="experiment.outputColumn" (selectionChange)="changeOutputColumn(this.experiment.inputColumns[0])">
+ <mat-option *ngFor="let inputColumn of experiment.inputColumns" [value]="inputColumn">{{inputColumn}}</mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="ns-col rounded">
+ <mat-form-field appearance="fill" class="align-items-center justify-content-center pt-3 w-100">
+ <mat-label>Tip problema</mat-label>
+ <mat-select [(value)]="experiment.type">
+ <mat-option [value]="ProblemType.Regression">Regresioni</mat-option>
+ <mat-option [value]="ProblemType.BinaryClassification">Binarni-klasifikacioni</mat-option>
+ <mat-option [value]="ProblemType.MultiClassification">Multi-klasifikacioni</mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="break-1"></div>
+ <div class="ns-col d-flex align-items-center justify-content-center">
+ <button *ngIf="experiment._id == ''" mat-button (click)="saveExperiment()" class="bottom-button text-offwhite rounded-bottom">
+ <div class="f-row" style="justify-content: space-around; width: 100%;">
+ <div>Sačuvaj</div>
+ <div class="icon-double pt-1">
+ <mat-icon>check</mat-icon>
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ <button *ngIf="experiment._id != ''" mat-button (click)="updateExperiment()" class="bottom-button text-offwhite rounded-bottom">
+ <div class="f-row" style="justify-content: space-around; width: 100%;">
+ <div>Sačuvaj izmene</div>
+ <div class="icon-double pt-1">
+ <mat-icon>check</mat-icon>
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts b/frontend/src/app/_elements/column-table/column-table.component.spec.ts
index 603889b2..360a8109 100644
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.spec.ts
+++ b/frontend/src/app/_elements/column-table/column-table.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ItemDatasetComponent } from './item-dataset.component';
+import { ColumnTableComponent } from './column-table.component';
-describe('ItemDatasetComponent', () => {
- let component: ItemDatasetComponent;
- let fixture: ComponentFixture<ItemDatasetComponent>;
+describe('ColumnTableComponent', () => {
+ let component: ColumnTableComponent;
+ let fixture: ComponentFixture<ColumnTableComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ ItemDatasetComponent ]
+ declarations: [ ColumnTableComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(ItemDatasetComponent);
+ fixture = TestBed.createComponent(ColumnTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/column-table/column-table.component.ts b/frontend/src/app/_elements/column-table/column-table.component.ts
new file mode 100644
index 00000000..c200e674
--- /dev/null
+++ b/frontend/src/app/_elements/column-table/column-table.component.ts
@@ -0,0 +1,346 @@
+import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChildren } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import Experiment, { ColumnEncoding, Encoding, ColumnType, NullValueOptions } from 'src/app/_data/Experiment';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+import { EncodingDialogComponent } from 'src/app/_modals/encoding-dialog/encoding-dialog.component';
+import { MatDialog } from '@angular/material/dialog';
+import { MissingvaluesDialogComponent } from 'src/app/_modals/missingvalues-dialog/missingvalues-dialog.component';
+import { MatCheckboxChange } from '@angular/material/checkbox';
+import { CsvParseService } from 'src/app/_services/csv-parse.service';
+import { ProblemType } from 'src/app/_data/Model';
+import { ExperimentsService } from 'src/app/_services/experiments.service';
+import { SaveExperimentDialogComponent } from 'src/app/_modals/save-experiment-dialog/save-experiment-dialog.component';
+import { AlertDialogComponent } from 'src/app/_modals/alert-dialog/alert-dialog.component';
+import Shared from 'src/app/Shared';
+
+@Component({
+ selector: 'app-column-table',
+ templateUrl: './column-table.component.html',
+ styleUrls: ['./column-table.component.css']
+})
+export class ColumnTableComponent implements AfterViewInit {
+
+ @Input() dataset?: Dataset;
+ @Input() experiment!: Experiment;
+ @Output() okPressed: EventEmitter<string> = new EventEmitter();
+ @Output() columnTableChanged = new EventEmitter();
+
+ Object = Object;
+ Encoding = Encoding;
+ NullValueOptions = NullValueOptions;
+ ColumnType = ColumnType;
+ ProblemType = ProblemType;
+ tableData?: any[][];
+ nullValOption: string[] = [];
+
+ columnsChecked: boolean[] = []; //niz svih kolona
+ loaded: boolean = false;
+
+
+ constructor(private datasetService: DatasetsService, private experimentService: ExperimentsService, public csvParseService: CsvParseService, public dialog: MatDialog) {
+ //ovo mi nece trebati jer primam dataset iz druge komponente
+ }
+
+ loadDataset(dataset: Dataset) {
+ this.dataset = dataset;
+
+ this.setColumnTypeInitial();
+
+ this.dataset.columnInfo.forEach(column => {
+ this.columnsChecked.push(true);
+ });
+
+ this.resetInputColumns();
+ this.resetOutputColumn();
+ this.resetColumnEncodings(Encoding.Label);
+ this.setDeleteRowsForMissingValTreatment();
+
+ this.nullValOption = [];
+ this.dataset.columnInfo.forEach(colInfo => {
+ this.nullValOption.push(`Obriši redove (${colInfo.numNulls})`);
+ });
+
+ this.datasetService.getDatasetFilePartial(this.dataset.fileId, 0, 10).subscribe((response: string | undefined) => {
+ if (response && this.dataset != undefined) {
+ this.tableData = this.csvParseService.csvToArray(response, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter.toString() == "") ? "," : this.dataset.delimiter);
+ }
+ });
+ this.loaded = true;
+ }
+
+ ngAfterViewInit(): void {
+
+ }
+
+ setColumnTypeInitial() {
+ if (this.dataset != undefined) {
+ for (let i = 0; i < this.dataset.columnInfo.length; i++) {
+ this.experiment.columnTypes[i] = (this.dataset.columnInfo[i].isNumber) ? ColumnType.numerical : ColumnType.categorical;
+ }
+ }
+ }
+
+ resetInputColumns() {
+ if (this.dataset != undefined) {
+ this.experiment.inputColumns = [];
+ for (let i = 0; i < this.dataset?.columnInfo.length; i++) {
+ this.experiment.inputColumns.push(this.dataset.columnInfo[i].columnName);
+ }
+ }
+ }
+ resetOutputColumn() {
+ this.experiment.outputColumn = this.experiment.inputColumns[0];
+ }
+
+ setDeleteRowsForMissingValTreatment() {
+ if (this.experiment != undefined) {
+ this.experiment.nullValues = NullValueOptions.DeleteRows;
+ this.experiment.nullValuesReplacers = [];
+ for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ this.experiment.nullValuesReplacers.push({
+ column: this.experiment.inputColumns[i],
+ option: NullValueOptions.DeleteRows,
+ value: ""
+ });
+ }
+ }
+ }
+
+ columnTableChangeDetected() {
+ this.columnTableChanged.emit();
+ }
+
+ columnTypeChanged(columnName: string) {
+ if (this.experiment.outputColumn == columnName)
+ this.changeOutputColumn(columnName);
+ else
+ this.columnTableChangeDetected();
+ }
+
+ changeInputColumns(targetMatCheckbox: MatCheckboxChange, columnName: string) {
+ if (this.experiment != undefined) {
+
+ if (targetMatCheckbox.checked) {
+ if (this.experiment.inputColumns.filter(x => x == columnName)[0] == undefined) {
+ this.experiment.inputColumns.push(columnName);
+ }
+ }
+ else {
+ this.experiment.inputColumns = this.experiment.inputColumns.filter(x => x != columnName);
+ //console.log("Input columns: ", this.experiment.inputColumns);
+ //TODO: da se zatamni kolona koja je unchecked
+ //this.experiment.encodings = this.experiment.encodings.filter(x => x.columnName != columnName); samo na kraju iz enkodinga skloni necekirane
+ this.experiment.nullValuesReplacers = this.experiment.nullValuesReplacers.filter(x => x.column != columnName);
+ if (columnName == this.experiment.outputColumn)
+ this.experiment.outputColumn = this.experiment.inputColumns[0];
+ }
+ this.columnTableChangeDetected();
+ }
+ }
+
+ changeOutputColumn(columnName: string) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+ let i = this.dataset.columnInfo.findIndex(x => x.columnName == this.experiment!.outputColumn);
+ if (this.experiment.columnTypes[i] == ColumnType.numerical) {
+ this.experiment.type = ProblemType.Regression;
+ }
+ else {
+ if (this.dataset.columnInfo[i].uniqueValues!.length == 2)
+ this.experiment.type = ProblemType.BinaryClassification;
+ else
+ this.experiment.type = ProblemType.MultiClassification;
+ }
+ this.columnTableChangeDetected();
+ }
+ }
+
+ resetColumnEncodings(encodingType: Encoding) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+ this.experiment.encodings = [];
+ for (let i = 0; i < this.dataset?.columnInfo.length; i++) {
+ this.experiment.encodings.push(new ColumnEncoding(this.dataset?.columnInfo[i].columnName, encodingType));
+ //console.log(this.experiment.encodings);
+ }
+ this.columnTableChangeDetected();
+ }
+ }
+ openEncodingDialog() {
+ const dialogRef = this.dialog.open(EncodingDialogComponent, {
+ width: '300px'
+ });
+ dialogRef.afterClosed().subscribe(selectedEncoding => {
+ if (selectedEncoding != undefined)
+ this.resetColumnEncodings(selectedEncoding);
+ });
+ }
+
+ resetMissingValuesTreatment(selectedMissingValuesOption: NullValueOptions) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+
+ if (selectedMissingValuesOption == NullValueOptions.DeleteColumns) {
+ this.experiment.nullValues = NullValueOptions.DeleteColumns;
+ this.experiment.nullValuesReplacers = [];
+ for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ this.experiment.nullValuesReplacers.push({
+ column: this.experiment.inputColumns[i],
+ option: NullValueOptions.DeleteColumns,
+ value: ""
+ });
+ this.nullValOption[i] = "Obriši kolonu";
+ }
+ }
+ else if (selectedMissingValuesOption == NullValueOptions.DeleteRows) {
+ this.experiment.nullValues = NullValueOptions.DeleteRows;
+ this.experiment.nullValuesReplacers = [];
+ for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ this.experiment.nullValuesReplacers.push({
+ column: this.experiment.inputColumns[i],
+ option: NullValueOptions.DeleteRows,
+ value: ""
+ });
+ let numOfRowsToDelete = (this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.inputColumns[i])[0]).numNulls;
+ this.nullValOption[i] = "Obriši redove (" + numOfRowsToDelete + ")";
+ }
+ }
+ this.columnTableChangeDetected();
+ }
+ }
+ openMissingValuesDialog() {
+ const dialogRef = this.dialog.open(MissingvaluesDialogComponent, {
+ width: '400px'
+ });
+ dialogRef.afterClosed().subscribe(selectedMissingValuesOption => {
+ if (selectedMissingValuesOption != undefined)
+ this.resetMissingValuesTreatment(selectedMissingValuesOption);
+ });
+ }
+
+ openSaveExperimentDialog() {
+ const dialogRef = this.dialog.open(SaveExperimentDialogComponent, {
+ width: '400px'
+ });
+ dialogRef.afterClosed().subscribe(selectedName => {
+ this.experiment.name = selectedName;
+ //napravi odvojene dugmice za save i update -> za update nece da se otvara dijalog za ime
+ this.experimentService.addExperiment(this.experiment).subscribe((response) => {
+ this.experiment._id = response._id;
+ this.okPressed.emit();
+ });
+ });
+ }
+
+ openUpdateExperimentDialog() {
+ this.experimentService.updateExperiment(this.experiment).subscribe((response) => {
+ this.experiment = response;
+ Shared.openDialog("Izmena eksperimenta", "Uspešno ste izmenili podatke o eksperimentu.");
+ });
+ }
+
+ MissValsDeleteClicked(event: Event, replacementType: NullValueOptions, index: number) {
+ if (this.experiment != undefined && this.dataset != undefined) {
+ let columnName = (<HTMLInputElement>event.currentTarget).value;
+ let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0];
+
+ if (arrayElement == undefined) {
+ this.experiment.nullValuesReplacers.push({
+ column: columnName,
+ option: (replacementType == NullValueOptions.DeleteColumns) ? NullValueOptions.DeleteColumns : NullValueOptions.DeleteRows,
+ value: ""
+ });
+ }
+ else {
+ arrayElement.option = (replacementType == NullValueOptions.DeleteColumns) ? NullValueOptions.DeleteColumns : NullValueOptions.DeleteRows;
+ arrayElement.value = "";
+ }
+
+ let numOfRowsToDelete = (this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.inputColumns[index])[0]).numNulls;
+ this.nullValOption[index] = (replacementType == NullValueOptions.DeleteColumns) ? "Obriši kolonu" : "Obriši redove (" + numOfRowsToDelete + ")";
+ this.columnTableChangeDetected();
+ }
+ }
+
+ MissValsReplaceClicked(event: Event, columnName: string, index: number) {
+ if (this.experiment != undefined) {
+ let fillValue = (<HTMLInputElement>event.currentTarget).value;
+ let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0];
+
+ if (arrayElement == undefined) {
+ this.experiment.nullValuesReplacers.push({
+ column: columnName,
+ option: NullValueOptions.Replace,
+ value: fillValue
+ });
+ }
+ else {
+ arrayElement.option = NullValueOptions.Replace;
+ arrayElement.value = fillValue;
+ }
+
+ this.nullValOption[index] = "Popuni sa: " + fillValue;
+ this.columnTableChangeDetected();
+ }
+ }
+ getValue(columnName: string): string {
+ if (<HTMLInputElement>document.getElementById(columnName) != undefined)
+ return (<HTMLInputElement>document.getElementById(columnName)).value;
+ return '0';
+ }
+ saveExperiment() {
+ this.openSaveExperimentDialog();
+ }
+ updateExperiment() {
+ this.openUpdateExperimentDialog();
+ }
+
+
+ tabs = [
+ new Tab(0, 'Podešavanja kolona', Table.Columns),
+ new Tab(1, 'Podaci', Table.Data),
+ new Tab(2, 'Korelaciona matrica', Table.CorrelationMatrix)
+ ]
+
+ selectedTab: Tab = this.tabs[0];
+ hoveringOverTab: (Tab | null) = null;
+
+ tabToDisplay: Table = Table.Columns;
+
+ selectTab(index: number) {
+ this.selectedTab = this.tabs[index];
+ this.tabToDisplay = this.tabs[index].value;
+ }
+
+ hoverOverTab(index: number) {
+ if (index < 0) {
+ this.hoveringOverTab = null;
+ this.tabToDisplay = this.selectedTab.value;
+ } else {
+ this.hoveringOverTab = this.tabs[index];
+ this.tabToDisplay = this.tabs[index].value;
+ }
+ }
+
+ calcZIndex(i: number) {
+ let zIndex = (this.tabs.length - i - 1)
+ if (this.selectedTab.index == i)
+ zIndex = this.tabs.length + 1;
+ if (this.hoveringOverTab?.index == i)
+ zIndex = this.tabs.length + 2;
+ return zIndex;
+ }
+
+ Table = Table;
+}
+
+export enum Table {
+ Columns,
+ Data,
+ CorrelationMatrix
+}
+
+export class Tab {
+ constructor(
+ public index: number,
+ public name: string,
+ public value: Table
+ ) { }
+}
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.css b/frontend/src/app/_elements/dataset-load/dataset-load.component.css
deleted file mode 100644
index ff6e2750..00000000
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.btnType1 {
- background-color: #003459;
- color: white;
- padding-top: 2vh;
- padding-bottom: 2vh;
-}
-.btnType2 {
- background-color: white;
- color: #003459;
- border-color: #003459;
- padding-top: 2vh;
- padding-bottom: 2vh;
-
-}
-.selectedDatasetClass {
- /*border-color: 2px solid #003459;*/
- background-color: lightblue;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.html b/frontend/src/app/_elements/dataset-load/dataset-load.component.html
deleted file mode 100644
index f244a882..00000000
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<div>
-
- <!--Sklonjeno ucitavanje novog dataseta i sve opcije u vezi sa tim, premesteno u add-new-dataset-->
-
- <div class="d-flex flex-row justify-content-center align-items-center mt-3 mb-5">
- <button type="button" id="btnMyDataset" class="btn" (click)="viewMyDatasetsForm()" [ngClass]="{'btnType1': showMyDatasets, 'btnType2': !showMyDatasets}">
- Izaberite dataset iz kolekcije
- </button>
- <h3 class="mt-3 mx-3">ili</h3>
- <button type="button" id="btnNewDataset" class="btn" (click)="viewNewDatasetForm()" [ngClass]="{'btnType1': !showMyDatasets, 'btnType2': showMyDatasets}">
- Dodajte novi dataset
- </button>
- </div>
-
- <div class="px-5 my-2">
- <input *ngIf="showMyDatasets" type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term">
- </div>
- <div class="px-5" *ngIf="showMyDatasets">
- <div class="overflow-auto" style="max-height: 500px;">
- <ul class="list-group">
- <li class="list-group-item p-3" *ngFor="let dataset of myDatasets|filter:term" [ngClass]="{'selectedDatasetClass': this.selectedDataset == dataset}">
- <app-item-dataset name="usersDataset" [dataset]="dataset" (click)="selectThisDataset(dataset);"></app-item-dataset>
- </li>
- </ul>
- </div>
- <div class="px-5 mt-5">
- <app-datatable [tableData]="tableData"></app-datatable>
- </div>
- </div>
-
- <app-add-new-dataset [style]="(showMyDatasets)?'display:none;visibility:hidden;':''" id="dataset">
- </app-add-new-dataset>
-
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
index be1dc097..73dbf2d2 100644
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
+++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
@@ -8,7 +8,6 @@ import { DatasetsService } from 'src/app/_services/datasets.service';
import { CsvParseService } from 'src/app/_services/csv-parse.service';
import { Output, EventEmitter } from '@angular/core';
import { SignalRService } from 'src/app/_services/signal-r.service';
-import { AuthService } from 'src/app/_services/auth.service';
@Component({
selector: 'app-dataset-load',
@@ -34,15 +33,7 @@ export class DatasetLoadComponent implements OnInit {
term: string = "";
- constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService, private authService: AuthService) {
- this.fetchDatasets();
-
- authService.loggedInEvent.subscribe(_ => {
- this.fetchDatasets();
- })
- }
-
- fetchDatasets() {
+ constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService) {
this.datasets.getMyDatasets().subscribe((datasets) => {
this.myDatasets = datasets;
});
@@ -61,6 +52,13 @@ export class DatasetLoadComponent implements OnInit {
//this.resetCbsAndRbs(); //TREBA DA SE DESI
}
+ refreshMyDatasets() {
+ this.datasets.getMyDatasets().subscribe((datasets) => {
+ this.myDatasets = datasets;
+ this.showMyDatasets = true;
+ });
+ }
+
selectThisDataset(dataset: Dataset) {
this.selectedDataset = dataset;
this.selectedDatasetLoaded = false;
@@ -90,19 +88,10 @@ export class DatasetLoadComponent implements OnInit {
return true;
}
- refreshMyDatasets(selectedDatasetId: string | null) {
- this.datasets.getMyDatasets().subscribe((datasets) => {
- this.myDatasets = datasets.reverse();
- this.showMyDatasets = true;
- this.selectedDataset = this.myDatasets.filter(x => x._id == selectedDatasetId)[0];
- this.resetSelectedDataset();
- });
- }
-
ngOnInit(): void {
if (this.signalRService.hubConnection) {
- this.signalRService.hubConnection.on("NotifyDataset", (dName: string, dId: string) => {
- this.refreshMyDatasets(dId);
+ this.signalRService.hubConnection.on("NotifyDataset", _ => {
+ this.refreshMyDatasets();
});
} else {
console.warn("Dataset-Load: No connection!");
diff --git a/frontend/src/app/_elements/datatable/datatable.component.html b/frontend/src/app/_elements/datatable/datatable.component.html
index 8db62aff..27d66dd3 100644
--- a/frontend/src/app/_elements/datatable/datatable.component.html
+++ b/frontend/src/app/_elements/datatable/datatable.component.html
@@ -1,18 +1,11 @@
-<div *ngIf="tableData.hasInput">
- <div>
- <div *ngIf="!tableData.loaded" backgroundColor="secondary" style="width: 100%; height: 100%;"
- class="d-flex justify-content-center align-items-center">
+<div *ngIf="tableData.hasInput" class="position-relative">
+ <div class="text-white">
+ <div *ngIf="!tableData.loaded" style="width: 100%; height: 100%;" class="d-flex justify-content-center align-items-center">
<app-loading></app-loading>
</div>
<div *ngIf="tableData.loaded && tableData.data">
- <div id="info" *ngIf="tableData.data.length > 0 && tableData.data[0].length > 0"
- class="d-flex flex-row justify-content-center align-items-center">
- <div class="fs-5 mb-3">
- Tabela {{tableData.numCols}}x{{tableData.numRows}}
- </div>
- </div>
- <div class="table-responsive" style="overflow: auto; border-radius: 5px;">
- <table *ngIf="tableData.data.length > 0 && tableData.hasHeader && tableData.data[0].length > 0" class="table table-bordered table-light">
+ <div style="border-radius: 5px; overflow-x: auto; overflow-y: hidden;">
+ <table *ngIf="tableData.data.length && tableData.data[0].length > 0" class="table table-responsive table-sm text-offwhite row-height">
<thead>
<tr>
<th *ngFor="let item of tableData.data[0]; let i = index">{{item}}</th>
@@ -22,22 +15,23 @@
<tr *ngFor="let row of tableData.data | slice:1">
<td *ngFor="let col of row">{{col}}</td>
</tr>
- <tr>
- <td colspan="100" class="text-lg-center fs-6">+ {{tableData.numRows - 11}} redova...</td>
- </tr>
- </tbody>
- </table>
- <table *ngIf="tableData.data.length > 0 && !tableData.hasHeader && tableData.data[0].length > 0" class="table table-bordered table-light">
- <tbody>
- <tr *ngFor="let row of tableData.data">
- <td *ngFor="let col of row">{{col}}</td>
- </tr>
- <tr>
- <td colspan="100" class="text-lg-center fs-6">+ {{tableData.numRows - 10}} redova...</td>
- </tr>
</tbody>
</table>
</div>
+ <div class="row" >
+ <div id="info" *ngIf="tableData.data.length > 0 && tableData.data[0].length > 0" class="d-flex flex-row justify-content-right align-items-right col-sm">
+ <div class=" mb-3">
+ Tabela {{tableData.numCols}} x {{tableData.numRows}}
+ </div>
+ </div>
+ <div class="col-sm"></div>
+ <div class="col-sm"></div>
+ <div class="footer-center col-sm">Prikazano 10/{{tableData.numRows}} redova</div>
+ <div class="col-sm"></div>
+ <div class="col-sm"></div>
+ <div class="col-sm"></div>
+
+ </div>
</div>
</div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/datatable/datatable.component.ts b/frontend/src/app/_elements/datatable/datatable.component.ts
index 82374f4d..560a1c21 100644
--- a/frontend/src/app/_elements/datatable/datatable.component.ts
+++ b/frontend/src/app/_elements/datatable/datatable.component.ts
@@ -18,7 +18,6 @@ export class DatatableComponent implements OnInit {
export class TableData {
constructor(
- public hasHeader = true,
public hasInput = false,
public loaded = false,
public numRows = 0,
diff --git a/frontend/src/app/_elements/folder/folder.component.css b/frontend/src/app/_elements/folder/folder.component.css
new file mode 100644
index 00000000..62324d62
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.css
@@ -0,0 +1,213 @@
+#folder {
+ /*position: absolute;
+ left: 50%;
+ transform: translateX(-50%);*/
+}
+
+#tabs {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ height: 3.1rem;
+}
+
+#tabs>.folder-tab:not(:first-child) {
+ margin-left: -5px;
+}
+
+.folder-tab {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ position: relative;
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-color: var(--ns-primary);
+ color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 0 1px;
+}
+
+.folder-tab:not(:first-child) {
+ margin-block-start: auto;
+}
+
+.selected-tab {
+ height: 3rem;
+ background-color: var(--ns-primary);
+ color: var(--offwhite);
+}
+
+.hover-tab {
+ height: 3.2rem;
+}
+
+.selected-tab,
+.hover-tab {
+ width: fit-content !important;
+}
+
+.tab-link {
+ color: var(--offwhite) !important;
+ text-decoration: none !important;
+ cursor: pointer;
+ padding: 0.5rem;
+}
+
+.tab-link:active {
+ text-decoration: underline !important;
+}
+
+.selected-tab {
+ background-color: var(--ns-primary);
+}
+
+#searchbar {
+ height: 2.5rem;
+ background-color: var(--ns-bg-dark-100);
+ border-bottom: 1px solid var(--ns-primary);
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: flex-start;
+ width: 100%;
+}
+
+.collapse-horizontal {
+ white-space: nowrap;
+ height: 2.5rem;
+ overflow-x: hidden;
+}
+
+#search-options {
+ margin-left: auto;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ height: 100%;
+}
+
+#selected-content {
+ background-color: var(--ns-bg-dark-50);
+ width: 100%;
+ /*backdrop-filter: blur(2px);*/
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 1px 1px 1px 1px;
+ border-top-right-radius: 4px;
+}
+
+#footer {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+}
+
+.bottom-button {
+ font-size: large;
+ position: relative;
+ background-color: var(--ns-primary);
+ width: 10rem;
+ height: 2.3rem;
+ border-color: var(--ns-primary);
+ border-style: solid;
+ border-width: 0px 1px 1px 1px;
+}
+
+.rounded-bottom {
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+
+.separator {
+ border-left-color: var(--ns-primary);
+ border-left-width: 1px;
+ border-left-style: solid;
+}
+
+.list-view {
+ height: 100%;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+}
+
+.list-item {
+ height: 3rem;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ border-bottom: 1px solid var(--ns-primary);
+ flex-shrink: 0;
+}
+
+.list-item:hover {
+ background-color: var(--ns-bg-dark-100);
+ box-shadow: 0px 3px 3px var(--ns-primary);
+}
+
+.list-item:hover>.hover-hide {
+ display: none;
+}
+
+.hover-show {
+ display: none;
+}
+
+.list-item:hover>.hover-show {
+ display: initial;
+}
+
+.list-add {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ flex-grow: 1;
+ height: 100%;
+}
+
+.folder-inside {
+ width: 100%;
+ height: 40rem;
+ overflow-y: auto;
+}
+
+.file-content {
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+
+.file-bottom-buttons {
+ position: absolute;
+ bottom: 15px;
+ right: 15px;
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+.file-button {
+ position: relative;
+ color: var(--offwhite);
+ border-radius: 4px;
+ border: 1px solid var(--ns-primary);
+ margin: 5px;
+ padding: 5px;
+ cursor: pointer;
+ z-index: 1001;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+.file-button:hover {
+ background-color: var(--ns-primary);
+}
+
+.form-hidden {
+ display: none;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/folder/folder.component.html b/frontend/src/app/_elements/folder/folder.component.html
new file mode 100644
index 00000000..bff066be
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.html
@@ -0,0 +1,111 @@
+<div id="folder">
+ <div id="tabs">
+ <div id="new-file-tab" *ngIf="newFile" class="folder-tab p-1 rounded-top" [style]="'z-index:' + (selectedTab == TabType.NewFile ? 11 : 10) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == TabType.NewFile, 'hover-tab' : hoverTab == TabType.NewFile}">
+ <mat-icon class="text-offwhite">add</mat-icon>
+ <a class="stretched-link tab-link" (click)="selectTab(TabType.NewFile)" (mouseenter)="hoverOverTab(TabType.NewFile)" (mouseleave)="hoverOverTab(TabType.None)">
+ {{newFile.name}}
+ </a>
+ </div>
+ <!--<div class="folder-tab p-1 rounded-top" *ngFor="let file of filteredFiles; let i = index" [style]="'z-index:' + calcZIndex(i) + ' ;'" [ngClass]="{'selected-tab' : selectedFileIndex == i, 'hover-tab' : hoveringOverFileIndex == i}">
+ <a class="m-1 stretched-link tab-link" (click)="selectFile(i)" (mouseenter)="hoverOverFile(i)" (mouseleave)="hoverOverFile(-1)">{{file.name}}</a>
+ </div>-->
+ <div class="folder-tab p-1 rounded-top" *ngFor="let tab of tabsToShow; let i = index" [style]="'z-index:' + (selectedTab == tab ? 11 : (tabsToShow.length - i)) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == tab, 'hover-tab' : hoverTab == tab}">
+ <a class="m-1 stretched-link tab-link" (click)="selectTab(tab)" (mouseenter)="hoverOverTab(tab)" (mouseleave)="hoverOverTab(TabType.None)">{{tabTitles[tab]}}</a>
+ </div>
+
+ <div class="folder-tab p-1 rounded-top" *ngIf="selectedFile" [style]="'z-index:' + (selectedTab == TabType.File ? 11 : (tabsToShow.length)) + ' ;'" [ngClass]="{'selected-tab' : selectedTab == TabType.File, 'hover-tab' : hoverTab == TabType.File}">
+ <a class="m-1 stretched-link tab-link" (click)="selectTab(TabType.File)" (mouseenter)="hoverOverTab(TabType.File)" (mouseleave)="hoverOverTab(TabType.None)">{{selectedFile.name}}</a>
+ </div>
+ </div>
+ <div id="selected-content" class="rounded-bottom text-offwhite">
+ <div id="searchbar" *ngIf="listView">
+ <!-- <div id="path" class="ps-2">{{folderName}}
+ </div>
+ <mat-icon>keyboard_arrow_right</mat-icon> -->
+ <div id="search" class="text-offwhite mx-1">
+ <mat-form-field>
+ <button matPrefix class="btn-clear input-icon"><mat-icon>search</mat-icon></button>
+ <input type="search" matInput name="search" [(ngModel)]="searchTerm" (input)="searchTermsChanged()">
+ <button matSuffix class="btn-clear input-icon" (click)="clearSearchTerm()"><mat-icon>clear</mat-icon></button>
+ </mat-form-field>
+ </div>
+ <div id="search-options">
+ <!-- <div id="collapseFilters" class="collapse collapse-horizontal">
+ <mat-icon class="text-offwhite ">timeline</mat-icon>
+ Regresioni
+ <mat-icon class="text-offwhite ">looks_two</mat-icon>
+ Binarni klasifikacioni
+ <mat-icon class="text-offwhite ">auto_awesome_motion</mat-icon>
+ Multiklasifikacioni
+ </div> -->
+ <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseFilters" aria-expanded="false" aria-controls="collapseFilters">
+ <mat-icon>filter_alt</mat-icon>
+ </button>
+ <!-- <div id="collapseSort" class="collapse collapse-horizontal">
+ [sort options here TODO]
+ </div> -->
+ <button class="btn-clear icon-toggle" data-bs-toggle="collapse" data-bs-target="#collapseSort" aria-expanded="false" aria-controls="collapseSort">
+ <mat-icon>sort</mat-icon>
+ </button>
+ <!-- <button class="btn-clear icon-toggle separator" [ngClass]="{'icon-toggle-on': listView}" (click)="toggleListView()">
+ <mat-icon>view_list</mat-icon>
+ </button> -->
+ </div>
+ </div>
+ <!--{{fileToDisplay ? fileToDisplay.name : 'No file selected.'}} {{selectedFileIndex}} {{hoveringOverFileIndex}}-->
+ <div class="folder-inside bg-blur">
+ <div class="file-content" [ngClass]="{'form-hidden' : listView}">
+ <div class="file-bottom-buttons" *ngIf="selectedTab != TabType.NewFile">
+ <button *ngIf="this.selectedFile && selectedTab == TabType.File" class="btn-clear file-button" (click)="deleteFile(this.selectedFile, $event)">
+ <mat-icon>delete</mat-icon>
+ </button>
+ <!-- <button class="btn-clear file-button">
+ <mat-icon>zoom_out_map</mat-icon>
+ </button> -->
+ </div>
+ <app-form-model [ngClass]="{'form-hidden': type != FolderType.Model}" [forExperiment]="forExperiment"></app-form-model>
+ <app-form-dataset [ngClass]="{'form-hidden': type != FolderType.Dataset}" [forExperiment]="forExperiment"></app-form-dataset>
+ </div>
+ <div [ngClass]="{'form-hidden' : !listView}" class="list-view">
+ <div *ngFor="let file of filteredFiles; let i = index" class="list-item force-link" (click)="selectFile(file)">
+ <div class="mx-2">
+ {{file.name}}
+ </div>
+ <div class="mx-2 hover-hide">
+ {{file.lastUpdated | date}}
+ </div>
+ <div class="mx-2 hover-show" *ngIf="selectedTab !== TabType.PublicDatasets && selectedTab !== TabType.PublicModels">
+ <button class="btn-clear file-button" (click)="deleteFile(file, $event)">
+ <mat-icon>delete</mat-icon>
+ </button>
+ </div>
+
+ </div>
+ <div class="list-add" [ngSwitch]="type">
+ <button mat-raised-button *ngSwitchCase="FolderType.Dataset" (click)="selectNewFile()">Dodaj {{privacy == Privacy.Public ? 'javni ' : ' '}}izvor podataka</button>
+ <button mat-raised-button *ngSwitchCase="FolderType.Model" (click)="selectNewFile()">Dodaj {{privacy == Privacy.Public ? 'javnu ' : ' '}}konfiguraciju neuronske mreže</button>
+ <button mat-raised-button *ngSwitchCase="FolderType.Experiment" routerLink="/experiment">Dodaj eksperiment</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer" [ngSwitch]="newFileSelected" *ngIf="!listView">
+ <button mat-button (click)="saveNewFile()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="true">
+ <div class="f-row">
+ <div>Sačuvaj</div>
+ <div class="pt-1">
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ <button mat-button (click)="ok()" class="bottom-button text-offwhite rounded-bottom" *ngSwitchCase="false">
+ <div class="f-row">
+ <div>Ok</div>
+ <div class="icon-double pt-1">
+ <mat-icon>check</mat-icon>
+ <mat-icon>check</mat-icon>
+ </div>
+ </div>
+ </button>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/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..fabb524c
--- /dev/null
+++ b/frontend/src/app/_elements/folder/folder.component.ts
@@ -0,0 +1,379 @@
+import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import { FolderFile, FolderType } from 'src/app/_data/FolderFile';
+import Model from 'src/app/_data/Model';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+import Shared from 'src/app/Shared';
+import { ModelsService } from 'src/app/_services/models.service';
+import { FormDatasetComponent } from '../form-dataset/form-dataset.component';
+import Experiment from 'src/app/_data/Experiment';
+import { ExperimentsService } from 'src/app/_services/experiments.service';
+import { PredictorsService } from 'src/app/_services/predictors.service';
+import { SignalRService } from 'src/app/_services/signal-r.service';
+import { FormModelComponent } from '../form-model/form-model.component';
+
+@Component({
+ selector: 'app-folder',
+ templateUrl: './folder.component.html',
+ styleUrls: ['./folder.component.css']
+})
+export class FolderComponent implements AfterViewInit {
+
+ @ViewChild(FormDatasetComponent) formDataset!: FormDatasetComponent;
+ @ViewChild(FormModelComponent) formModel!: FormModelComponent;
+
+ @Input() folderName: string = 'Moji podaci';
+ @Input() files!: FolderFile[]
+
+ newFile?: Dataset | Model;
+
+ @Input() type: FolderType = FolderType.Dataset;
+ @Input() forExperiment!: Experiment;
+ @Input() startingTab!: TabType;
+
+ newFileSelected: boolean = true;
+
+ selectedFileIndex: number = -1;
+ selectedFile?: FolderFile;
+ hoveringOverFileIndex: number = -1;
+
+ fileToDisplay?: FolderFile;
+
+ @Output() selectedFileChanged: EventEmitter<FolderFile> = new EventEmitter();
+ @Output() okPressed: EventEmitter<string> = new EventEmitter();
+
+ searchTerm: string = '';
+
+ constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService, private signalRService: SignalRService) {
+ this.tabsToShow.forEach(tab => this.folders[tab] = []);
+ }
+
+ ngAfterViewInit(): void {
+ this.refreshFiles(null);
+
+ if (this.signalRService.hubConnection) {
+ this.signalRService.hubConnection.on("NotifyDataset", (dName: string, dId: string) => {
+ if (this.type == FolderType.Dataset) {
+ this.refreshFiles(dId);
+ }
+ });
+ } else {
+ console.warn("Dataset-Load: No connection!");
+ }
+ }
+
+ displayFile() {
+ if (this.type == FolderType.Dataset)
+ this.formDataset.dataset = <Dataset>this.fileToDisplay;
+ else if (this.type == FolderType.Model)
+ this.formModel.newModel = <Model>this.fileToDisplay;
+ }
+
+ hoverOverFile(i: number) {
+ /*this.hoveringOverFileIndex = i;
+ if (i != -1) {
+ this.fileToDisplay = this.files[i];
+ } else {
+ if (this.newFileSelected) {
+ this.fileToDisplay = this.newFile;
+ } else {
+ this.fileToDisplay = this.files[this.selectedFileIndex];
+ }
+ }
+ this.displayFile();*/
+ }
+
+ selectNewFile() {
+ if (!this.newFile) {
+ this.createNewFile();
+ }
+ this.fileToDisplay = this.newFile;
+ this.newFileSelected = true;
+ this.listView = false;
+ this.displayFile();
+ if(this.type == FolderType.Dataset)
+ this.formDataset.clear();
+ }
+
+ selectFile(file?: FolderFile) {
+ this.selectedFile = file;
+ this.fileToDisplay = file;
+ this.newFileSelected = false;
+ this.listView = false;
+ this.selectedFileChanged.emit(this.selectedFile);
+ this.selectTab(TabType.File);
+ this.displayFile();
+
+ if(this.type == FolderType.Dataset)
+ this.formDataset.loadExisting();
+ }
+
+ createNewFile() {
+ if (this.type == FolderType.Dataset) {
+ this.newFile = new Dataset();
+ } else if (this.type == FolderType.Model) {
+ this.newFile = new Model();
+ }
+ }
+
+ ok() {
+ this.okPressed.emit();
+ }
+
+ _initialized: boolean = false;
+
+ refreshFiles(selectedDatasetId: string | null) {
+ this.files = []
+ this.filteredFiles.length = 0;
+ this.folders[TabType.NewFile] = [];
+ this.folders[TabType.File] = [];
+ this.tabsToShow.forEach(tab => {
+ this.folders[tab] = [];
+ });
+
+ this.datasetsService.getMyDatasets().subscribe((datasets) => {
+ this.folders[TabType.MyDatasets] = datasets;
+ if (selectedDatasetId) {
+ this.selectFile(datasets.filter(x => x._id == selectedDatasetId)[0]);
+ }
+ });
+
+ this.experimentsService.getMyExperiments().subscribe((experiments) => {
+ this.folders[TabType.MyExperiments] = experiments;
+ });
+
+ this.datasetsService.getPublicDatasets().subscribe((datasets) => {
+ this.folders[TabType.PublicDatasets] = datasets;
+ });
+
+ this.modelsService.getMyModels().subscribe((models) => {
+ this.folders[TabType.MyModels] = models;
+ });
+
+ /*this.modelsService.getMyModels().subscribe((models) => {
+ this.folders[TabType.PublicModels] = models;
+ });*/
+ this.folders[TabType.PublicModels] = [];
+
+ this.experimentsService.getMyExperiments().subscribe((experiments) => {
+ this.folders[TabType.MyExperiments] = experiments;
+ });
+
+ if (!this._initialized) {
+ this.files = this.folders[this.startingTab];
+ this.filteredFiles = [];
+ this.selectTab(this.startingTab);
+ this._initialized = true;
+ }
+
+ this.searchTermsChanged();
+ }
+
+ saveNewFile() {
+ switch (this.type) {
+ case FolderType.Dataset:
+ this.formDataset!.uploadDataset((dataset: Dataset) => {
+ this.newFile = undefined;
+ Shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira.");
+ this.refreshFiles(null);
+ },
+ () => {
+ Shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset.");
+ });
+ break;
+ case FolderType.Model:
+ this.modelsService.addModel(this.formModel.newModel).subscribe(model => {
+ this.newFile = undefined;
+ Shared.openDialog("Obaveštenje", "Uspešno ste dodali novu konfiguraciju neuronske mreže u kolekciju.");
+ this.refreshFiles(null); // todo select model
+ }, (err) => {
+ Shared.openDialog("Neuspeo pokušaj!", "Konfiguracija neuronske mreže sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeću konfiguraciju.");
+ });
+ break;
+ }
+ }
+
+
+ /*calcZIndex(i: number) {
+ let zIndex = (this.files.length - i - 1)
+ if (this.selectedFileIndex == i)
+ zIndex = this.files.length + 2;
+ if (this.hoveringOverFileIndex == i)
+ zIndex = this.files.length + 3;
+ return zIndex;
+ }
+
+ newFileZIndex() {
+ return (this.files.length + 1);
+ }*/
+
+ clearSearchTerm() {
+ this.searchTerm = '';
+ this.searchTermsChanged();
+ }
+
+ filteredFiles: FolderFile[] = [];
+
+ searchTermsChanged() {
+ this.filteredFiles.length = 0;
+ if (!this.files) return;
+ this.filteredFiles.push(...this.files.filter((file) => file.name.toLowerCase().includes(this.searchTerm.toLowerCase())));
+ /*if (this.selectedFile) {
+ if (!this.filteredFiles.includes(this.selectedFile)) {
+ if (this.hoverTab === TabType.None && this.getFolderType(this.selectedTab) === this.type) {
+ this.selectFile(undefined);
+ console.log(this.getFolderType(this.selectedTab), this.type);
+ }
+ } else {
+ //this.selectedFileIndex = this.filteredFiles.indexOf(this.selectedFile);
+ }
+ }*/
+ }
+
+ listView: boolean = true;
+
+ toggleListView() {
+ this.listView = !this.listView;
+ }
+
+ deleteFile(file: FolderFile, event: Event) {
+ event.stopPropagation();
+ //console.log('delete');
+ switch (this.type) {
+ case FolderType.Dataset:
+ this.datasetsService.deleteDataset(<Dataset>file).subscribe((response) => {
+ this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1);
+ this.refreshFiles(null);
+ });
+ break;
+ case FolderType.Model:
+ this.modelsService.deleteModel(<Model>file).subscribe((response) => {
+ this.refreshFiles(null);
+ });
+ break;
+ case FolderType.Experiment:
+ // this.experimentsService.deleteExperiment(<Model>file).subscribe((response) => {
+ // console.log(response);
+ // });
+ //todo delete za predictor
+ break;
+ }
+ }
+
+ folders: { [tab: number]: FolderFile[] } = {};
+
+ tabTitles: { [tab: number]: string } = {
+ [TabType.File]: 'Fajl',
+ [TabType.NewFile]: 'Novi fajl',
+ [TabType.MyDatasets]: 'Moji izvori podataka',
+ [TabType.PublicDatasets]: 'Javni izvori podataka',
+ [TabType.MyModels]: 'Moje konfiguracije neuronske mreže',
+ [TabType.PublicModels]: 'Javne konfiguracije neuronske mreže',
+ [TabType.MyExperiments]: 'Eksperimenti',
+ };
+
+ FolderType = FolderType;
+ Privacy = Privacy;
+ TabType = TabType;
+
+ privacy: Privacy = Privacy.Private;
+
+ @Input() tabsToShow: TabType[] = [
+ TabType.MyDatasets,
+ TabType.PublicDatasets,
+ TabType.MyModels,
+ TabType.PublicModels,
+ TabType.MyExperiments
+ ]
+
+ @Input() selectedTab: TabType = TabType.NewFile;
+ hoverTab: TabType = TabType.None;
+
+ selectTab(tab: TabType) {
+ setTimeout(() => {
+ if (tab == TabType.NewFile) {
+ this.selectNewFile();
+ }
+
+ this.listView = this.getListView(tab);
+ this.type = this.getFolderType(tab);
+ this.privacy = this.getPrivacy(tab);
+ this.selectedTab = tab;
+ this.files = this.folders[tab];
+
+ if (tab !== TabType.File && tab !== TabType.NewFile)
+ this.searchTermsChanged();
+ });
+ }
+
+ getListView(tab: TabType) {
+ switch (tab) {
+ case TabType.File:
+ case TabType.NewFile:
+ case TabType.None:
+ return false;
+ case TabType.MyExperiments:
+ case TabType.MyDatasets:
+ case TabType.MyModels:
+ case TabType.PublicDatasets:
+ case TabType.PublicModels:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ getFolderType(tab: TabType) {
+ switch (tab) {
+ case TabType.MyExperiments:
+ return FolderType.Experiment;
+ case TabType.MyDatasets:
+ case TabType.PublicDatasets:
+ return FolderType.Dataset;
+ case TabType.MyModels:
+ case TabType.PublicModels:
+ return FolderType.Model;
+ default:
+ return this.type;
+ }
+ }
+
+ getPrivacy(tab: TabType) {
+ switch (tab) {
+ case TabType.PublicDatasets:
+ case TabType.PublicModels:
+ return Privacy.Public;
+ default:
+ return Privacy.Private;
+ }
+ }
+
+ hoverOverTab(tab: TabType) {
+ this.listView = this.getListView(tab);
+ this.privacy = this.getPrivacy(tab);
+ this.hoverTab = tab;
+ if (tab == TabType.None) {
+ this.listView = this.getListView(this.selectedTab);
+ this.files = this.folders[this.selectedTab];
+ } else {
+ this.files = this.folders[tab];
+ }
+ this.searchTermsChanged();
+ }
+}
+
+export enum Privacy {
+ Private,
+ Public
+}
+
+export enum TabType {
+ NewFile,
+ File,
+ MyDatasets,
+ PublicDatasets,
+ MyModels,
+ PublicModels,
+ MyExperiments,
+ None
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.css b/frontend/src/app/_elements/form-dataset/form-dataset.component.css
new file mode 100644
index 00000000..953daa0c
--- /dev/null
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.css
@@ -0,0 +1,64 @@
+.folderBox {
+ width: 100%;
+ height: 100%;
+ position: relative;
+}
+
+.topBar {
+ width: 100%;
+ margin: 1rem;
+ align-items: flex-start;
+}
+
+.topBar label{
+ font-size: 30px;
+}
+.topBar mat-form-field{
+ width: 250px;
+}
+
+.toptop{
+ margin-left: 1.5%;
+ width: 50%;
+}
+
+.fileButton{
+ margin-top: 10px;
+}
+
+.file-container {
+ border: 4px solid transparent;
+ position: relative;
+ margin-left: 3%;
+ width: 94%;
+ min-height: 400px;
+ height: 95%;
+}
+
+.dottedClass {
+ border: 4px dotted white;
+ border-radius: 25px;
+}
+
+.icon-display {
+ position: absolute;
+ top: 45%;
+ left: 50%;
+ transform: translate(-50%, -50%) scale(4);
+}
+
+.hidden {
+ visibility: hidden;
+}
+
+.file {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+}
+
+.file-container input{
+ border-radius: 5px;
+ left: 0%;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.html b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
new file mode 100644
index 00000000..281f9c05
--- /dev/null
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
@@ -0,0 +1,65 @@
+<div class="folderBox" *ngIf="dataset">
+
+ <div class="row" style="margin-right: 0;">
+ <div class="topBar">
+ <div class="row toptop">
+ <div class="col-sm mb-3">
+ <div class="fileButton">
+ <button type="button" mat-raised-button (click)="fileInput.click()">Dodaj izvor podataka</button>
+
+ </div>
+ </div>
+
+ <div class="col-sm">
+ <div role="group">
+ <div class="row">
+ <mat-form-field class="example-full-width" appearance="fill">
+ <mat-label>Naziv</mat-label>
+ <input type="text" matInput value="{{dataset?.name}}" [(ngModel)]="dataset.name">
+
+
+ <mat-error *ngIf="nameFormControl.hasError('required')">
+ Naziv je <strong>obavezan</strong>
+ </mat-error>
+ </mat-form-field>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm">
+ <mat-form-field appearance="fill">
+ <mat-label>Delimiter</mat-label>
+ <mat-select id="delimiterOptions" [(ngModel)]="dataset.delimiter" (change)="update()" value=",">
+ <mat-option *ngFor="let option of delimiterOptions" [value]="option">
+ {{ option }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="col-sm">
+
+ </div>
+ </div>
+ <div class="row" *ngIf="firstInput">
+ <label class=" mt-5">{{filename}}</label>
+
+ </div>
+ </div>
+ </div>
+
+
+ <div class="row" style="margin-right: 0;">
+ <div class="file-container" [ngClass]="{'dottedClass': !tableData.hasInput}">
+
+ <i class="material-icons-outlined icon-display" [ngClass]="{'hidden': tableData.hasInput}">file_upload</i>
+
+ <input class="file" id="file-upload" (change)="changeListener($event)" #fileInput type="file" accept=".csv">
+
+ <div class="mt-5 datatable">
+ <app-datatable [tableData]="tableData"></app-datatable>
+ </div>
+
+
+ </div>
+ </div>
+
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.spec.ts b/frontend/src/app/_elements/form-dataset/form-dataset.component.spec.ts
index 5601b57b..51491c58 100644
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.spec.ts
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.spec.ts
@@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { DatasetLoadComponent } from './dataset-load.component';
+import { FormDatasetComponent } from './form-dataset.component';
-describe('DatasetLoadComponent', () => {
- let component: DatasetLoadComponent;
- let fixture: ComponentFixture<DatasetLoadComponent>;
+describe('FormDatasetComponent', () => {
+ let component: FormDatasetComponent;
+ let fixture: ComponentFixture<FormDatasetComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
- declarations: [ DatasetLoadComponent ]
+ declarations: [ FormDatasetComponent ]
})
.compileComponents();
});
beforeEach(() => {
- fixture = TestBed.createComponent(DatasetLoadComponent);
+ fixture = TestBed.createComponent(FormDatasetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
index 1f395105..1eed2cdc 100644
--- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
@@ -1,38 +1,50 @@
-import { Component, EventEmitter, Output, ViewChild } from '@angular/core';
+import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import Dataset from 'src/app/_data/Dataset';
import { DatasetsService } from 'src/app/_services/datasets.service';
import { ModelsService } from 'src/app/_services/models.service';
import shared from 'src/app/Shared';
import { DatatableComponent, TableData } from '../datatable/datatable.component';
import { CsvParseService } from 'src/app/_services/csv-parse.service';
+import { FormControl, Validators } from '@angular/forms';
@Component({
- selector: 'app-add-new-dataset',
- templateUrl: './add-new-dataset.component.html',
- styleUrls: ['./add-new-dataset.component.css']
+ selector: 'app-form-dataset',
+ templateUrl: './form-dataset.component.html',
+ styleUrls: ['./form-dataset.component.css']
})
-export class AddNewDatasetComponent {
+export class FormDatasetComponent {
@ViewChild(DatatableComponent) datatable!: DatatableComponent;
- delimiterOptions: Array<string> = [",", ";", "|", "razmak", "novi red"]; //podrazumevano ","
+ nameFormControl = new FormControl('', [Validators.required, Validators.email]);
+
+ delimiterOptions: Array<string> = [",", ";", "|", "razmak", "novi red"]; //podrazumevano ","
csvRecords: any[] = [];
files: File[] = [];
rowsNumber: number = 0;
colsNumber: number = 0;
- dataset: Dataset; //dodaj ! potencijalno
+ @Input() dataset: Dataset; //dodaj ! potencijalno
tableData: TableData = new TableData();
+ @ViewChild('fileInput') fileInput!: ElementRef
+
+ filename: String;
+
constructor(private modelsService: ModelsService, private datasetsService: DatasetsService, private csv: CsvParseService) {
this.dataset = new Dataset();
this.dataset.delimiter = ',';
+ this.filename = "";
}
//@ViewChild('fileImportInput', { static: false }) fileImportInput: any; cemu je ovo sluzilo?
+ clear(){
+ this.tableData.hasInput = false;
+ }
+
changeListener($event: any): void {
this.files = $event.srcElement.files;
if (this.files.length == 0 || this.files[0] == null) {
@@ -42,12 +54,17 @@ export class AddNewDatasetComponent {
else
this.tableData.hasInput = true;
+ this.filename = this.files[0].name;
this.tableData.loaded = false;
this.update();
}
+ firstInput = false;
+
update() {
+ this.firstInput = true
+
if (this.files.length < 1)
return;
@@ -56,48 +73,73 @@ export class AddNewDatasetComponent {
if (typeof fileReader.result === 'string') {
const result = this.csv.csvToArray(fileReader.result, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter)
- if (this.dataset.hasHeader)
- this.csvRecords = result.splice(0, 11);
- else
- this.csvRecords = result.splice(0, 10);
+
+ this.csvRecords = result.splice(0, 11);
this.colsNumber = result[0].length;
this.rowsNumber = result.length;
- this.tableData.data = this.csvRecords
- this.tableData.hasHeader = this.dataset.hasHeader;
+ this.tableData.data = this.csvRecords;
this.tableData.loaded = true;
this.tableData.numCols = this.colsNumber;
this.tableData.numRows = this.rowsNumber;
}
}
fileReader.readAsText(this.files[0]);
+
+ this.dataset.name = this.filename.slice(0, this.filename.length - 4);
+ }
+
+ loadExisting(){
+ this.firstInput = false;
+
+ this.tableData.hasInput = true;
+ this.tableData.loaded = false;
+
+ this.datasetsService.getDatasetFile(this.dataset.fileId).subscribe((file: string | undefined) => {
+ if (file) {
+ this.tableData.loaded = true;
+ this.tableData.numRows = this.dataset.rowCount;
+ this.tableData.numCols = this.dataset.columnInfo.length;
+ this.tableData.data = this.csv.csvToArray(file, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter);
+
+ }
+ });
+
+
}
+ /*exportAsXLSX():void {
+ this.excelService.exportAsExcelFile(this.data, 'sample');
+ }*/
+
checkAccessible() {
if (this.dataset.isPublic)
this.dataset.accessibleByLink = true;
}
- uploadDataset() {
+ uploadDataset(onSuccess: Function = (dataset: Dataset) => { }, onError: Function = () => { }) {
if (this.files[0] == undefined) {
shared.openDialog("Greška", "Niste izabrali fajl za učitavanje.");
return;
}
- this.modelsService.uploadData(this.files[0]).subscribe((file) => {
+ return this.modelsService.uploadData(this.files[0]).subscribe((file) => {
//console.log('ADD MODEL: STEP 2 - ADD DATASET WITH FILE ID ' + file._id);
+ this.dataset._id = "";
this.dataset.fileId = file._id;
this.dataset.uploaderId = shared.userId;
this.datasetsService.addDataset(this.dataset).subscribe((dataset) => {
- shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira.");
+ onSuccess();
}, (error) => {
- shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset.");
+ onError();
}); //kraj addDataset subscribe
}, (error) => {
-
+ onError();
}); //kraj uploadData subscribe
}
+
+
}
diff --git a/frontend/src/app/_elements/form-model/form-model.component.css b/frontend/src/app/_elements/form-model/form-model.component.css
new file mode 100644
index 00000000..11b6ef5e
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.css
@@ -0,0 +1,104 @@
+#container {
+ color: var(--offwhite);
+}
+
+mat-label {
+ color: var(--offwhite) !important;
+}
+
+select {
+ color: var(--offwhite) !important;
+}
+
+mat-form-field {
+ color: var(--offwhite) !important;
+ padding: 0;
+ margin: 5px;
+ font-size: 12px;
+ width: 100%;
+}
+
+hr {
+ color: var(--offwhite) !important;
+ margin-bottom: 30px;
+}
+
+.neuron {
+ text-align: justify;
+ border: 1px solid white;
+ border-radius: 5px;
+ padding: 0;
+ color: var(--offwhite) !important;
+ background-color: var(--ns-bg-dark-100) !important;
+ min-width: none;
+ max-width: 12.5rem;
+}
+
+.row {
+ margin: 0;
+ padding: 0;
+}
+
+.mat-fix ::ng-deep .mat-form-field-wrapper {
+ margin-bottom: -1.85em;
+}
+
+#layers-control {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+}
+
+#layers {
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: row;
+ overflow-x: auto;
+ overflow-wrap: break-word;
+ overflow-y: hidden;
+ width: 100%;
+}
+
+.layer {
+ border: 1px solid var(--ns-primary);
+ border-radius: 4px;
+ margin: 5px;
+ padding: 0px;
+ width: 12rem;
+ height: 11.1rem;
+}
+
+.tm {
+ margin-left: 10px;
+}
+
+.layer>* {
+ margin-top: 0;
+}
+
+.layer>mat-form-field {
+ margin-left: 0;
+}
+
+.m-2 {
+ max-height: 20 rem;
+}
+
+mat-slider {
+ width: 50%;
+}
+
+.slider {
+ background-color: transparent;
+}
+
+.center-center {
+ text-align: center;
+ margin-right: 10px;
+ padding-right: 10px;
+ padding-bottom: 15px;
+ font-size: 20px !important;
+ font-weight: 600;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/form-model/form-model.component.html b/frontend/src/app/_elements/form-model/form-model.component.html
new file mode 100644
index 00000000..96a5e1b6
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.html
@@ -0,0 +1,228 @@
+<div *ngIf="newModel">
+ <div id="container">
+ <div class="ns-row">
+
+ <div class="ns-col">
+ <mat-form-field class="example-full-width" appearance="fill" class="mat-fix">
+ <mat-label>Naziv</mat-label>
+ <input type="text" matInput [(ngModel)]="newModel.name">
+ </mat-form-field>
+ </div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Tip problema</mat-label>
+ <mat-select [(ngModel)]="newModel.type">
+ <mat-option *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <div class="break-1"></div>
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Optimizacija</mat-label>
+ <mat-select [(ngModel)]="newModel.optimizer">
+ <mat-option *ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+
+ </mat-form-field>
+ </div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Funkcija troška</mat-label>
+ <mat-select [(ngModel)]="newModel.lossFunction">
+ <mat-option *ngFor="let option of Object.keys(LossFunction); let optionName of Object.values(LossFunction)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <div class="break-2"></div>
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Funkcija aktivacije izlaznog sloja</mat-label>
+ <mat-select name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction">
+ <mat-option *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Stopa učenja</mat-label>
+ <mat-select [(ngModel)]="newModel.learningRate">
+ <mat-option *ngFor="let option of Object.keys(LearningRate); let optionName of Object.values(LearningRate)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <div class="break-1"></div>
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Broj epoha</mat-label>
+ <input type="number" matInput [(ngModel)]="newModel.epochs" min="1" max="1000">
+ </mat-form-field>
+ </div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Broj uzoraka po iteraciji</mat-label>
+
+ <mat-select matNativeControl [(value)]="newModel.batchSize">
+ <mat-option *ngFor="let option of Object.keys(BatchSize); let optionName of Object.values(BatchSize)" [value]="option">{{option}}</mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ </div>
+ </div>
+
+ <!-- GRAF -->
+
+ <div class="m-2">
+ <div class="row">
+ <div class="col-sm-3 rounded" style="border:1px solid var(--ns-primary);margin-top: 10px;">
+ <div class="row slider rounded mb-3" style="margin-left: 10px;">
+
+ <div class="text-center pt-3 pb-0 mb-0"><b>{{testSetDistribution}}%</b> : <b>{{100-testSetDistribution}}%</b></div>
+ <div class="text-center pt-0 mt-0">Trening
+ <mat-slider min="10" max="90" step="10" [(ngModel)]="testSetDistribution" (input)="updateTestSet($event)"></mat-slider>
+ Test</div>
+
+ </div>
+ <div class="row slider rounded text-offwhite justify-content-center align-items-center" style="margin-left: 10px;">
+ <mat-checkbox class="pt-4 mb-3" color="accent">Nasumični redosled podataka</mat-checkbox>
+ </div>
+
+
+ </div>
+
+ <div class="col-sm-9">
+ <!-- {{forExperiment._columnsSelected}} -->
+ <app-graph [model]="newModel" *ngIf="forExperiment._columnsSelected" [inputColumns]="forExperiment.inputColumns"></app-graph>
+ <app-graph [model]="newModel" *ngIf="!forExperiment._columnsSelected" [inputColumns]="['Nisu odabrane ulazne kolone']"></app-graph>
+ </div>
+ </div>
+ </div>
+
+ <!-- SVI LAYERI -->
+
+ <div class="ns-row">
+
+ <div class="ns-col" id="layers-control">
+ <div>Broj Skrivenih Slojeva</div>
+ <button class="btn-clear btn-icon bubble" (click)="addLayer()">
+ <mat-icon>add</mat-icon>
+ </button>
+ <div>{{newModel.hiddenLayers}}</div>
+ <button class="btn-clear btn-icon bubble" (click)="removeLayer()">
+ <mat-icon>remove</mat-icon>
+ </button>
+
+ </div>
+ <div class="break-1"></div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Aktivaciona funkcija svih slojeva</mat-label>
+
+ <mat-select [(ngModel)]="selectedActivation" (selectionChange)="changeAllActivation()">
+ <mat-option *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Broj neurona svih slojeva</mat-label>
+ <input matInput type="number" min="1" max="18" [(ngModel)]="selectedNumberOfNeurons" (change)="changeAllNumberOfNeurons()">
+ </mat-form-field>
+ </div>
+ <div class="break-2"></div>
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Regularizacija svih slojeva</mat-label>
+ <mat-select [(ngModel)]="selectedRegularisation" (selectionChange)="changeAllRegularisation()">
+ <mat-option *ngFor="let option of Object.keys(Regularisation); let optionName of Object.values(Regularisation)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+
+
+ <div class="ns-col">
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Stopa regularizacije svih slojeva</mat-label>
+ <mat-select [(ngModel)]="selectedRegularisationRate" (selectionChange)="changeAllRegularisationRate()">
+ <mat-option *ngFor="let option of Object.keys(RegularisationRate); let optionName of Object.values(RegularisationRate)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+
+ <!-- LAYERI -->
+
+ <div id="layers">
+ <div class="layer" *ngFor="let item of newModel.layers; let i=index">
+
+
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Aktivacija</mat-label>
+ <button matPrefix class="btn-clear center-center text-offwhite">
+ <div>
+ #{{i+1}}
+ </div>
+ </button>
+ <mat-select [(ngModel)]="newModel.layers[i].activationFunction">
+ <mat-option *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+
+ <div class="d-flex flex-row align-items-center justify-content-center tm">
+ <div class="col-6" style="font-size: 13px;">Broj čvorova</div>
+ <button class="btn-clear btn-icon bubble" (click)="addNeuron(i)">
+ <mat-icon>add</mat-icon>
+ </button>
+ <div class="col-2 text-center">{{newModel.layers[i].neurons}}</div>
+ <button class="btn-clear btn-icon bubble" (click)="removeNeuron(i)">
+ <mat-icon>remove</mat-icon>
+ </button>
+ </div>
+
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Regularizacija</mat-label>
+ <mat-select [(ngModel)]="newModel.layers[i].regularisation">
+ <mat-option *ngFor="let option of Object.keys(Regularisation); let optionName of Object.values(Regularisation)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+
+ <mat-form-field appearance="fill" class="mat-fix">
+ <mat-label>Stopa regularizacije</mat-label>
+ <mat-select [(ngModel)]="newModel.layers[i].regularisationRate">
+ <mat-option *ngFor="let option of Object.keys(RegularisationRate); let optionName of Object.values(RegularisationRate)" [value]="option">
+ {{ optionName }}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/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..71b374b0
--- /dev/null
+++ b/frontend/src/app/_elements/form-model/form-model.component.ts
@@ -0,0 +1,138 @@
+import { Component, OnInit, Input, ViewChild, Output, EventEmitter, AfterViewInit } from '@angular/core';
+import { FormControl, Validators } from '@angular/forms';
+import Shared from 'src/app/Shared';
+import Experiment from 'src/app/_data/Experiment';
+import Model, { Layer, ActivationFunction, LossFunction, LearningRate, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType, Regularisation, RegularisationRate, BatchSize } from 'src/app/_data/Model';
+import { GraphComponent } from '../graph/graph.component';
+import { MatSliderChange } from '@angular/material/slider';
+
+@Component({
+ selector: 'app-form-model',
+ templateUrl: './form-model.component.html',
+ styleUrls: ['./form-model.component.css']
+})
+export class FormModelComponent implements AfterViewInit {
+ @ViewChild(GraphComponent) graph!: GraphComponent;
+ @Input() forExperiment!: Experiment;
+ @Output() selectedModelChangeEvent = new EventEmitter<Model>();
+ testSetDistribution: number = 70;
+ constructor() { }
+
+ ngAfterViewInit(): void { }
+
+ selectFormControl = new FormControl('', Validators.required);
+ nameFormControl = new FormControl('', [Validators.required, Validators.email]);
+ selectTypeFormControl = new FormControl('', Validators.required);
+ selectOptFormControl = new FormControl('', Validators.required);
+ selectLFFormControl = new FormControl('', Validators.required);
+ selectLRFormControl = new FormControl('', Validators.required);
+ selectEpochFormControl = new FormControl('', Validators.required);
+ selectAFFormControl = new FormControl('', Validators.required);
+ selectBSFormControl = new FormControl('', Validators.required);
+ selectActivationFormControl = new FormControl('', Validators.required);
+ selectRegularisationFormControl = new FormControl('', Validators.required);
+ selectRRateFormControl = new FormControl('', Validators.required);
+
+ newModel!: Model;
+
+ selectedModel?: Model;
+
+ ProblemType = ProblemType;
+ ActivationFunction = ActivationFunction;
+ RegularisationRate = RegularisationRate;
+ Regularisation = Regularisation;
+ metrics: any = Metrics;
+ LossFunction = LossFunction;
+ Optimizer = Optimizer;
+ BatchSize = BatchSize;
+ Object = Object;
+ document = document;
+ shared = Shared;
+ LearningRate = LearningRate;
+ Layer = Layer;
+
+ term: string = "";
+ selectedMetrics = [];
+ lossFunction: any = LossFunction;
+
+ loadModel(model: Model) {
+ this.newModel = model;
+ }
+
+ updateGraph() {
+ //console.log(this.newModel.layers);
+ this.graph.update();
+ }
+
+ removeLayer() {
+ if (this.newModel.hiddenLayers > 1) {
+ this.newModel.layers.splice(this.newModel.layers.length - 1, 1);
+ this.newModel.hiddenLayers -= 1;
+ this.updateGraph();
+ }
+ }
+ addLayer() {
+ if (this.newModel.hiddenLayers < 128) {
+ this.newModel.layers.push(new Layer(this.newModel.layers.length, this.selectedActivation, this.selectedNumberOfNeurons, this.selectedRegularisation, this.selectedRegularisationRate));
+
+ this.newModel.hiddenLayers += 1;
+ this.updateGraph();
+ }
+
+ }
+ /*
+ setNeurons()
+ {
+ for(let i=0;i<this.newModel.hiddenLayers;i++){
+ this.newModel.hiddenLayerNeurons[i]=1;
+ }
+ }*/
+ numSequence(n: number): Array<number> {
+ return Array(n);
+ }
+
+ removeNeuron(index: number) {
+ if (this.newModel.layers[index].neurons > 1) {
+ this.newModel.layers[index].neurons -= 1;
+ this.updateGraph();
+ }
+ }
+ addNeuron(index: number) {
+ if (this.newModel.layers[index].neurons < 18) {
+ this.newModel.layers[index].neurons += 1;
+ this.updateGraph();
+ }
+ }
+ selectedActivation: ActivationFunction = ActivationFunction.Sigmoid;
+ selectedRegularisationRate: RegularisationRate = RegularisationRate.RR1;
+ selectedRegularisation: Regularisation = Regularisation.L1;
+ selectedNumberOfNeurons: number = 3;
+
+ changeAllActivation() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].activationFunction = this.selectedActivation;
+
+ }
+
+ }
+ changeAllRegularisation() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].regularisation = this.selectedRegularisation;
+ }
+ }
+ changeAllRegularisationRate() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].regularisationRate = this.selectedRegularisationRate;
+ }
+ }
+ changeAllNumberOfNeurons() {
+ for (let i = 0; i < this.newModel.layers.length; i++) {
+ this.newModel.layers[i].neurons = this.selectedNumberOfNeurons;
+ this.updateGraph();
+ }
+ }
+
+ updateTestSet(event: MatSliderChange) {
+ this.testSetDistribution = event.value!;
+ }
+}
diff --git a/frontend/src/app/_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..456a8df1 100644
--- a/frontend/src/app/_elements/graph/graph.component.css
+++ b/frontend/src/app/_elements/graph/graph.component.css
@@ -0,0 +1,20 @@
+.node-text {
+ position: absolute;
+ width: 100px;
+ height: 40px;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ /*border: 1px solid red;*/
+ transform: translate(-50%, -50%);
+}
+
+.node-text:not(.inputs) {
+ color: transparent;
+}
+
+.node-text:not(.inputs):hover {
+ color: var(--offwhite);
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/graph/graph.component.html b/frontend/src/app/_elements/graph/graph.component.html
index 1c21fb6c..35753d40 100644
--- a/frontend/src/app/_elements/graph/graph.component.html
+++ b/frontend/src/app/_elements/graph/graph.component.html
@@ -1,3 +1,8 @@
-<div #graphWrapper class="w-100" style="height: 16rem;">
- <canvas #graphCanvas class="border"></canvas>
+<div #graphWrapper class="w-100 position-relative" style="height: 14rem;">
+ <!-- <ng-container *ngFor="let layer of layers; let i = index">
+ <div [ngClass]="{'inputs': i==0}" class="node-text" *ngFor="let node of layer; let j = index" [style.left.%]="node.x * 99.4" [style.top.%]="node.y * 100">
+ {{ i == 0 ? (inputColumns && inputColumns.length >= j ? inputColumns[j] : 'nepoznato') : (i > 0 && i
+ < layers.length - 1 ? model!.layers[i-1].activationFunction : (i==layers.length - 1 ? 'out' : '')) }} </div>
+ </ng-container> -->
+ <canvas #graphCanvas></canvas>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/graph/graph.component.ts b/frontend/src/app/_elements/graph/graph.component.ts
index 8051acc3..da2c7767 100644
--- a/frontend/src/app/_elements/graph/graph.component.ts
+++ b/frontend/src/app/_elements/graph/graph.component.ts
@@ -1,6 +1,7 @@
import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
-import Dataset from 'src/app/_data/Dataset';
-import Model from 'src/app/_data/Model';
+import { RgbColor } from '@syncfusion/ej2-angular-heatmap';
+import Dataset, { ColumnInfo } from 'src/app/_data/Dataset';
+import Model, { Layer } from 'src/app/_data/Model';
@Component({
selector: 'app-graph',
@@ -15,17 +16,19 @@ export class GraphComponent implements AfterViewInit {
canvas!: ElementRef;
@Input() model?: Model;
- @Input() inputCols: number = 1;
+ //@Input() inputCols: number = 1;
@Input() lineThickness: number = 2;
@Input() nodeRadius: number = 15;
- @Input() lineColor: string = '#00a8e8';
+ @Input() lineColor1: RgbColor = new RgbColor(0, 168, 232);
+ @Input() lineColor2: RgbColor = new RgbColor(0, 70, 151);
@Input() nodeColor: string = '#222277';
@Input() borderColor: string = '#00a8e8';
- @Input() inputNodeColor: string = '#ffdd11';
- @Input() outputNodeColor: string = '#44ee22';
+ @Input() inputNodeColor: string = '#00a8e8';
+ @Input() outputNodeColor: string = '#dfd7d7';
- private ctx?: CanvasRenderingContext2D;
+ private ctx!: CanvasRenderingContext2D;
+ @Input() inputColumns?: string[];
constructor() { }
@@ -40,18 +43,19 @@ export class GraphComponent implements AfterViewInit {
window.addEventListener('resize', () => { this.resize() });
this.update();
this.resize();
+ //console.log(this.layers);
}
- layers?: Node[][];
+ layers: Node[][] = [];
update() {
- this.layers = [];
+ this.layers.length = 0;
let inputNodeIndex = 0;
const inputLayer: Node[] = [];
- while (inputNodeIndex < this.inputCols) {
+ while (this.inputColumns && inputNodeIndex < this.inputColumns.length) {
const x = 0.5 / (this.model!.hiddenLayers + 2);
- const y = (inputNodeIndex + 0.5) / this.inputCols;
+ const y = (inputNodeIndex + 0.5) / this.inputColumns.length;
const node = new Node(x, y, this.inputNodeColor);
inputLayer.push(node);
inputNodeIndex += 1;
@@ -62,9 +66,9 @@ export class GraphComponent implements AfterViewInit {
while (layerIndex < this.model!.hiddenLayers + 1) {
const newLayer: Node[] = [];
let nodeIndex = 0;
- while (nodeIndex < this.model!.hiddenLayerNeurons) {
+ while (nodeIndex < this.model!.layers[layerIndex - 1].neurons) {
const x = (layerIndex + 0.5) / (this.model!.hiddenLayers + 2);
- const y = (nodeIndex + 0.5) / this.model!.hiddenLayerNeurons;
+ const y = (nodeIndex + 0.5) / this.model!.layers[layerIndex - 1].neurons;
const node = new Node(x, y, this.nodeColor);
newLayer.push(node);
nodeIndex += 1;
@@ -80,7 +84,7 @@ export class GraphComponent implements AfterViewInit {
}
draw() {
- this.ctx!.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
+ this.ctx.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
let index = 0;
while (index < this.layers!.length - 1) {
@@ -93,29 +97,38 @@ export class GraphComponent implements AfterViewInit {
}
for (let layer of this.layers!) {
- for (let node of layer) {
- this.drawNode(node);
- }
+ layer.forEach((node, index) => {
+ this.drawNode(node, 0.5 / layer.length + 0.5);
+ });
}
}
+ bezierOffset = 5;
+
drawLine(node1: Node, node2: Node) {
- this.ctx!.strokeStyle = this.lineColor;
- this.ctx!.lineWidth = this.lineThickness;
- this.ctx!.beginPath();
- this.ctx!.moveTo(node1.x * this.width, node1.y * this.height);
- this.ctx!.lineTo(node2.x * this.width, node2.y * this.height);
- this.ctx!.stroke();
+ const lineColor: RgbColor = this.lerpColor(this.lineColor1, this.lineColor2, node1.y);
+ this.ctx.strokeStyle = `rgb(${lineColor.R}, ${lineColor.G}, ${lineColor.B})`;
+ this.ctx.lineWidth = this.lineThickness;
+ this.ctx.beginPath();
+ this.ctx.moveTo(node1.x * this.width, node1.y * this.height);
+ //this.ctx.lineTo(node2.x * this.width, node2.y * this.height);
+ const middle = (node1.x + (node2.x - node1.x) / 2) * this.width;
+ this.ctx.bezierCurveTo(
+ middle, node1.y * this.height,
+ middle, node2.y * this.height,
+ node2.x * this.width, node2.y * this.height);
+ this.ctx.stroke();
}
- drawNode(node: Node) {
- this.ctx!.fillStyle = node.color;
- this.ctx!.strokeStyle = this.borderColor;
- this.ctx!.lineWidth = this.lineThickness;
- this.ctx!.beginPath();
- this.ctx!.arc(node.x * this.width, node.y * this.height, this.nodeRadius, 0, 2 * Math.PI);
- this.ctx!.fill();
- this.ctx!.stroke();
+ drawNode(node: Node, sizeMult: number) {
+ const lineColor: RgbColor = this.lerpColor(this.lineColor1, this.lineColor2, node.y);
+ this.ctx.strokeStyle = `rgb(${lineColor.R}, ${lineColor.G}, ${lineColor.B})`;
+ this.ctx.fillStyle = node.color;
+ this.ctx.lineWidth = this.lineThickness;
+ this.ctx.beginPath();
+ this.ctx.arc(node.x * this.width, node.y * this.height, this.nodeRadius * sizeMult, 0, 2 * Math.PI);
+ this.ctx.fill();
+ this.ctx.stroke();
}
width = 200;
@@ -134,6 +147,16 @@ export class GraphComponent implements AfterViewInit {
this.draw();
}
+
+ lerpColor(value1: RgbColor, value2: RgbColor, amount: number): RgbColor {
+ const newColor = new RgbColor(0, 0, 0);
+ amount = amount < 0 ? 0 : amount;
+ amount = amount > 1 ? 1 : amount;
+ newColor.R = value1.R + (value2.R - value1.R) * amount;
+ newColor.G = value1.G + (value2.G - value1.G) * amount;
+ newColor.B = value1.B + (value2.B - value1.B) * amount;
+ return newColor;
+ };
}
class Node {
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.css b/frontend/src/app/_elements/item-dataset/item-dataset.component.css
deleted file mode 100644
index dc851671..00000000
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.css
+++ /dev/null
@@ -1,23 +0,0 @@
-.card{
- margin-top:0;
- padding: 0;
-}
-.p-2{
- margin: 0;
- padding: 0;
-}
-hr{
- margin: 0;
- padding: 0;
-}
-b{
- margin-left: 5px;
- margin-right: 10px;
-}
-th{
- margin: 10px;
- padding: 10px;
-}
-p{
- text-align: justify;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.html b/frontend/src/app/_elements/item-dataset/item-dataset.component.html
deleted file mode 100644
index 11ff61c3..00000000
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<div class="card" style="min-width: 12rem;">
-<div class="card-header d-flex mb-2 justify-content-" style="padding: 0;margin: 0;">
-
- <div class=" p-2 float-left "><b style="color: gray;">Naziv</b></div>
- <div class=" p-2 float-left"><b>{{dataset.name}}</b></div>
-</div>
-<div class="card-body overflow-hidden">
- <b style="color: gray;">Opis</b>
- <hr style="width: 20%;"> <p> {{dataset.description}}</p>
- <hr>
- <div class="d-flex justify-content-center">
- <div class=" p-2" >
- <h4><span class="badge bg-secondary">{{dataset.extension}}</span></h4>
- </div>
- <div class="p-2">
- <span class="material-icons">{{visibleicon}}</span>
- </div>
- <div class="p-2">
- <span class="material-icons">{{accessibleicon}}</span>
- </div>
- </div>
- <hr>
- <div class="col text-center">
-<button (click)=toggleDisplayDiv() class="btn btn-primary btn-sm active " mat-raised-button color="primary" style="margin: 0.5rem;">Kolone</button>
- <div [hidden]="isShowDiv" style="overflow: scroll; overflow-y: hidden;">
- <table class="table table-bordered table-md" >
- <thead>
- <th scope="col" *ngFor="let column of dataset.columnInfo" >{{column.columnName}}</th>
- </thead>
- </table>
- </div>
-</div>
- <table>
- <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{dataset.dateCreated |date}}</td></tr>
- <tr><td><span class="material-icons">edit_calendar</span></td><td><span style="color: grey;"> <b> Poslednja izmena</b></span></td><td>{{dataset.lastUpdated |date}}</td></tr>
- </table>
-
-</div>
-<div class="card-footer">
-
- </div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.ts b/frontend/src/app/_elements/item-dataset/item-dataset.component.ts
deleted file mode 100644
index 44b95310..00000000
--- a/frontend/src/app/_elements/item-dataset/item-dataset.component.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import Dataset from 'src/app/_data/Dataset';
-
-@Component({
- selector: 'app-item-dataset',
- templateUrl: './item-dataset.component.html',
- styleUrls: ['./item-dataset.component.css']
-})
-export class ItemDatasetComponent {
-
- @Input() dataset: Dataset = new Dataset();
- visibleicon='';
- accessibleicon='';
- isShowDiv = true;
- toggleDisplayDiv() {
- this.isShowDiv = !this.isShowDiv;
- }
- constructor() {
- }
- ngOnInit(): void {
- if(this.dataset.isPublic==true)
- {
- this.visibleicon='visibility'
- }
- else
- {
- this.visibleicon='visibility_off';
- }
-
- if(this.dataset.accessibleByLink==true)
- {
- this.accessibleicon='link'
- }
- else
- {
- this.accessibleicon='link_off';
- }
- }
-}
-
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.html b/frontend/src/app/_elements/item-experiment/item-experiment.component.html
deleted file mode 100644
index 51fbfef3..00000000
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<div class="card" style="min-width: 12rem;">
- <div class="card-header">
- Naziv eksperimenta: <b>{{experiment.name}}</b>
- </div>
- <div class="card-body overflow-hidden">
- <p class="card-text">
- Opis: {{experiment.description}}
- </p>
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.ts b/frontend/src/app/_elements/item-experiment/item-experiment.component.ts
deleted file mode 100644
index 31900d35..00000000
--- a/frontend/src/app/_elements/item-experiment/item-experiment.component.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import Experiment from 'src/app/_data/Experiment';
-
-@Component({
- selector: 'app-item-experiment',
- templateUrl: './item-experiment.component.html',
- styleUrls: ['./item-experiment.component.css']
-})
-export class ItemExperimentComponent{
-
- @Input() experiment: Experiment = new Experiment();
-
- constructor() { }
-
-}
diff --git a/frontend/src/app/_elements/item-model/item-model.component.css b/frontend/src/app/_elements/item-model/item-model.component.css
deleted file mode 100644
index 5ea24c72..00000000
--- a/frontend/src/app/_elements/item-model/item-model.component.css
+++ /dev/null
@@ -1,23 +0,0 @@
-.card{
- margin: 0.5rem;
- padding: 0;
-}
-.p-2{
- margin: 0;
- padding: 0;
-}
-hr{
- margin: 0;
- padding: 0;
-}
-b{
- margin-left: 5px;
- margin-right: 10px;
-}
-th{
- margin: 10px;
- padding: 10px;
-}
-p{
- text-align: justify;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-model/item-model.component.html b/frontend/src/app/_elements/item-model/item-model.component.html
deleted file mode 100644
index 447f023e..00000000
--- a/frontend/src/app/_elements/item-model/item-model.component.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<div class="card" style="min-width: 12rem;">
- <div class="card-header d-flex mb-2 justify-content-" style="padding: 0;margin: 0;">
-
- <div class=" p-2 float-left "><b style="color: gray;">Naziv</b></div>
- <div class=" p-2 float-left"><b>{{model.name}}</b></div>
- </div>
- <div class="card-body overflow-hidden">
- <app-graph [model]="model"></app-graph>
- <br>
- <b style="color: gray;">Opis</b><hr style="width: 20%;">
- <p class="card-text">
- {{model.description}}
- </p>
- <hr>
-
- <div>
- <table>
- <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{model.dateCreated |date}}</td></tr>
- <tr><td><span class="material-icons">edit_calendar</span></td><td><span style="color: grey;"> <b> Poslednja izmena</b></span></td><td>{{model.lastUpdated |date}}</td></tr>
- </table>
- </div>
-
- </div>
- <button (click)=toggleDisplayDiv() class="btn btn-default btn-lg " mat-raised-button color="primary" style="margin: 0.5rem;">Parametri</button>
- <div [hidden]="isShowDiv">
- <!-- <table>
- <tr>
- <td><span style="color: grey;"> <b> Nasumično raspoređivanje podataka</b></span></td><td>{{randomOrd}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Podela podataka na trening i test skup</b></span></td><td>{{randomOrd}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Veličina skupa za treniranje</b></span></td><td>{{randomOrd}}</td>
- </tr>
- </table>-->
- <hr>
- <table>
- <tr>
- <td><span style="color: grey;"> <b> Tip problema</b></span></td><td>{{model.type}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Optimizator</b></span></td><td>{{model.optimizer}}</td>
- </tr>
- <tr>
- <td> <span style="color: grey;"> <b> Funkcija gubitka</b></span></td><td>{{model.lossFunction}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Batch size</b></span></td><td>{{model.batchSize}}</td>
- </tr>
- <tr>
- <td><span style="color: grey;"> <b> Broj epoha</b></span></td><td>{{model.epochs}}</td>
- </tr>
-
- </table>
-
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-model/item-model.component.ts b/frontend/src/app/_elements/item-model/item-model.component.ts
deleted file mode 100644
index b837667b..00000000
--- a/frontend/src/app/_elements/item-model/item-model.component.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import Model from 'src/app/_data/Model';
-
-@Component({
- selector: 'app-item-model',
- templateUrl: './item-model.component.html',
- styleUrls: ['./item-model.component.css']
-})
-export class ItemModelComponent implements OnInit {
-
- @Input() model: Model = new Model();
- isShowDiv = true;
- randomOrd='';
-
- toggleDisplayDiv() {
- this.isShowDiv = !this.isShowDiv;
- }
-
- constructor() { }
-
- ngOnInit(): void {
- /*if(this.model.randomOrder)
- {
- this.randomOrd='Da';
- }
- else
- {
- this.randomOrd='Ne';
- }
-*/
- }
-
-}
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.html b/frontend/src/app/_elements/item-predictor/item-predictor.component.html
deleted file mode 100644
index 3199dcc8..00000000
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<div class="card" style="min-width: 12rem;">
- <div class="card-header d-flex mb-2 justify-content-" style="padding: 0;margin: 0;">
-
- <div class=" p-2 float-left "><b style="color: gray;">Prediktor</b></div>
-
- </div>
- <div class="card-body overflow-hidden">
- <b style="color: gray;">Opis</b><hr style="width: 20%;">
- <p class="card-text">
- {{predictor.description}}
- </p>
-
- <b style="color: gray;">Ulazne kolone</b>
- <div style="overflow: scroll; overflow-y: hidden;">
-
- <table class="table table-bordered table-md" >
- <thead>
- <th scope="col" *ngFor="let column of predictor.inputs" >{{column}}</th>
- </thead>
- </table>
- </div>
- <b style="color: gray;">Izlazna kolona: </b><b>{{predictor.output}}</b>
- <hr>
- <div>
- <table>
- <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{predictor.dateCreated |date}}</td></tr>
- </table>
- </div>
- </div>
- <div class="card-footer text-center">
- <button class="btn btn-md col-4" style="background-color:#003459; color:white;"
- (click)="openPredictor();">Iskoristi</button>
-
- </div>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.ts b/frontend/src/app/_elements/item-predictor/item-predictor.component.ts
deleted file mode 100644
index 246032e0..00000000
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Component, Input, OnInit } from '@angular/core';
-import { Router } from '@angular/router';
-import Predictor from 'src/app/_data/Predictor';
-
-@Component({
- selector: 'app-item-predictor',
- templateUrl: './item-predictor.component.html',
- styleUrls: ['./item-predictor.component.css']
-})
-export class ItemPredictorComponent implements OnInit {
-
- @Input() predictor: Predictor = new Predictor();
-
- constructor(private router: Router) { }
-
- ngOnInit(): void {
- }
-
- openPredictor() {
- this.router.navigate(['predict/'+ this.predictor._id]);
- }
-
-}
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.html b/frontend/src/app/_elements/line-chart/line-chart.component.html
deleted file mode 100644
index c8f406f4..00000000
--- a/frontend/src/app/_elements/line-chart/line-chart.component.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<div class="chart-wrapper">
- <canvas id="myChart">
-
- </canvas>
-</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.css b/frontend/src/app/_elements/metric-view/metric-view.component.css
index e69de29b..f91c1ccc 100644
--- a/frontend/src/app/_elements/metric-view/metric-view.component.css
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.css
@@ -0,0 +1,10 @@
+#container{
+ width: 100%;
+ height: 90%;
+ border-radius: 5px;
+ background-color:var(--ns-primary-25);
+ border:1px solid var(--ns-accent);
+}
+#line{
+ background-color:#dfd7d7f0 ;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.html b/frontend/src/app/_elements/metric-view/metric-view.component.html
index e7a4c547..d72bc92b 100644
--- a/frontend/src/app/_elements/metric-view/metric-view.component.html
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.html
@@ -1,5 +1,8 @@
-<div>
- <app-line-chart>
-
- </app-line-chart>
+<div id="container" class="d-flex justify-content-center flex-row w-100">
+ <div id="line" style="width: 100%;height: 100%;background-color:var(--ns-bg-dark-100);">
+ <app-line-chart></app-line-chart>
+ </div>
+ <div style="background-color: var(--ns-bg-dark-100);width: 50%;height: 50%;">
+ <app-scatterchart></app-scatterchart>
+ </div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.ts b/frontend/src/app/_elements/metric-view/metric-view.component.ts
index 9193a0e5..6fd2f320 100644
--- a/frontend/src/app/_elements/metric-view/metric-view.component.ts
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.ts
@@ -1,6 +1,6 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
-import { SignalRService } from 'src/app/_services/signal-r.service';
-import { LineChartComponent } from '../line-chart/line-chart.component';
+import { LineChartComponent } from '../_charts/line-chart/line-chart.component';
+
@Component({
selector: 'app-metric-view',
templateUrl: './metric-view.component.html',
@@ -9,13 +9,13 @@ import { LineChartComponent } from '../line-chart/line-chart.component';
export class MetricViewComponent implements OnInit {
@ViewChild(LineChartComponent) linechartComponent!: LineChartComponent;
- @Input() history!: any[];
-
- constructor(private signalRService: SignalRService) { }
+ constructor() { }
ngOnInit(): void {
}
+ history: any[] = [];
+
update(history: any[]) {
const myAcc: number[] = [];
const myMae: number[] = [];
@@ -28,7 +28,7 @@ export class MetricViewComponent implements OnInit {
myEpochs.push(epoch + 1);
for (let key in metrics) {
let value = metrics[key];
- console.log(key, ':::', value, epoch);
+ //console.log(key, ':::', value, epoch);
if (key === 'accuracy') {
myAcc.push(parseFloat(value));
}
diff --git a/frontend/src/app/_elements/model-load/model-load.component.css b/frontend/src/app/_elements/model-load/model-load.component.css
deleted file mode 100644
index c716f964..00000000
--- a/frontend/src/app/_elements/model-load/model-load.component.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.btnType1 {
- background-color: #003459;
- color: white;
- padding-top: 2vh;
- padding-bottom: 2vh;
-}
-.btnType2 {
- background-color: white;
- color: #003459;
- border-color: #003459;
- padding-top: 2vh;
- padding-bottom: 2vh;
-}
-.selectedModelClass {
- /*border-color: 2px solid #003459;*/
- background-color: lightblue;
-} \ No newline at end of file
diff --git a/frontend/src/app/_elements/model-load/model-load.component.html b/frontend/src/app/_elements/model-load/model-load.component.html
index 1f9852d1..dcb35c21 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.html
+++ b/frontend/src/app/_elements/model-load/model-load.component.html
@@ -1,10 +1,12 @@
<div>
<div class="d-flex flex-row justify-content-center align-items-center mt-3 mb-5">
- <button type="button" id="btnMyModel" class="btn" (click)="viewMyModelsForm()" [ngClass]="{'btnType1': showMyModels, 'btnType2': !showMyModels}">
+ <button type="button" id="btnMyModel" class="btn" (click)="viewMyModelsForm()"
+ [ngClass]="{'btnType1': showMyModels, 'btnType2': !showMyModels}">
Izaberite model iz kolekcije
</button>
<h3 class="mt-3 mx-3">ili</h3>
- <button type="button" id="btnNewModel" class="btn" (click)="viewNewModelForm()" [ngClass]="{'btnType1': !showMyModels, 'btnType2': showMyModels}">
+ <button type="button" id="btnNewModel" class="btn" (click)="viewNewModelForm()"
+ [ngClass]="{'btnType1': !showMyModels, 'btnType2': showMyModels}">
Dodajte novi model
</button>
</div>
@@ -15,7 +17,8 @@
<div *ngIf="showMyModels" class="px-5">
<div class="overflow-auto" style="max-height: 500px;">
<ul class="list-group">
- <li class="list-group-item p-3" *ngFor="let model of myModels|filter:((forExperiment != undefined) ? forExperiment.type : '')" [ngClass]="{'selectedModelClass': this.selectedModel == model}">
+ <li class="list-group-item p-3" *ngFor="let model of myModels|filter:term|filter:(forExperiment ? forExperiment.type : '')"
+ [ngClass]="{'selectedModelClass': this.selectedModel == model}">
<app-item-model name="usersModel" [model]="model" (click)="selectThisModel(model);">
</app-item-model>
</li>
@@ -40,7 +43,7 @@
<textarea class="form-control" name="desc" rows="3" [(ngModel)]="newModel.description"></textarea>
</div>
</div>
-
+
</div>
<h2 class="mt-5 mb-4 mx-5">Parametri treniranja modela:</h2>
<div>
@@ -51,7 +54,8 @@
<label for="type" class="col-form-label">Tip problema: </label>
</div>
<div class="col-2">
- <select id="typeOptions" class="form-select" name="type" [(ngModel)]="newModel.type" (change)="filterOptions()">
+ <select id=typeOptions class="form-select" name="type" [(ngModel)]="newModel.type"
+ (change)="filterOptions()">
<option
*ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)"
[value]="option">
@@ -64,7 +68,10 @@
<label for="hiddenLayers" class="col-form-label">Broj skrivenih slojeva: </label>
</div>
<div class="col-1">
- <input type="number" min="1" class="form-control" name="hiddenLayers" [(ngModel)]="newModel.hiddenLayers" (change)="newModel.hiddenLayerActivationFunctions = [].constructor(newModel.hiddenLayers).fill(newModel.hiddenLayerActivationFunctions[0])" (ngModelChange)="updateGraph()">
+ <input type="number" min="1" class="form-control" name="hiddenLayers"
+ [(ngModel)]="newModel.hiddenLayers"
+ (change)="newModel.hiddenLayerActivationFunctions = [].constructor(newModel.hiddenLayers).fill(newModel.hiddenLayerActivationFunctions[0])"
+ (ngModelChange)="updateGraph()">
</div>
</div>
@@ -75,7 +82,7 @@
<label for="optimizer" class="col-form-label">Optimizacija: </label>
</div>
<div class="col-2">
- <select id="optimizerOptions" class="form-select" name="optimizer" [(ngModel)]="newModel.optimizer">
+ <select id=optimizerOptions class="form-select" name="optimizer" [(ngModel)]="newModel.optimizer">
<option
*ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)"
[value]="option">
@@ -89,7 +96,8 @@
<label for="hiddenLayerNeurons" class="col-form-label">Broj neurona skrivenih slojeva: </label>
</div>
<div class="col-1">
- <input type="number" min="1" class="form-control" name="hiddenLayerNeurons" [(ngModel)]="newModel.hiddenLayerNeurons" (ngModelChange)="updateGraph()">
+ <input type="number" min="1" class="form-control" name="hiddenLayerNeurons"
+ [(ngModel)]="newModel.hiddenLayerNeurons" (ngModelChange)="updateGraph()">
</div>
</div>
@@ -99,7 +107,8 @@
<label for="lossFunction" class="col-form-label">Funkcija troška: </label>
</div>
<div class="col-2">
- <select id="lossFunctionOptions" class="form-select" name="lossFunction" [(ngModel)]="newModel.lossFunction" aria-checked="true">
+ <select id=lossFunctionOptions class="form-select" name="lossFunction"
+ [(ngModel)]="newModel.lossFunction" aria-checked="true">
<option
*ngFor="let option of Object.keys(lossFunction); let optionName of Object.values(lossFunction)"
[value]="option">
@@ -109,21 +118,23 @@
</div>
<div class="col-1"></div>
<div class="col-3">
- <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji:&nbsp;<b>{{newModel.batchSize}}</b><br>(izaberite stepen dvojke)</label>
+ <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label>
</div>
<div class="col-1">
-
- <input type="number" min="0" step="1" max="7" class="form-control" name="batchSizePower" [(ngModel)]="batchSizePower" (click)="updateBatchSize()">
-
+
+ <input type="number" min="0" step="1" max="7" class="form-control" name="batchSizePower" [(ngModel)]="batchSizePower" (click)="updateBatchSize()" >
+ {{newModel.batchSize}}
+
</div>
-
+
<div class="row p-2">
<div class="col-1"></div>
<div class="col-3 m-1">
<label for="epochs" class="col-form-label">Broj epoha: </label>
</div>
<div class="col-1">
- <input type="number" min="1" max="1000" class="form-control" name="epochs" [(ngModel)]="newModel.epochs">
+ <input type="number" min="1" max="1000" class="form-control" name="epochs"
+ [(ngModel)]="newModel.epochs">
</div>
</div>
</div>
@@ -137,7 +148,8 @@
<div class="row p-2" style="align-self: center;">
<div class="col-1"></div>
<div class="col-3">
- <label for="hiddenLayerActivationFunction" class="col-form-label" style="text-align: center;">Funkcija aktivacije<br>skrivenih slojeva:</label>
+ <label for="hiddenLayerActivationFunction" class="col-form-label"
+ style="text-align: center;">Funkcija aktivacije<br>skrivenih slojeva:</label>
</div>
<div class="col-2 mt-2">
<div *ngFor="let item of [].constructor(newModel.hiddenLayers); let i = index">
@@ -145,7 +157,8 @@
<div class="input-group-prepend">
<span class="input-group-text">#{{i+1}}</span>
</div>
- <select [id]="'hiddenLayerActivationFunctionOption_'+i" class="form-select" [(ngModel)]="newModel.hiddenLayerActivationFunctions[i]">
+ <select [id]="'hiddenLayerActivationFunctionOption_'+i" class="form-select"
+ [(ngModel)]="newModel.hiddenLayerActivationFunctions[i]" >
<option
*ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)"
[value]="option">
@@ -157,10 +170,12 @@
</div>
<div class="col-1"></div>
<div class="col-2">
- <label for="outputLayerActivationFunction" class="col-form-label" style="text-align: center;">Funkcija aktivacije<br>izlaznog sloja:</label>
+ <label for="outputLayerActivationFunction" class="col-form-label"
+ style="text-align: center;">Funkcija aktivacije<br>izlaznog sloja:</label>
</div>
<div class="col-2 mt-2">
- <select id="outputLayerActivationFunctionOptions" class="form-select" name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction">
+ <select id=outputLayerActivationFunctionOptions class="form-select"
+ name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction">
<option
*ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)"
[value]="option">
@@ -173,23 +188,26 @@
</div>
</div>
- <!--<div class="form-check form-check-inline overflow-auto m-4" style="width: max-content;">
+ <div class="form-check form-check-inline overflow-auto m-4" style="width: max-content;">
<h3>Izaberite metrike:</h3>
<div id="divMetricsinput" class="mt-2 mx-5">
- <div *ngFor="let option of Object.keys(metrics); let optionName of Object.values(metrics) " class="form-check form-check-inline">
+ <div *ngFor="let option of Object.keys(metrics); let optionName of Object.values(metrics) "
+ class="form-check form-check-inline">
- <input name="cbmetrics" class="form-check-input" type="checkbox" value="{{option}}" id="metrics_{{option}}" style="float: left;" checked>
+ <input name="cbmetrics" class="form-check-input" type="checkbox" value="{{option}}"
+ id="metrics_{{option}}" style="float: left;" checked>
<label class="form-check-label" for="metrics_{{option}}" for="inlineCheckbox2">
{{optionName}}
</label>
</div>
</div>
- </div>-->
-
+ </div>
+
<div class="form-group row mt-3 mb-3">
<div class="col"></div>
- <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="uploadModel();">Sačuvaj
+ <button class="btn btn-lg col-4" style="background-color:#003459; color:white;"
+ (click)="uploadModel();">Sačuvaj
model</button>
<div class="col"></div>
</div>
diff --git a/frontend/src/app/_elements/model-load/model-load.component.ts b/frontend/src/app/_elements/model-load/model-load.component.ts
index fb4b3fd0..dbca3d17 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.ts
+++ b/frontend/src/app/_elements/model-load/model-load.component.ts
@@ -2,9 +2,7 @@ import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from '@angu
import Shared from 'src/app/Shared';
import Experiment from 'src/app/_data/Experiment';
import Model, { ActivationFunction, LossFunction, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType } from 'src/app/_data/Model';
-import { AuthService } from 'src/app/_services/auth.service';
import { ModelsService } from 'src/app/_services/models.service';
-import { SignalRService } from 'src/app/_services/signal-r.service';
import { GraphComponent } from '../graph/graph.component';
@@ -16,7 +14,7 @@ import { GraphComponent } from '../graph/graph.component';
export class ModelLoadComponent implements OnInit {
@ViewChild(GraphComponent) graph!: GraphComponent;
- @Input() forExperiment?: Experiment;
+ @Input() forExperiment?:Experiment;
@Output() selectedModelChangeEvent = new EventEmitter<Model>();
newModel: Model = new Model();
@@ -33,42 +31,24 @@ export class ModelLoadComponent implements OnInit {
shared = Shared;
term: string = "";
+ selectedProblemType: string = '';
selectedMetrics = [];
lossFunction: any = LossFunction;
showMyModels: boolean = true;
- batchSizePower: number = 2;
-
- constructor(private modelsService: ModelsService, private authService: AuthService) {
- //console.log("forExperiment = ", this.forExperiment);
- this.fetchModels();
-
- this.authService.loggedInEvent.subscribe(_ => {
- this.fetchModels();
- })
- }
-
- fetchModels(andSelectWithId: string | null = '') {
- //if (this.forExperiment == undefined) {
+ constructor(private modelsService: ModelsService) {
this.modelsService.getMyModels().subscribe((models) => {
- this.myModels = models.reverse();
- this.selectThisModel(this.myModels.filter(x => x._id == andSelectWithId)[0]);
+ this.myModels = models;
});
- /*}
- else {
- this.modelsService.getMyModelsByType(ProblemType.Regression).subscribe((models) => {
- this.myModels = models;
- //console.log("modeli po tipu: ", this.myModels);
- });
- }*/
}
ngOnInit(): void {
}
-
- updateBatchSize() {
- this.newModel.batchSize = 2 ** this.batchSizePower;
+ batchSizePower:number=1;
+ updateBatchSize()
+ {
+ this.newModel.batchSize=2**this.batchSizePower;
}
updateGraph() {
@@ -92,14 +72,9 @@ export class ModelLoadComponent implements OnInit {
this.newModel.uploaderId = Shared.userId;
this.modelsService.addModel(this.newModel).subscribe((response) => {
- console.log(this.newModel);
- //Shared.openDialog('Model dodat', 'Model je uspešno dodat u bazu.');
-
- Shared.openYesNoDialog("Model dodat", "Model je uspešno dodat u bazu. Da li želite da nastavite treniranje sa dodatim modelom?", () => {
- this.fetchModels(response._id);
- this.showMyModels = true;
- });
- this.fetchModels();
+ Shared.openDialog('Model dodat', 'Model je uspešno dodat u bazu.');
+ // treba da se selektuje nov model u listi modela
+ //this.selectedModel =
}, (error) => {
Shared.openDialog('Greška', 'Model sa unetim nazivom već postoji u Vašoj kolekciji. Promenite naziv modela i nastavite sa kreiranim datasetom.');
});
diff --git a/frontend/src/app/_elements/navbar/navbar.component.css b/frontend/src/app/_elements/navbar/navbar.component.css
index e69de29b..fcfad876 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.css
+++ b/frontend/src/app/_elements/navbar/navbar.component.css
@@ -0,0 +1,8 @@
+.dropdown-item:hover {
+ background-color: var(--ns-primary);
+}
+
+h4 {
+ margin-top: 0.82rem;
+ margin-right: 10px;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/navbar/navbar.component.html b/frontend/src/app/_elements/navbar/navbar.component.html
index 1988b834..105151aa 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.html
+++ b/frontend/src/app/_elements/navbar/navbar.component.html
@@ -1,17 +1,15 @@
-<header class="sticky-top p-3 bg-dark text-white">
+<header class="text-offwhite" style="background-color: #002b49;">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a routerLink="" class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none">
- <img src="../../../assets/svg/logo_no_text.svg" class="bi me-2" width="64" height="40">
+ <img src="../../../assets/images/logo.png" class="bi me-2" width="64" height="64">
+ <h4>Igr<span class="highlight">ann</span>onica</h4>
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
- <li><a routerLink="" class="nav-link px-2" [class]="(currentUrl === '') ? 'text-secondary' : 'text-white'">Početna</a></li>
- <li><a routerLink="experiment" class="nav-link px-2" [class]="(currentUrl === '/experiment') ? 'text-secondary' : 'text-white'">Napravi eksperiment</a>
+ <li><a routerLink="experiment" class="nav-link px-2" [class]="(currentUrl === '/experiment') ? 'text-primary' : 'text-offwhite'">Napravi eksperiment</a>
</li>
- <li><a routerLink="training" class="nav-link px-2" [class]="(currentUrl === '/training') ? 'text-secondary' : 'text-white'">Treniraj model</a>
- </li>
- <li><a routerLink="my-predictors" class="nav-link px-2" [class]="(currentUrl === '/my-predictors') ? 'text-secondary' : 'text-white' + (shared.loggedIn) ? '' : 'disabled'">Predvidi</a>
+ <li><a routerLink="archive" class="nav-link px-2" [class]="(currentUrl === '/archive') ? 'text-primary' : 'text-offwhite'">Arhiva</a>
</li>
</ul>
@@ -19,12 +17,8 @@
<a href="#" class="d-block link-light text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<img [src]="'/assets/profilePictures/'+ shared.photoId +'.png'" alt="mdo" width="32" height="32" class="rounded-circle">
</a>
- <ul class="dropdown-menu text-small" aria-labelledby="dropdownUser1" style="position: absolute; inset: 0px 0px auto auto; margin: 0px; transform: translate(0px, 34px);" data-popper-placement="bottom-end">
- <li><a class="dropdown-item" routerLink="my-datasets">Moji izvori podataka</a></li>
- <li><a class="dropdown-item" routerLink="my-models">Moji modeli</a></li>
- <li><a class="dropdown-item" routerLink="my-predictors">Moji prediktori</a></li>
+ <ul class="dropdown-menu text-small ns-bg-dark-100" aria-labelledby="dropdownUser1" style="position: absolute; inset: 0px 0px auto auto; margin: 0px; transform: translate(0px, 34px);" data-popper-placement="bottom-end">
<li><a class="dropdown-item" routerLink="profile">Moj profil</a></li>
- <li><a class="dropdown-item" routerLink="settings" disabled>Podešavanja</a></li>
<li>
<hr class="dropdown-divider">
</li>
@@ -32,10 +26,10 @@
</ul>
</div>
<div *ngIf="!shared.loggedIn" class="dropdown text-end">
- <button type="button" mat-raised-button color="primary" class="mx-2" data-bs-toggle="modal" data-bs-target="#modalForLogin">
+ <button type="button" mat-raised-button color="accent" class="mx-2" data-bs-toggle="modal" data-bs-target="#modalForLogin">
Prijavi se
</button>
- <button type="button" mat-raised-button color="primary" data-bs-toggle="modal" data-bs-target="#modalForRegister">
+ <button type="button" mat-raised-button color="accent" data-bs-toggle="modal" data-bs-target="#modalForRegister">
Registruj se
</button>
</div>
diff --git a/frontend/src/app/_elements/navbar/navbar.component.ts b/frontend/src/app/_elements/navbar/navbar.component.ts
index d5d1744f..e2551f7a 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.ts
+++ b/frontend/src/app/_elements/navbar/navbar.component.ts
@@ -1,30 +1,26 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Location } from '@angular/common';
import { AuthService } from '../../_services/auth.service';
import shared from 'src/app/Shared';
import { UserInfoService } from 'src/app/_services/user-info.service';
import { MatDialog } from '@angular/material/dialog';
-import { SignalRService } from 'src/app/_services/signal-r.service';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
- styleUrls: ['./navbar.component.css']
+ styleUrls: ['./navbar.component.css'],
+ encapsulation: ViewEncapsulation.Emulated
})
export class NavbarComponent implements OnInit {
currentUrl: string;
shared = shared;
- constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService, private matDialog: MatDialog, private signalRService: SignalRService) {
+ constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService, private matDialog: MatDialog) {
shared.dialog = matDialog;
this.currentUrl = this.location.path();
this.location.onUrlChange(() => {
this.currentUrl = this.location.path();
- });
-
- this.auth.loggedInEvent.subscribe(_ => {
- this.signalRService.startConnection();
})
}
@@ -39,6 +35,5 @@ export class NavbarComponent implements OnInit {
logOut() {
this.auth.logOut();
- this.signalRService.stopConnection();
}
}
diff --git a/frontend/src/app/_elements/notifications/notifications.component.ts b/frontend/src/app/_elements/notifications/notifications.component.ts
index f324662a..5716c1e6 100644
--- a/frontend/src/app/_elements/notifications/notifications.component.ts
+++ b/frontend/src/app/_elements/notifications/notifications.component.ts
@@ -25,6 +25,7 @@ export class NotificationsComponent implements OnInit {
const existingNotification = this.notifications.find(x => x.id === mId)
const progress = ((currentEpoch + 1) / totalEpochs);
//console.log("Ukupno epoha", totalEpochs, "Trenutna epoha:", currentEpoch);
+ //console.log("stat:", stat);
if (!existingNotification)
this.notifications.push(new Notification(`Treniranje modela: ${mName}`, mId, progress, true));
else {
diff --git a/frontend/src/app/_elements/playlist/playlist.component.css b/frontend/src/app/_elements/playlist/playlist.component.css
new file mode 100644
index 00000000..353a094c
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.css
@@ -0,0 +1,61 @@
+.ns-wrapper {
+ width: 100%;
+ max-width: 800px;
+ max-height: 600px;
+ height: 100%;
+ transform-style: preserve-3d;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+}
+
+.ns-cards {
+ position: relative;
+ width: 300%;
+ height: 25rem;
+ margin-bottom: 20px;
+}
+
+.ns-card {
+ position: absolute;
+ width: 60%;
+ height: 100%;
+ left: 0;
+ right: 0;
+ margin: auto;
+ transition: transform 0.4s ease;
+ cursor: pointer;
+}
+
+.ns-card:hover {
+ opacity: 1 !important;
+}
+
+input[type=radio] {
+ display: none;
+}
+
+#item-1:checked~.ns-cards #view-item-3,
+#item-2:checked~.ns-cards #view-item-1,
+#item-3:checked~.ns-cards #view-item-2 {
+ transform: translatex(-40%) scale(0.8);
+ opacity: 0.5;
+ z-index: 0;
+}
+
+#item-1:checked~.ns-cards #view-item-2,
+#item-2:checked~.ns-cards #view-item-3,
+#item-3:checked~.ns-cards #view-item-1 {
+ transform: translatex(40%) scale(0.8);
+ opacity: 0.5;
+ z-index: 0;
+}
+
+#item-1:checked~.ns-cards #view-item-1,
+#item-2:checked~.ns-cards #view-item-2,
+#item-3:checked~.ns-cards #view-item-3 {
+ transform: translatex(0) scale(1);
+ opacity: 1;
+ z-index: 1;
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/playlist/playlist.component.html b/frontend/src/app/_elements/playlist/playlist.component.html
new file mode 100644
index 00000000..b82de163
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.html
@@ -0,0 +1,19 @@
+<div class="ns-wrapper" *ngIf="tableDatas && tableDatas.length==3">
+ <input type="radio" name="slider" id="item-1" value="0" [(ngModel)]="selectedId">
+ <input type="radio" name="slider" id="item-2" value="1" [(ngModel)]="selectedId">
+ <input type="radio" name="slider" id="item-3" value="2" [(ngModel)]="selectedId">
+ <div class="ns-cards">
+ <label class="ns-card ns-bg-dark-100 ns-border-primary rounded" for="item-1" id="view-item-1">
+ <app-datatable [tableData]="tableDatas[0]"></app-datatable>
+ </label>
+ <label class="ns-card ns-bg-dark-100 ns-border-primary rounded" for="item-2" id="view-item-2">
+ <app-datatable [tableData]="tableDatas[1]"></app-datatable>
+ </label>
+ <label class="ns-card ns-bg-dark-100 ns-border-primary rounded" for="item-3" id="view-item-3">
+ <app-datatable [tableData]="tableDatas[2]"></app-datatable>
+ </label>
+ </div>
+ <div class="ns-infobox text-offwhite">
+ <h2>{{datasets[getIndex(selectedId)].name}}</h2>
+ </div>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/playlist/playlist.component.spec.ts b/frontend/src/app/_elements/playlist/playlist.component.spec.ts
new file mode 100644
index 00000000..0afe8041
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PlaylistComponent } from './playlist.component';
+
+describe('PlaylistComponent', () => {
+ let component: PlaylistComponent;
+ let fixture: ComponentFixture<PlaylistComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PlaylistComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PlaylistComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/playlist/playlist.component.ts b/frontend/src/app/_elements/playlist/playlist.component.ts
new file mode 100644
index 00000000..7f476178
--- /dev/null
+++ b/frontend/src/app/_elements/playlist/playlist.component.ts
@@ -0,0 +1,49 @@
+import { Component, Input, OnInit } from '@angular/core';
+import Dataset from 'src/app/_data/Dataset';
+import { TableData } from 'src/app/_elements/datatable/datatable.component';
+import { CsvParseService } from 'src/app/_services/csv-parse.service';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+
+@Component({
+ selector: 'app-playlist',
+ templateUrl: './playlist.component.html',
+ styleUrls: ['./playlist.component.css']
+})
+export class PlaylistComponent implements OnInit {
+
+ selectedId: string = "0";
+
+ @Input() datasets!: Dataset[];
+
+ tableDatas?: TableData[];
+
+ constructor(private datasetService: DatasetsService, private csv: CsvParseService) {
+
+ }
+
+ getIndex(str: string) {
+ return parseInt(str);
+ }
+
+ ngOnInit(): void {
+ this.tableDatas = [];
+
+ this.datasets.forEach((dataset, index) => {
+ if (index < 3) {
+ this.datasetService.getDatasetFile(dataset.fileId).subscribe((file: string | undefined) => {
+ if (file) {
+ const tableData = new TableData();
+ tableData.hasInput = true;
+ tableData.loaded = true;
+ tableData.numRows = dataset.rowCount;
+ tableData.numCols = dataset.columnInfo.length;
+ tableData.data = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter.toString() == "") ? "," : dataset.delimiter);
+ this.tableDatas!.push(tableData);
+ }
+ });
+ }
+ });
+
+ //console.log(this.tableDatas);
+ }
+}
diff --git a/frontend/src/app/_elements/reactive-background/reactive-background.component.html b/frontend/src/app/_elements/reactive-background/reactive-background.component.html
index 756952fb..c63dd6ac 100644
--- a/frontend/src/app/_elements/reactive-background/reactive-background.component.html
+++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.html
@@ -1 +1 @@
-<canvas id="bgCanvas" width="200" height="200" style="position: fixed; z-index: -1;"></canvas> \ No newline at end of file
+<canvas #bgCanvas width="200" height="200" style="position: fixed; z-index: -1;"></canvas> \ No newline at end of file
diff --git a/frontend/src/app/_elements/reactive-background/reactive-background.component.ts b/frontend/src/app/_elements/reactive-background/reactive-background.component.ts
index 980e3e6f..1a6157e3 100644
--- a/frontend/src/app/_elements/reactive-background/reactive-background.component.ts
+++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.ts
@@ -1,14 +1,19 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { CookieService } from 'ngx-cookie-service';
+import Shared from 'src/app/Shared';
@Component({
selector: 'app-reactive-background',
templateUrl: './reactive-background.component.html',
styleUrls: ['./reactive-background.component.css']
})
-export class ReactiveBackgroundComponent implements OnInit {
+export class ReactiveBackgroundComponent implements AfterViewInit {
+
+ @ViewChild('bgCanvas') canvasRef!: ElementRef;
@Input() numPoints: number = 450;
@Input() speed: number = 0.001; // 0-1
+ @Input() scrollSpeed: number = 1;
@Input() maxSize: number = 6;
@Input() minDistance: number = 0.07; //0-1
@@ -19,30 +24,43 @@ export class ReactiveBackgroundComponent implements OnInit {
@Input() pointColor: string = '#ffffff';
@Input() cursorLineColor: string = '#ff0000';
+ @Input() animate: boolean = true;
+ @Input() fill: number = 1.0;
+
+ private fleeSpeed = 0.005;
+
private points: Point[] = [];
private width = 200;
private height = 200;
private ratio = 1;
- private canvas?: HTMLCanvasElement;
- private ctx?: CanvasRenderingContext2D;
+ private canvas!: HTMLCanvasElement;
+ private ctx!: CanvasRenderingContext2D;
private time: number = 0;
- constructor() { }
+ constructor(private cookie: CookieService) { }
private mouseX = 0;
private mouseY = 0;
- ngOnInit(): void {
-
+ ngAfterViewInit(): void {
document.addEventListener('mousemove', (e) => {
this.mouseX = e.clientX / this.width;
this.mouseY = e.clientY / this.height;
})
- this.canvas = (<HTMLCanvasElement>document.getElementById('bgCanvas'));
+ document.addEventListener('mouseleave', _ => {
+ this.mouseX = -1;
+ this.mouseY = -1;
+ })
+
+ document.addEventListener('scroll', (e) => {
+ this.scrollBackground(e);
+ })
+
+ this.canvas = (<HTMLCanvasElement>this.canvasRef.nativeElement);
const ctx = this.canvas.getContext('2d');
if (ctx) {
this.ctx = ctx;
@@ -64,41 +82,77 @@ export class ReactiveBackgroundComponent implements OnInit {
this.resize();
setInterval(() => {
+ if (this.cookie.check('animateBackground')) {
+ this.animate = this.cookie.get('animateBackground') == 'true';
+ }
+ if (this.cookie.check('backgroundFill')) {
+ this.fill = parseFloat(this.cookie.get('backgroundFill'));
+ }
+
this.drawBackground();
}, 1000 / 60);
+
+ Shared.bgScroll.subscribe((amount) => {
+ this.scrollBackgroundFromSharedEvent(amount);
+ })
+ }
+
+ private lastScrollY: number = 0;
+
+ scrollBackgroundFromSharedEvent(amount: number) {
+ const scrolledAmount = amount - this.lastScrollY;
+ this.scrollPoints(scrolledAmount);
+ this.lastScrollY = amount;
+ }
+
+ scrollBackground(e: Event) {
+ const scrolledAmount = window.scrollY - this.lastScrollY;
+ this.scrollPoints(scrolledAmount);
+ this.lastScrollY = window.scrollY;
+ }
+
+ scrollPoints(amount: number) {
+ this.points.forEach((point, index) => {
+ if (index > this.numPoints * this.fill) return;
+ point.y = point.y - (amount / this.height) * this.scrollSpeed;
+ this.keepPointWithinBounds(point);
+ })
}
drawBackground() {
if (!this.ctx || !this.canvas) return;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.ctx.fillStyle = this.bgColor;
- this.ctx.fillRect(0, 0, this.width, this.height);
+ //this.ctx.fillStyle = this.bgColor;
+ //this.ctx.fillRect(0, 0, this.width, this.height);
this.points.forEach((point, index) => {
+ if (index > this.numPoints * this.fill) return;
+
this.drawLines(point, index);
this.drawPoint(point);
- this.updatePoint(point);
+
+ if (this.animate)
+ this.updatePoint(point);
});
//this.drawPoint(new Point(this.mouseX, this.mouseY, 12, 0));
-
- this.time += 1;
}
drawLines(p: Point, index: number) {
let i = index + 1;
- while (i < this.points.length) {
+ while (i < this.points.length * this.fill) {
const otherPoint = this.points[i];
const dist = this.distance(p.x, p.y, otherPoint.x, otherPoint.y);
if (dist < this.minDistance) {
const h = HEX[Math.round((1 - dist / this.minDistance) * 16)]
- this.ctx!.strokeStyle = this.lineColor + h + h;
- this.ctx!.beginPath();
- this.ctx!.moveTo(p.x * this.width, p.y * this.height);
- this.ctx!.lineTo(otherPoint.x * this.width, otherPoint.y * this.height);
- this.ctx!.stroke();
+ this.ctx.strokeStyle = this.lineColor + h;
+ this.ctx.lineWidth = this.maxSize / 2;
+ this.ctx.beginPath();
+ this.ctx.moveTo(p.x * this.width, p.y * this.height);
+ this.ctx.lineTo(otherPoint.x * this.width, otherPoint.y * this.height);
+ this.ctx.stroke();
}
i++;
@@ -106,10 +160,10 @@ export class ReactiveBackgroundComponent implements OnInit {
}
drawPoint(p: Point) {
- this.ctx!.fillStyle = this.pointColor;
- this.ctx!.beginPath();
- this.ctx!.arc(p.x * this.width, p.y * this.height, p.size, 0, 2 * Math.PI);
- this.ctx!.fill();
+ this.ctx.fillStyle = this.pointColor;
+ this.ctx.beginPath();
+ this.ctx.arc(p.x * this.width, p.y * this.height, p.size * this.screenDepth(p.x), 0, 2 * Math.PI);
+ this.ctx.fill();
}
resize() {
@@ -122,40 +176,66 @@ export class ReactiveBackgroundComponent implements OnInit {
this.canvas.height = this.height;
}
+ if (this.cookie.check('animateBackground')) {
+ this.animate = this.cookie.get('animateBackground') == 'true';
+ }
+ if (this.cookie.check('backgroundFill')) {
+ this.fill = parseFloat(this.cookie.get('backgroundFill'));
+ }
+
this.drawBackground();
}
updatePoint(p: Point) {
+ const mx = this.mouseX;
+ const my = this.mouseY;
+ const distToCursor = this.distance(p.x, p.y, mx, my);
+
+ if (distToCursor < this.cursorDistance) {
+
+ const t = (distToCursor / this.cursorDistance);
+ p.x -= ((mx - p.x) / distToCursor) * this.speed * (1 + t * 2);
+ p.y -= ((my - p.y) / distToCursor) * this.speed * (1 + t * 2);
+
+ p.direction = this.lerp(p.direction, Math.atan2(my - p.y, mx - p.x) * 180 / Math.PI, t);
+
+ const grd = this.ctx.createLinearGradient(p.x * this.width, p.y * this.height, mx * this.width, my * this.height);
+ const alpha = HEX[Math.round(p.size / this.maxSize * (HEX.length - 1))];
+ grd.addColorStop(0, this.cursorLineColor + alpha);
+ grd.addColorStop(0.5, this.cursorLineColor + '00');
+ this.ctx.strokeStyle = grd;
+ this.ctx.beginPath();
+ this.ctx.moveTo(p.x * this.width, p.y * this.height);
+ this.ctx.lineTo(mx * this.width, my * this.height);
+ this.ctx.stroke();
+ }
+
const vx = Math.sin(p.direction);
const vy = Math.cos(p.direction);
p.x = p.x + vx * this.speed;
p.y = p.y + vy * this.speed;
- const mx = this.mouseX;
- const my = this.mouseY;
- const distToCursor = this.distance(p.x, p.y, mx, my);
- if (distToCursor < this.cursorDistance) {
+ this.keepPointWithinBounds(p);
+ }
- p.x -= ((mx - p.x) / distToCursor) / 500;
- p.y -= ((my - p.y) / distToCursor) / 500;
-
- const grd = this.ctx!.createLinearGradient(p.x * this.width, p.y * this.height, mx * this.width, my * this.height);
- grd.addColorStop(0, this.cursorLineColor + 'ff');
- grd.addColorStop(1, this.cursorLineColor + '00');
- this.ctx!.strokeStyle = grd;
- this.ctx!.beginPath();
- this.ctx!.moveTo(p.x * this.width, p.y * this.height);
- this.ctx!.lineTo(mx * this.width, my * this.height);
- this.ctx!.stroke();
- }
+ lerp(start: number, end: number, amt: number) {
+ return (1 - amt) * start + amt * end
+ }
- p.x %= 1;
- p.y %= 1;
+ keepPointWithinBounds(p: Point) {
+ p.x = p.x % 1.0;
+ p.y = p.y % 1.0;
+ p.x = ((1 - Math.sign(p.x)) / 2) + p.x;
+ p.y = ((1 - Math.sign(p.y)) / 2) + p.y;
}
distance(x1: number, y1: number, x2: number, y2: number): number {
- return Math.sqrt(((x2 - x1) ** 2) + ((y2 / this.ratio - y1 / this.ratio) ** 2));
+ return Math.sqrt(((x2 - x1) ** 2) + ((y2 / this.ratio - y1 / this.ratio) ** 2) / this.screenDepth(x1)) * this.ratio;
+ }
+
+ screenDepth(x: number): number {
+ return (1.5 - Math.sin(x * Math.PI));
}
}
@@ -168,4 +248,4 @@ class Point {
) { }
}
-const HEX = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']; \ No newline at end of file
+const HEX = ['00', '11', '22', '33', '44', '55', '66', '77', '88', '99', 'aa', 'bb', 'cc', 'dd', 'ee', 'ff']; \ No newline at end of file