aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSonja Galovic <galovicsonja@gmail.com>2022-06-06 04:21:57 +0200
committerSonja Galovic <galovicsonja@gmail.com>2022-06-06 04:21:57 +0200
commita5dedce9016f75de00954f02cdaf865a66454220 (patch)
tree9f8c4dd85c367fdd63af75dfb34b1a7a9e1ba5db
parent2b5200bc9d3c55ca2d3f44c7dda23568125540ff (diff)
parent67151f6cc5fcb9a66fc08a181eb8e1a6acaca733 (diff)
Merge branch 'redesign' of http://gitlab.pmf.kg.ac.rs/igrannonica/neuronstellar into redesign
-rw-r--r--backend/api/api/Controllers/DatasetController.cs32
-rw-r--r--backend/api/api/Controllers/ModelController.cs32
-rw-r--r--backend/api/api/Interfaces/IDatasetService.cs3
-rw-r--r--backend/api/api/Interfaces/IModelService.cs3
-rw-r--r--backend/api/api/Services/DatasetService.cs20
-rw-r--r--backend/api/api/Services/FileService.cs2
-rw-r--r--backend/api/api/Services/ModelService.cs16
-rw-r--r--docs/SpecifikacijaDizajnaSoftvera.docxbin0 -> 5793452 bytes
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.css3
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.html10
-rw-r--r--frontend/src/app/_elements/folder/folder.component.html10
-rw-r--r--frontend/src/app/_elements/folder/folder.component.ts36
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.html8
-rw-r--r--frontend/src/app/_elements/form-dataset/form-dataset.component.ts1
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.html5
-rw-r--r--frontend/src/app/_elements/form-model/form-model.component.ts17
-rw-r--r--frontend/src/app/_elements/graph/graph.component.css11
-rw-r--r--frontend/src/app/_elements/graph/graph.component.html12
-rw-r--r--frontend/src/app/_elements/graph/graph.component.ts5
-rw-r--r--frontend/src/app/_modals/share-dialog/share-dialog.component.css0
-rw-r--r--frontend/src/app/_modals/share-dialog/share-dialog.component.html14
-rw-r--r--frontend/src/app/_modals/share-dialog/share-dialog.component.spec.ts25
-rw-r--r--frontend/src/app/_modals/share-dialog/share-dialog.component.ts70
-rw-r--r--frontend/src/app/_pages/page-dataset/page-dataset.component.css0
-rw-r--r--frontend/src/app/_pages/page-dataset/page-dataset.component.html4
-rw-r--r--frontend/src/app/_pages/page-dataset/page-dataset.component.spec.ts25
-rw-r--r--frontend/src/app/_pages/page-dataset/page-dataset.component.ts45
-rw-r--r--frontend/src/app/_pages/page-model/page-model.component.css0
-rw-r--r--frontend/src/app/_pages/page-model/page-model.component.html4
-rw-r--r--frontend/src/app/_pages/page-model/page-model.component.spec.ts25
-rw-r--r--frontend/src/app/_pages/page-model/page-model.component.ts41
-rw-r--r--frontend/src/app/_services/datasets.service.ts8
-rw-r--r--frontend/src/app/_services/models.service.ts8
-rw-r--r--frontend/src/app/app-routing.module.ts4
-rw-r--r--frontend/src/app/app.module.ts8
-rw-r--r--frontend/src/styles/layout.css12
36 files changed, 479 insertions, 40 deletions
diff --git a/backend/api/api/Controllers/DatasetController.cs b/backend/api/api/Controllers/DatasetController.cs
index c93ac9db..9a3c3d86 100644
--- a/backend/api/api/Controllers/DatasetController.cs
+++ b/backend/api/api/Controllers/DatasetController.cs
@@ -282,5 +282,37 @@ namespace api.Controllers
return Ok($"Dataset with ID = {id} deleted");
}
+
+ [HttpPut("UpdateAccessibleByLink/{datasetId}")]
+ [Authorize(Roles = "User")]
+ public ActionResult UpdateAccessibleByLink(string datasetId, [FromBody] bool accessibleByLink)
+ {
+ string uploaderId = getUserId();
+
+ Dataset dataset = _datasetService.GetOneDataset(datasetId);
+
+ if (uploaderId != dataset.uploaderId)
+ return Unauthorized();
+
+ _datasetService.UpdateAccessibleByLink(datasetId, accessibleByLink);
+
+ return Ok(dataset.accessibleByLink);
+ }
+
+ [HttpPut("UpdateIsPublic/{datasetId}")]
+ [Authorize(Roles = "User")]
+ public ActionResult UpdateIsPublic(string datasetId, [FromBody] bool isPublic)
+ {
+ string uploaderId = getUserId();
+
+ Dataset dataset = _datasetService.GetOneDataset(datasetId);
+
+ if (uploaderId != dataset.uploaderId)
+ return Unauthorized();
+
+ _datasetService.UpdateIsPublic(datasetId, isPublic);
+
+ return Ok(dataset.isPublic);
+ }
}
} \ No newline at end of file
diff --git a/backend/api/api/Controllers/ModelController.cs b/backend/api/api/Controllers/ModelController.cs
index c574de28..c7dfe89c 100644
--- a/backend/api/api/Controllers/ModelController.cs
+++ b/backend/api/api/Controllers/ModelController.cs
@@ -312,6 +312,38 @@ namespace api.Controllers
}
+ [HttpPut("UpdateAccessibleByLink/{modelId}")]
+ [Authorize(Roles = "User")]
+ public ActionResult UpdateAccessibleByLink(string modelId, [FromBody] bool accessibleByLink)
+ {
+ string uploaderId = getUserId();
+
+ Model model = _modelService.GetOneModel(modelId);
+
+ if (uploaderId != model.uploaderId)
+ return Unauthorized();
+
+ _modelService.UpdateAccessibleByLink(modelId, accessibleByLink);
+
+ return Ok(model.accessibleByLink);
+ }
+
+ [HttpPut("UpdateIsPublic/{modelId}")]
+ [Authorize(Roles = "User")]
+ public ActionResult UpdateIsPublic(string modelId, [FromBody] bool isPublic)
+ {
+ string uploaderId = getUserId();
+
+ Model model = _modelService.GetOneModel(modelId);
+
+ if (uploaderId != model.uploaderId)
+ return Unauthorized();
+
+ _modelService.UpdateIsPublic(modelId, isPublic);
+
+ return Ok(model.isPublic);
+ }
+
}
public class TrainModelObject
diff --git a/backend/api/api/Interfaces/IDatasetService.cs b/backend/api/api/Interfaces/IDatasetService.cs
index 2f7d0010..5a91c82b 100644
--- a/backend/api/api/Interfaces/IDatasetService.cs
+++ b/backend/api/api/Interfaces/IDatasetService.cs
@@ -19,5 +19,8 @@ namespace api.Services
public void Update(Dataset dataset);
string GetDatasetId(string fileId);
//bool CheckDb();
+
+ public void UpdateAccessibleByLink(string datasetId, bool accessibleByLink);
+ public void UpdateIsPublic(string datasetId, bool isPublic);
}
}
diff --git a/backend/api/api/Interfaces/IModelService.cs b/backend/api/api/Interfaces/IModelService.cs
index 41cd279c..949c7278 100644
--- a/backend/api/api/Interfaces/IModelService.cs
+++ b/backend/api/api/Interfaces/IModelService.cs
@@ -19,6 +19,9 @@ namespace api.Services
void Delete(string userId, string name);
bool CheckHyperparameters(int inputNeurons, int hiddenLayerNeurons, int hiddenLayers, int outputNeurons);
bool CheckDb();
+
+ public void UpdateAccessibleByLink(string modelId, bool accessibleByLink);
+ public void UpdateIsPublic(string modelId, bool isPublic);
}
}
diff --git a/backend/api/api/Services/DatasetService.cs b/backend/api/api/Services/DatasetService.cs
index 0b84721e..cd27f275 100644
--- a/backend/api/api/Services/DatasetService.cs
+++ b/backend/api/api/Services/DatasetService.cs
@@ -75,7 +75,7 @@ namespace api.Services
public Dataset GetOneDataset(string userId, string id)
{
- return _dataset.Find(dataset => dataset.uploaderId == userId && dataset._id == id && dataset.isPreProcess).FirstOrDefault();
+ return _dataset.Find(dataset => (dataset.uploaderId == userId || dataset.isPublic || dataset.accessibleByLink) && dataset._id == id && dataset.isPreProcess).FirstOrDefault();
}
public Dataset GetOneDatasetN(string userId, string name)
{
@@ -104,6 +104,22 @@ namespace api.Services
return dataset._id;
}
-
+
+ public void UpdateAccessibleByLink(string datasetId, bool accessibleByLink)
+ {
+ Dataset dataset = _dataset.Find(dataset => dataset._id == datasetId).FirstOrDefault();
+ dataset.accessibleByLink = accessibleByLink;
+
+ _dataset.ReplaceOne(dataset => dataset._id == datasetId, dataset);
+ }
+
+ public void UpdateIsPublic(string datasetId, bool isPublic)
+ {
+ Dataset dataset = _dataset.Find(dataset => dataset._id == datasetId).FirstOrDefault();
+ dataset.isPublic = isPublic;
+
+ _dataset.ReplaceOne(dataset => dataset._id == datasetId, dataset);
+ }
+
}
}
diff --git a/backend/api/api/Services/FileService.cs b/backend/api/api/Services/FileService.cs
index 6ef74ca1..e5b7945f 100644
--- a/backend/api/api/Services/FileService.cs
+++ b/backend/api/api/Services/FileService.cs
@@ -28,7 +28,7 @@ namespace api.Services
public string GetFilePath(string id, string uploaderId)
{
FileModel file;
- if (_dataset.Find(x=>x.fileId==id && x.isPublic==true).FirstOrDefault()!=null)
+ if (_dataset.Find(x=>x.fileId==id && (x.isPublic==true ||x.accessibleByLink==true)).FirstOrDefault()!=null)
file = _file.Find(x => x._id == id).FirstOrDefault();
else
file = _file.Find(x => x._id == id && x.uploaderId == uploaderId).FirstOrDefault();
diff --git a/backend/api/api/Services/ModelService.cs b/backend/api/api/Services/ModelService.cs
index 71db6340..1c690ca7 100644
--- a/backend/api/api/Services/ModelService.cs
+++ b/backend/api/api/Services/ModelService.cs
@@ -106,5 +106,21 @@ namespace api.Services
return true;
}
+
+ public void UpdateAccessibleByLink(string modelId, bool accessibleByLink)
+ {
+ Model model = _model.Find(model => model._id == modelId).FirstOrDefault();
+ model.accessibleByLink = accessibleByLink;
+
+ _model.ReplaceOne(model => model._id == modelId, model);
+ }
+
+ public void UpdateIsPublic(string modelId, bool isPublic)
+ {
+ Model model = _model.Find(model => model._id == modelId).FirstOrDefault();
+ model.isPublic = isPublic;
+
+ _model.ReplaceOne(model => model._id == modelId, model);
+ }
}
}
diff --git a/docs/SpecifikacijaDizajnaSoftvera.docx b/docs/SpecifikacijaDizajnaSoftvera.docx
new file mode 100644
index 00000000..dfc5345b
--- /dev/null
+++ b/docs/SpecifikacijaDizajnaSoftvera.docx
Binary files differ
diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
index 862a86e1..60b341d0 100644
--- a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.css
@@ -22,7 +22,8 @@ canvas {
}
.hide {
- display: none;
+ display: none !important;
+ background-color: red;
}
.dl-button {
diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
index cc1c0121..505dd50b 100644
--- a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
@@ -1,5 +1,5 @@
<div #wrapper class="position-relative" style="width:100%;height:95%; border: 1px solid var(--ns-accent); background-color: var(--ns-bg-dark-100); border-radius: 15px;">
- <div class="d-flex flex-column align-items-stretch" [ngClass]="{'hide':experiment.type != ProblemType.Regression}" style="width: 100%; height: 90%;">
+ <div class="d-flex flex-column align-items-stretch" [ngClass]="{'hide':experiment.type !== ProblemType.Regression}" style="width: 100%; height: 90%;">
<div class="canvas-container">
<canvas #myChartmae>
</canvas>
@@ -9,18 +9,16 @@
</canvas>
</div>
</div>
- <div class="ns-row" [ngClass]="{'hide':experiment.type == ProblemType.Regression}">
- <div class="ns-col">
+ <div class="d-flex flex-column align-items-stretch" [ngClass]="{'hide':experiment.type === ProblemType.Regression}" style="width: 100%; height: 90%;">
+ <div class="canvas-container">
<canvas #myChartloss>
</canvas>
</div>
- <div class="ns-col">
+ <div class="canvas-container">
<canvas #myChartacc>
</canvas>
</div>
</div>
-
-
<div class="bottom d-flex flex-row" style="justify-content: space-between; align-items: center;">
<h3 class="mt-5 mx-2"><span *ngIf="modelName.length > 0 && predictor">Model treniran za konfiguraciju: {{modelName}}</span><span *ngIf="modelName.length > 0 && !predictor">Model se trenira za konfiguraciju: {{modelName}}</span></h3>
<button *ngIf="predictor" class="mt-5 mx-2 btn-clear dl-button d-flex flex-row" (click)="downloadFile();" style="display: inline-block;" matTooltip="Preuzmi H5" matTooltipPosition="above">
diff --git a/frontend/src/app/_elements/folder/folder.component.html b/frontend/src/app/_elements/folder/folder.component.html
index 5d5e7822..e77e05a3 100644
--- a/frontend/src/app/_elements/folder/folder.component.html
+++ b/frontend/src/app/_elements/folder/folder.component.html
@@ -76,6 +76,9 @@
{{file.lastUpdated | date}}
</div>
<div class="mx-2 hover-show" *ngIf="selectedTab !== TabType.PublicDatasets && selectedTab !== TabType.PublicModels">
+ <button *ngIf="selectedTab==TabType.MyDatasets || selectedTab==TabType.MyModels" class="btn-clear file-button" (click)="shareFile(file,$event)" style="display: inline-block;" matTooltip="Podeli" matTooltipPosition="above">
+ <mat-icon>share</mat-icon>
+ </button>
<button *ngIf="selectedTab==TabType.MyDatasets" class="btn-clear file-button" (click)="downloadFile(file,$event)" style="display: inline-block;" matTooltip="Preuzmi" matTooltipPosition="above">
<mat-icon>download</mat-icon>
</button>
@@ -122,12 +125,15 @@
<div [ngSwitch]="newFileSelected" *ngIf="!listView">
<div class="file-bottom-buttons" *ngIf="selectedTab != TabType.NewFile">
<div class="file-bottom-buttons-helper">
- <button *ngIf="this.selectedFile && selectedTab == TabType.File && privacy != Privacy.Public" class="btn-clear file-button" (click)="deleteFile(this.selectedFile, $event)" matTooltip="Obriši" matTooltipPosition="above">
+ <button *ngIf="this.selectedFile && selectedTab == TabType.File && privacy != Privacy.Public" class="btn-clear file-button" (click)="deleteFile(selectedFile, $event)" matTooltip="Obriši" matTooltipPosition="above">
<mat-icon>delete</mat-icon>
</button>
- <button *ngIf="this.selectedFile && selectedTab==TabType.File && FolderType.Dataset==this.type" class="btn-clear file-button" (click)="downloadFile(this.selectedFile,$event)" style="display: inline-block;" matTooltip="Preuzmi" matTooltipPosition="above">
+ <button *ngIf="this.selectedFile && selectedTab==TabType.File && FolderType.Dataset==this.type" class="btn-clear file-button" (click)="downloadFile(selectedFile,$event)" style="display: inline-block;" matTooltip="Preuzmi" matTooltipPosition="above">
<mat-icon>download</mat-icon>
</button>
+ <button *ngIf="this.selectedFile && selectedTab == TabType.File" class="btn-clear file-button" (click)="shareFile(selectedFile,$event)" style="display: inline-block;" matTooltip="Podeli" matTooltipPosition="above">
+ <mat-icon>share</mat-icon>
+ </button>
</div>
<!-- <button class="btn-clear file-button">
<mat-icon>zoom_out_map</mat-icon>
diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts
index 6eccb61b..5fa20859 100644
--- a/frontend/src/app/_elements/folder/folder.component.ts
+++ b/frontend/src/app/_elements/folder/folder.component.ts
@@ -15,6 +15,8 @@ import { ActivatedRoute, Router } from '@angular/router';
import Predictor from 'src/app/_data/Predictor';
import FileSaver from 'file-saver';
import isEqual from 'lodash.isequal';
+import { ShareDialogComponent } from 'src/app/_modals/share-dialog/share-dialog.component';
+import { MatDialog } from '@angular/material/dialog';
@Component({
selector: 'app-folder',
@@ -50,7 +52,7 @@ export class FolderComponent implements AfterViewInit {
searchTerm: string = '';
- constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService, private signalRService: SignalRService, private router: Router, private route: ActivatedRoute) {
+ constructor(private datasetsService: DatasetsService, private experimentsService: ExperimentsService, private modelsService: ModelsService, private predictorsService: PredictorsService, private signalRService: SignalRService, private router: Router, private route: ActivatedRoute, public dialog: MatDialog) {
this.tabsToShow.forEach(tab => this.folders[tab] = []);
}
@@ -104,6 +106,18 @@ export class FolderComponent implements AfterViewInit {
}
selectFile(file?: FolderFile) {
+ if (this.privacy == Privacy.Public) {
+ if (file) {
+ if (this.type == FolderType.Dataset) {
+ this.router.navigate(['dataset', file?._id]);
+ } else if (this.type == FolderType.Model) {
+ this.router.navigate(['model', file?._id]);
+ }
+ }
+
+ }
+
+
this.formDataset.resetPagging();
this.selectedFile = file;
this.updateLastFileData(file);
@@ -154,6 +168,22 @@ export class FolderComponent implements AfterViewInit {
this.okPressed.emit();
}
+ fileToShare: FolderFile | undefined = undefined;
+
+ shareFile(file: FolderFile, event: Event) {
+ event.stopPropagation();
+
+ this.fileToShare = file;
+
+ const dialogRef = this.dialog.open(ShareDialogComponent, {
+ width: '550px',
+ data: { file: this.fileToShare, fileType: this.type }
+ });
+ dialogRef.afterClosed().subscribe(experiment => {
+ this.refreshFiles();
+ });
+ }
+
_initialized: boolean = false;
refreshFiles(selectedDatasetId: string | null = null, selectedModelId: string | null = null) {
@@ -547,9 +577,9 @@ export class FolderComponent implements AfterViewInit {
selectTab(tab: TabType) {
if (tab == TabType.NewFile) {
this.selectNewFile();
- } else if (tab == TabType.File) {
+ } /*else if (tab == TabType.File) {
this.selectFile(this.selectedFile);
- }
+ }*/
this.listView = this.getListView(tab);
this.type = this.getFolderType(tab);
this.privacy = this.getPrivacy(tab);
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.html b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
index 2a956128..b518f15c 100644
--- a/frontend/src/app/_elements/form-dataset/form-dataset.component.html
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.html
@@ -2,7 +2,7 @@
<div class="topBar">
<div class="kolona mb-3">
<div class="fileButton">
- <button type="button" mat-raised-button (click)="fileInput.click()" [disabled]="dataset._id != ''" >
+ <button type="button" mat-raised-button (click)="fileInput.click()" [disabled]="dataset._id != '' || disableAll">
<span *ngIf="!firstInput && dataset._id == '' ">Dodaj izvor podataka</span>
<span *ngIf="firstInput && dataset._id == '' ">{{filename}}</span>
<span *ngIf="dataset._id != '' ">Fajl je odabran</span>
@@ -14,7 +14,7 @@
<div role="group">
<mat-form-field class="example-full-width" appearance="fill">
<mat-label>Naziv</mat-label>
- <input type="text" matInput value="{{dataset?.name}}" [(ngModel)]="dataset.name" (input)="editEvent.emit()">
+ <input type="text" matInput value="{{dataset?.name}}" [(ngModel)]="dataset.name" (input)="editEvent.emit()" [readonly]="disableAll">
<mat-error *ngIf="nameFormControl.hasError('required')">
@@ -26,7 +26,7 @@
<div class="kolona">
<mat-form-field appearance="fill">
<mat-label>Delimiter</mat-label>
- <mat-select id="delimiterOptions" [(ngModel)]="dataset.delimiter" (selectionChange)="update(); editEvent.emit();" value=",">
+ <mat-select id="delimiterOptions" [(ngModel)]="dataset.delimiter" (selectionChange)="update(); editEvent.emit();" value="," [disabled]="disableAll">
<mat-option *ngFor="let option of delimiterOptions" [value]="option">
{{ option }}
</mat-option>
@@ -42,7 +42,7 @@
<i class="material-icons-outlined icon-display" [ngClass]="{'hidden': tableData.hasInput}">file_upload</i>
- <input class="file" id="file-upload" [disabled]="dataset._id != ''" (change)="changeListener($event)" (valueChange)="dataset.isPreProcess = false; editEvent.emit()" #fileInput type="file" accept=".csv">
+ <input class="file" id="file-upload" [disabled]="dataset._id != '' || disableAll" (change)="changeListener($event)" (valueChange)="dataset.isPreProcess = false; editEvent.emit()" #fileInput type="file" accept=".csv">
<div class="datatable">
<div [ngClass]="{'hidden': (!existingFlag)}" class="text-center">
diff --git a/frontend/src/app/_elements/form-dataset/form-dataset.component.ts b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
index 3df76aa5..3eb6fe39 100644
--- a/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
+++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts
@@ -13,6 +13,7 @@ import { FormControl, Validators } from '@angular/forms';
styleUrls: ['./form-dataset.component.css']
})
export class FormDatasetComponent {
+ @Input() disableAll: boolean = false;
@ViewChild(DatatableComponent) datatable!: DatatableComponent;
diff --git a/frontend/src/app/_elements/form-model/form-model.component.html b/frontend/src/app/_elements/form-model/form-model.component.html
index 0d770fc1..1f0208fa 100644
--- a/frontend/src/app/_elements/form-model/form-model.component.html
+++ b/frontend/src/app/_elements/form-model/form-model.component.html
@@ -1,4 +1,4 @@
-<div *ngIf="newModel">
+<div *ngIf="newModel!=undefined">
<div id="container">
<div class="ns-row">
@@ -122,7 +122,8 @@
<div class="ns-col">
<!-- {{forExperiment._columnsSelected}} -->
- <app-graph [model]="newModel" [inputColumns]="getInputColumns()"></app-graph>
+
+ <app-graph [model]="newModel" [inputColumns]="getInputColumns()" [outputColumn]="(this.forExperiment!=undefined)?this.forExperiment.outputColumn:''"></app-graph>
</div>
</div>
</div>
diff --git a/frontend/src/app/_elements/form-model/form-model.component.ts b/frontend/src/app/_elements/form-model/form-model.component.ts
index 7c84d2ba..7831f573 100644
--- a/frontend/src/app/_elements/form-model/form-model.component.ts
+++ b/frontend/src/app/_elements/form-model/form-model.component.ts
@@ -12,6 +12,8 @@ import { MatSliderChange } from '@angular/material/slider';
styleUrls: ['./form-model.component.css']
})
export class FormModelComponent implements AfterViewInit {
+ @Input() disableAll: boolean = false;
+
@ViewChild(GraphComponent) graph!: GraphComponent;
@Input() forExperiment!: Experiment;
@Output() selectedModelChangeEvent = new EventEmitter<Model>();
@@ -25,11 +27,7 @@ export class FormModelComponent implements AfterViewInit {
@Output() editEvent = new EventEmitter();
ngAfterViewInit(): void {
- this.lossFunction = this.lossFunctions[this.forProblemType][0];
- this.outputLayerActivationFunction = this.outputLayerActivationFunctions[this.forProblemType][0];
-
- this.newModel.lossFunction = this.lossFunction;
- this.newModel.outputLayerActivationFunction = this.outputLayerActivationFunction;
+
}
selectFormControl = new FormControl('', Validators.required);
@@ -80,10 +78,17 @@ export class FormModelComponent implements AfterViewInit {
loadModel(model: Model) {
this.newModel = model;
this.forProblemType = model.type;
+ this.lossFunction = this.lossFunctions[this.forProblemType][0];
+ this.outputLayerActivationFunction = this.outputLayerActivationFunctions[this.forProblemType][0];
+
+ this.newModel.lossFunction = this.lossFunction;
+ this.newModel.outputLayerActivationFunction = this.outputLayerActivationFunction;
+ this.updateGraph();
}
updateGraph() {
- this.graph.update();
+ if(this.newModel)
+ this.graph.update();
}
removeLayer() {
diff --git a/frontend/src/app/_elements/graph/graph.component.css b/frontend/src/app/_elements/graph/graph.component.css
index 456a8df1..f8604125 100644
--- a/frontend/src/app/_elements/graph/graph.component.css
+++ b/frontend/src/app/_elements/graph/graph.component.css
@@ -10,11 +10,14 @@
/*border: 1px solid red;*/
transform: translate(-50%, -50%);
}
-
-.node-text:not(.inputs) {
+.output{
+ font-weight: bold;
+ color: var(--offwhite);
+}
+.node-text:not(.output) {
color: transparent;
}
-.node-text:not(.inputs):hover {
+.node-text:not(.output):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 411e8b53..2ba42eba 100644
--- a/frontend/src/app/_elements/graph/graph.component.html
+++ b/frontend/src/app/_elements/graph/graph.component.html
@@ -1,8 +1,10 @@
<div #graphWrapper class="position-relative" style="height: 14rem; width: 100%;">
- <!-- <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> -->
+ <div *ngIf="outputColumn!='' && inputColumns!=[]">
+ <ng-container *ngFor="let layer of layers; let i = index">
+ <div [ngClass]="{'inputs': i==0,'output':i==layers.length-1}" class="node-text" *ngFor="let node of layer; let j = index" [style.left.%]="node.x * 100" [style.top.%]="(i!=layers.length-1)?node.y * 100:node.y*70">
+ {{ i == 0 ? (inputColumns && inputColumns.length >= j ? inputColumns[j] : 'nepoznato') : (i > 0 && i
+ < layers.length - 1 ? model!.layers[i-1].activationFunction : (i==layers.length - 1 ? " "+outputColumn : '')) }} </div>
+ </ng-container>
+ </div>
<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 04be9f04..f14e54e7 100644
--- a/frontend/src/app/_elements/graph/graph.component.ts
+++ b/frontend/src/app/_elements/graph/graph.component.ts
@@ -28,7 +28,8 @@ export class GraphComponent implements AfterViewInit {
@Input() outputNodeColor: string = '#dfd7d7';
private ctx!: CanvasRenderingContext2D;
- @Input() inputColumns?: string[];
+ @Input() inputColumns?: string[]=[];
+ @Input() outputColumn?:string="";
constructor() { }
@@ -81,7 +82,7 @@ export class GraphComponent implements AfterViewInit {
this.layers.push([new Node(outX, outY, this.outputNodeColor)])
this.draw();
}
-
+
draw() {
this.ctx.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
diff --git a/frontend/src/app/_modals/share-dialog/share-dialog.component.css b/frontend/src/app/_modals/share-dialog/share-dialog.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_modals/share-dialog/share-dialog.component.css
diff --git a/frontend/src/app/_modals/share-dialog/share-dialog.component.html b/frontend/src/app/_modals/share-dialog/share-dialog.component.html
new file mode 100644
index 00000000..32584197
--- /dev/null
+++ b/frontend/src/app/_modals/share-dialog/share-dialog.component.html
@@ -0,0 +1,14 @@
+<h1 mat-dialog-title class="text-center">Podeli {{data.fileType == 0 ? 'izvor podataka' : 'konfiguraciju neuronske mreže'}}</h1>
+<div mat-dialog-content class="mt-5 mb-3 mx-1">
+ <form>
+ <mat-checkbox class="m-2" [(ngModel)]="data.file.isPublic" [ngModelOptions]="{standalone: true}" (change)="publicChanged()">Svi mogu da vide </mat-checkbox>
+ <mat-checkbox class="m-2" [(ngModel)]="data.file.accessibleByLink" [ngModelOptions]="{standalone: true}" (change)="linkChanged()">Osobe sa linkom mogu da vide</mat-checkbox>
+ <div class="input-group my-3">
+ <input type="text" [value]="link" class="form-control" placeholder="" aria-label="Copy link button addon" aria-describedby="button-copy">
+ <button class="btn btn-outline-primary" type="button" id="button-copy" (click)="copy()" [disabled]="!data.file.accessibleByLink"><div class="mt-1"><mat-icon *ngIf="data.file.accessibleByLink">link</mat-icon><mat-icon *ngIf="!data.file.accessibleByLink">link_off</mat-icon></div></button>
+ </div>
+ </form>
+</div>
+<div mat-dialog-actions class="justify-content-center">
+ <button mat-stroked-button (click)="close()">Gotovo</button>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_modals/share-dialog/share-dialog.component.spec.ts b/frontend/src/app/_modals/share-dialog/share-dialog.component.spec.ts
new file mode 100644
index 00000000..51bd4c9a
--- /dev/null
+++ b/frontend/src/app/_modals/share-dialog/share-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ShareDialogComponent } from './share-dialog.component';
+
+describe('ShareDialogComponent', () => {
+ let component: ShareDialogComponent;
+ let fixture: ComponentFixture<ShareDialogComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ ShareDialogComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ShareDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_modals/share-dialog/share-dialog.component.ts b/frontend/src/app/_modals/share-dialog/share-dialog.component.ts
new file mode 100644
index 00000000..2331cd8b
--- /dev/null
+++ b/frontend/src/app/_modals/share-dialog/share-dialog.component.ts
@@ -0,0 +1,70 @@
+import { Component, Inject, Input, OnInit } from '@angular/core';
+import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+import Dataset from 'src/app/_data/Dataset';
+import { FolderType } from 'src/app/_data/FolderFile';
+import Model from 'src/app/_data/Model';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+import { ModelsService } from 'src/app/_services/models.service';
+
+interface DialogData {
+ file: (Dataset | Model);
+ fileType: FolderType;
+}
+
+@Component({
+ selector: 'app-share-dialog',
+ templateUrl: './share-dialog.component.html',
+ styleUrls: ['./share-dialog.component.css']
+})
+export class ShareDialogComponent implements OnInit {
+ constructor(public dialogRef: MatDialogRef<ShareDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: DialogData, private modelsService: ModelsService, private datasetsService: DatasetsService) {
+
+ }
+
+ link: string = '';
+
+ ngOnInit(): void {
+ let link = window.location.origin;
+ if (this.data.fileType == FolderType.Dataset) {
+ link += '/dataset/';
+ } else if (this.data.fileType == FolderType.Model) {
+ link += '/model/';
+ }
+ link += this.data.file._id;
+ this.link = link;
+ }
+
+ close() {
+ this.dialogRef.close();
+ }
+
+ copy() {
+ navigator.clipboard.writeText(this.link);
+ }
+
+ publicChanged() {
+ if (this.data.fileType == FolderType.Dataset) {
+ this.datasetsService.updateDatasetIsPublic(this.data.file._id, this.data.file.isPublic).subscribe(() => {
+ });
+ } else if (this.data.fileType == FolderType.Model) {
+ this.modelsService.updateModelIsPublic(this.data.file._id, this.data.file.isPublic).subscribe(() => {
+ });
+ }
+
+ if (this.data.file.isPublic) {
+ this.data.file.accessibleByLink = true;
+ }
+ }
+
+ linkChanged() {
+ if (this.data.fileType == FolderType.Dataset) {
+ this.datasetsService.updateDatasetAccessibleByLink(this.data.file._id, this.data.file.accessibleByLink).subscribe(() => {
+ });
+ } else if (this.data.fileType == FolderType.Model) {
+ this.modelsService.updateModelAccessibleByLink(this.data.file._id, this.data.file.accessibleByLink).subscribe(() => {
+ });
+ }
+ }
+
+ FolderType = FolderType;
+}
diff --git a/frontend/src/app/_pages/page-dataset/page-dataset.component.css b/frontend/src/app/_pages/page-dataset/page-dataset.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_pages/page-dataset/page-dataset.component.css
diff --git a/frontend/src/app/_pages/page-dataset/page-dataset.component.html b/frontend/src/app/_pages/page-dataset/page-dataset.component.html
new file mode 100644
index 00000000..2357a656
--- /dev/null
+++ b/frontend/src/app/_pages/page-dataset/page-dataset.component.html
@@ -0,0 +1,4 @@
+<div class="force-centered" style="color: var(--offwhite);">
+ <app-form-dataset [disableAll]="true"></app-form-dataset>
+ <button mat-raised-button class="m-3 p-2" (click)="import()">Uvezi</button>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/page-dataset/page-dataset.component.spec.ts b/frontend/src/app/_pages/page-dataset/page-dataset.component.spec.ts
new file mode 100644
index 00000000..2c961cd5
--- /dev/null
+++ b/frontend/src/app/_pages/page-dataset/page-dataset.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageDatasetComponent } from './page-dataset.component';
+
+describe('PageDatasetComponent', () => {
+ let component: PageDatasetComponent;
+ let fixture: ComponentFixture<PageDatasetComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageDatasetComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageDatasetComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_pages/page-dataset/page-dataset.component.ts b/frontend/src/app/_pages/page-dataset/page-dataset.component.ts
new file mode 100644
index 00000000..b6d0c2a4
--- /dev/null
+++ b/frontend/src/app/_pages/page-dataset/page-dataset.component.ts
@@ -0,0 +1,45 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import Shared from 'src/app/Shared';
+import Dataset from 'src/app/_data/Dataset';
+import { FormDatasetComponent } from 'src/app/_elements/form-dataset/form-dataset.component';
+import { DatasetsService } from 'src/app/_services/datasets.service';
+
+@Component({
+ selector: 'app-page-dataset',
+ templateUrl: './page-dataset.component.html',
+ styleUrls: ['./page-dataset.component.css']
+})
+export class PageDatasetComponent implements OnInit {
+
+ @ViewChild(FormDatasetComponent) formDataset!: FormDatasetComponent;
+
+ constructor(private route: ActivatedRoute, private router: Router, private datasetsService: DatasetsService) { }
+
+ ngOnInit(): void {
+ this.route.queryParams.subscribe(params => {
+ let id = this.route.snapshot.paramMap.get("id");
+ if (id) {
+ this.datasetsService.getDatasetById(id).subscribe((dataset) => {
+ this.formDataset.dataset = dataset;
+ this.formDataset.loadExisting();
+ });
+ } else {
+ this.router.navigate(['']);
+ }
+ });
+ }
+
+ import() {
+ this.formDataset.dataset._id = "";
+ this.formDataset.dataset.isPreProcess = true;
+ this.formDataset.dataset.isPublic = false;
+ this.datasetsService.stealDataset(this.formDataset.dataset).subscribe((response) => {
+ Shared.openDialog("Obaveštenje", "Uspešno ste dodali javni izvor podataka u vašu kolekciju.");
+ }, (error: any) => {
+ if (error.error == "Dataset with this name already exists") {
+ Shared.openDialog("Obaveštenje", "Izvor podataka sa ovim imenom postoji u vašoj kolekciji.");
+ }
+ });
+ }
+}
diff --git a/frontend/src/app/_pages/page-model/page-model.component.css b/frontend/src/app/_pages/page-model/page-model.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_pages/page-model/page-model.component.css
diff --git a/frontend/src/app/_pages/page-model/page-model.component.html b/frontend/src/app/_pages/page-model/page-model.component.html
new file mode 100644
index 00000000..e494542f
--- /dev/null
+++ b/frontend/src/app/_pages/page-model/page-model.component.html
@@ -0,0 +1,4 @@
+<div class="force-centered" style="color: var(--offwhite);">
+ <app-form-model [disableAll]="true"></app-form-model>
+ <button mat-raised-button class="m-3 p-2" (click)="import()">Uvezi</button>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/page-model/page-model.component.spec.ts b/frontend/src/app/_pages/page-model/page-model.component.spec.ts
new file mode 100644
index 00000000..e235de5c
--- /dev/null
+++ b/frontend/src/app/_pages/page-model/page-model.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageModelComponent } from './page-model.component';
+
+describe('PageModelComponent', () => {
+ let component: PageModelComponent;
+ let fixture: ComponentFixture<PageModelComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageModelComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageModelComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_pages/page-model/page-model.component.ts b/frontend/src/app/_pages/page-model/page-model.component.ts
new file mode 100644
index 00000000..f988ef0d
--- /dev/null
+++ b/frontend/src/app/_pages/page-model/page-model.component.ts
@@ -0,0 +1,41 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import Shared from 'src/app/Shared';
+import Model from 'src/app/_data/Model';
+import { FormModelComponent } from 'src/app/_elements/form-model/form-model.component';
+import { ModelsService } from 'src/app/_services/models.service';
+
+@Component({
+ selector: 'app-page-model',
+ templateUrl: './page-model.component.html',
+ styleUrls: ['./page-model.component.css']
+})
+export class PageModelComponent implements OnInit {
+
+ @ViewChild(FormModelComponent) formModel!: FormModelComponent;
+
+ constructor(private route: ActivatedRoute, private router: Router, private modelsService: ModelsService) { }
+
+ ngOnInit(): void {
+ this.route.queryParams.subscribe(params => {
+ let id = this.route.snapshot.paramMap.get("id");
+ if (id) {
+ this.modelsService.getModelById(id).subscribe((model) => {
+ this.formModel.newModel = model;
+ });
+ } else {
+ this.router.navigate(['']);
+ }
+ });
+ }
+
+ import() {
+ this.formModel.newModel._id = "";
+ this.formModel.newModel.isPublic = false;
+ this.modelsService.stealModel(this.formModel.newModel).subscribe((response) => {
+ Shared.openDialog("Obaveštenje", "Uspešno ste dodali javnu konfiguraciju neuronske mreže u vašu kolekciju.");
+ }, (error: any) => {
+ Shared.openDialog("Obaveštenje", "Konfiguracija neuronske mreže sa ovim imenom postoji u vašoj kolekciji.");
+ });
+ }
+}
diff --git a/frontend/src/app/_services/datasets.service.ts b/frontend/src/app/_services/datasets.service.ts
index 42fa5e55..11838b1d 100644
--- a/frontend/src/app/_services/datasets.service.ts
+++ b/frontend/src/app/_services/datasets.service.ts
@@ -70,4 +70,12 @@ export class DatasetsService {
downloadFile(id: string): Observable<Blob> {
return this.http.get(`${Configuration.settings.apiURL}/file/Download?id=` + id, { headers: this.authService.authHeader(), responseType: 'blob' });
}
+
+ updateDatasetAccessibleByLink(id: string, acessibleByLink: boolean) {
+ return this.http.put(`${Configuration.settings.apiURL}/dataset/updateAccessibleByLink/` + id, acessibleByLink, { headers: this.authService.authHeader(), responseType: 'text' });
+ }
+
+ updateDatasetIsPublic(id: string, isPublic: boolean) {
+ return this.http.put(`${Configuration.settings.apiURL}/dataset/updateIsPublic/` + id, isPublic, { headers: this.authService.authHeader(), responseType: 'text' });
+ }
}
diff --git a/frontend/src/app/_services/models.service.ts b/frontend/src/app/_services/models.service.ts
index 016fa9bc..398c7813 100644
--- a/frontend/src/app/_services/models.service.ts
+++ b/frontend/src/app/_services/models.service.ts
@@ -51,4 +51,12 @@ export class ModelsService {
return this.http.get<Model>(`${Configuration.settings.apiURL}/model/byid/${modelId}`, { headers: this.authService.authHeader() });
}
+ updateModelAccessibleByLink(id: string, acessibleByLink: boolean) {
+ return this.http.put(`${Configuration.settings.apiURL}/model/updateAccessibleByLink/` + id, acessibleByLink, { headers: this.authService.authHeader(), responseType: 'text' });
+ }
+
+ updateModelIsPublic(id: string, isPublic: boolean) {
+ return this.http.put(`${Configuration.settings.apiURL}/model/updateIsPublic/` + id, isPublic, { headers: this.authService.authHeader(), responseType: 'text' });
+ }
+
}
diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts
index 507089a8..fa53d5f0 100644
--- a/frontend/src/app/app-routing.module.ts
+++ b/frontend/src/app/app-routing.module.ts
@@ -9,6 +9,8 @@ import { ExperimentComponent } from './_pages/experiment/experiment.component';
import { ArchiveComponent } from './_pages/archive/archive.component';
import { ColumnTableComponent } from './_elements/column-table/column-table.component';
import { TestComponent } from './_pages/test/test.component';
+import { PageDatasetComponent } from './_pages/page-dataset/page-dataset.component';
+import { PageModelComponent } from './_pages/page-model/page-model.component';
const routes: Routes = [
{ path: '', component: HomeComponent, data: { title: 'Početna strana' } },
@@ -19,6 +21,8 @@ const routes: Routes = [
{ path: 'profile', component: ProfileComponent, canActivate: [AuthGuardService], data: { title: 'Profil' } },
{ path: 'playground', component: PlaygroundComponent, data: { title: 'Zabava' } },
{ path: 'test', component: TestComponent, data: { title: 'Test' } },
+ { path: 'dataset/:id', component: PageDatasetComponent, data: { title: 'Izvor podataka' } },
+ { path: 'model/:id', component: PageModelComponent, data: { title: 'Konfiguracija neuronske mreže' } },
{ path: '**', redirectTo: '' }
];
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index e2823761..21b09318 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -54,6 +54,9 @@ import { HeatmapComponent } from './_elements/_charts/heatmap/heatmap.component'
import { HeatMapAllModule } from '@syncfusion/ej2-angular-heatmap';
import { MetricViewComponent } from './_elements/metric-view/metric-view.component';
import { SpinnerComponent } from './_elements/spinner/spinner.component';
+import { PageDatasetComponent } from './_pages/page-dataset/page-dataset.component';
+import { PageModelComponent } from './_pages/page-model/page-model.component';
+import { ShareDialogComponent } from './_modals/share-dialog/share-dialog.component';
export function initializeApp(appConfig: Configuration) {
return () => appConfig.load();
@@ -95,7 +98,10 @@ export function initializeApp(appConfig: Configuration) {
LineChartComponent,
SaveExperimentDialogComponent,
SpinnerComponent,
- UpdateExperimentDialogComponent
+ UpdateExperimentDialogComponent,
+ PageDatasetComponent,
+ PageModelComponent,
+ ShareDialogComponent
],
imports: [
BrowserModule,
diff --git a/frontend/src/styles/layout.css b/frontend/src/styles/layout.css
index 07c0bf34..790d2ca7 100644
--- a/frontend/src/styles/layout.css
+++ b/frontend/src/styles/layout.css
@@ -66,4 +66,14 @@ body {
.break-2 {
display: none;
}
-}*/ \ No newline at end of file
+}*/
+
+.force-centered {
+ width: 80%;
+ height: 80%;
+ margin: auto;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+} \ No newline at end of file