From e67d39d44bdb7d582964cbd643098b38dddd9c94 Mon Sep 17 00:00:00 2001 From: Sonja Galovic Date: Wed, 18 May 2022 21:55:36 +0200 Subject: Radjeno na otvaranju prediktora na eksperiment strani njegovim izborom u kolekciji. Dodata ruta i fja - poziv beku za prediktor. --- frontend/src/app/_services/models.service.ts | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'frontend/src/app/_services/models.service.ts') diff --git a/frontend/src/app/_services/models.service.ts b/frontend/src/app/_services/models.service.ts index fc888556..7c315491 100644 --- a/frontend/src/app/_services/models.service.ts +++ b/frontend/src/app/_services/models.service.ts @@ -58,4 +58,8 @@ export class ModelsService { return this.http.get(`${Configuration.settings.apiURL}/model/publicmodels`, { headers: this.authService.authHeader() }); } + getModelById(modelId: string): Observable { + return this.http.get(`${Configuration.settings.apiURL}/model/byid/${modelId}`, { headers: this.authService.authHeader() }); + } + } -- cgit v1.2.3 From c1f507071e65903a6efda7285fff096bb7b8a7e5 Mon Sep 17 00:00:00 2001 From: Ivan Ljubisavljevic Date: Thu, 19 May 2022 00:30:50 +0200 Subject: Sredjeno dodavanje public modela u svoje. Takodje sredjeno refreshovanje liste modela i datasetovanja prilikom brisanja. Dodati dijalozi za brisanje i dodavanje --- backend/api/api/Controllers/DatasetController.cs | 19 ++-------- backend/api/api/Controllers/ModelController.cs | 40 ++++++++++++++++++++++ backend/api/api/Models/Model.cs | 6 ---- backend/api/api/Models/Predictor.cs | 25 +------------- backend/api/api/Services/DatasetService.cs | 12 +------ backend/api/api/Services/FillAnEmptyDb.cs | 13 +++---- .../src/app/_elements/folder/folder.component.ts | 32 +++++++++++------ frontend/src/app/_services/models.service.ts | 4 +++ 8 files changed, 76 insertions(+), 75 deletions(-) (limited to 'frontend/src/app/_services/models.service.ts') diff --git a/backend/api/api/Controllers/DatasetController.cs b/backend/api/api/Controllers/DatasetController.cs index e4741412..6fb139bd 100644 --- a/backend/api/api/Controllers/DatasetController.cs +++ b/backend/api/api/Controllers/DatasetController.cs @@ -196,7 +196,7 @@ namespace api.Controllers string ext = ".csv"; - //nesto + //Check Directory if (!Directory.Exists(folderPath)) @@ -279,19 +279,4 @@ namespace api.Controllers } } -} - -/* -{ - "_id": "", - "name": "name", - "description": "description", - "header" : ["ag","rt"], - "fileId" : "652", - "extension": "csb", - "isPublic" : true, - "accessibleByLink": true, - "dateCreated": "dateCreated", - "lastUpdated" : "proba12" -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/backend/api/api/Controllers/ModelController.cs b/backend/api/api/Controllers/ModelController.cs index 39eb7830..be30ae6f 100644 --- a/backend/api/api/Controllers/ModelController.cs +++ b/backend/api/api/Controllers/ModelController.cs @@ -211,6 +211,8 @@ namespace api.Controllers return BadRequest("Bad parameters!");*/ model.uploaderId = getUserId(); + model.dateCreated = DateTime.Now; + model.lastUpdated = DateTime.Now; var existingModel = _modelService.GetOneModel(model.uploaderId, model.name); @@ -232,6 +234,44 @@ namespace api.Controllers } } + // POST api//stealModel + [HttpPost("stealModel")] + [Authorize(Roles = "User,Guest")] + public ActionResult StealModel([FromBody] Model model)//, bool overwrite) + { + bool overwrite = false; + //username="" ako je GUEST + //Experiment e = _experimentService.Get(model.experimentId); umesto 1 ide e.inputColumns.Length TODO!!!!!!!!!!!!!!!!! + //model.inputNeurons = e.inputColumns.Length; + /*if (_modelService.CheckHyperparameters(1, model.hiddenLayerNeurons, model.hiddenLayers, model.outputNeurons) == false) + return BadRequest("Bad parameters!");*/ + + model.uploaderId = getUserId(); + model._id = ""; + model.dateCreated = DateTime.Now; + model.lastUpdated = DateTime.Now; + model.isPublic = false; + + var existingModel = _modelService.GetOneModel(model.uploaderId, model.name); + + + if (existingModel != null && !overwrite && model.validationSize < 1 && model.validationSize > 0) + return NotFound($"Model already exisits or validation size is not between 0-1"); + else + { + //_modelService.Create(model); + //return Ok(); + if (existingModel == null) + _modelService.Create(model); + else + { + _modelService.Replace(model); + } + + return CreatedAtAction(nameof(Get), new { id = model._id }, model); + } + } + // PUT api//{name} [HttpPut("{name}")] [Authorize(Roles = "User,Guest")] diff --git a/backend/api/api/Models/Model.cs b/backend/api/api/Models/Model.cs index a807316f..bbbf201e 100644 --- a/backend/api/api/Models/Model.cs +++ b/backend/api/api/Models/Model.cs @@ -14,18 +14,14 @@ namespace api.Models public string name { get; set; } public string description { get; set; } - //datetime public DateTime dateCreated { get; set; } public DateTime lastUpdated { get; set; } - //proveriti id - //public string experimentId { get; set; } //Neural net training public string type { get; set; } public string optimizer { get; set; } public string lossFunction { get; set; } - //public int inputNeurons { get; set; } public int hiddenLayers { get; set; } public string batchSize { get; set; } public string learningRate { get; set; } @@ -36,8 +32,6 @@ namespace api.Models public string[] metrics { get; set; } public int epochs { get; set; } - //public bool isTrained { get; set; } - //public NullValues[] nullValues { get; set; } public bool randomOrder { get; set; } public bool randomTestSet { get; set; } public float randomTestSetDistribution { get; set; } diff --git a/backend/api/api/Models/Predictor.cs b/backend/api/api/Models/Predictor.cs index 530257b2..bfe95a0f 100644 --- a/backend/api/api/Models/Predictor.cs +++ b/backend/api/api/Models/Predictor.cs @@ -29,27 +29,4 @@ namespace api.Models } -} - -/** -* Paste one or more documents here - -{ - "_id": { - "$oid": "625dc348b7856ace8a6f8702" - - }, - "uploaderId" : "6242ea59486c664208d4255c", - "inputs": ["proba", - "proba2", - "proba3" - ], - "output" : "izlaz", - "isPublic" : true, - "accessibleByLink" : true, - "dateCreated" : "2022-04-11T20:33:26.937+00:00", - "experimentId" : "Neki id eksperimenta", - "modelId" : "Neki id eksperimenta", - "h5FileId" : "Neki id eksperimenta", - "metrics" : [{ }] -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/backend/api/api/Services/DatasetService.cs b/backend/api/api/Services/DatasetService.cs index f38a363b..0b84721e 100644 --- a/backend/api/api/Services/DatasetService.cs +++ b/backend/api/api/Services/DatasetService.cs @@ -104,16 +104,6 @@ namespace api.Services return dataset._id; } - /* -public bool CheckDb() -{ - Dataset? dataset = null; - dataset = _dataset.Find(dataset => dataset.username == "igrannonica").FirstOrDefault(); - - if (dataset != null) - return false; - else - return true; -}*/ + } } diff --git a/backend/api/api/Services/FillAnEmptyDb.cs b/backend/api/api/Services/FillAnEmptyDb.cs index 811e723a..cd35dc78 100644 --- a/backend/api/api/Services/FillAnEmptyDb.cs +++ b/backend/api/api/Services/FillAnEmptyDb.cs @@ -54,7 +54,7 @@ namespace api.Services dataset._id = ""; dataset.uploaderId = "000000000000000000000000"; - dataset.name = "Titanik dataset(public)"; + dataset.name = "Titanik dataset (public)"; dataset.description = "Titanik dataset"; dataset.fileId = _fileService.GetFileId(fullPath); dataset.extension = ".csv"; @@ -277,6 +277,7 @@ namespace api.Services model.lossFunction = "sparse_categorical_crossentropy"; model.hiddenLayers = 5; model.batchSize = "64"; + model.learningRate = "1"; model.outputNeurons = 0; model.layers = new[] { @@ -368,7 +369,7 @@ namespace api.Services dataset._id = ""; dataset.uploaderId = "000000000000000000000000"; dataset.name = "IMDB-Movie-Data Dataset (public)"; - dataset.description = "IMDB-Movie-Data Dataset(public)"; + dataset.description = "IMDB-Movie-Data Dataset (public)"; dataset.fileId = _fileService.GetFileId(fullPath); dataset.extension = ".csv"; dataset.isPublic = true; @@ -421,8 +422,8 @@ namespace api.Services model._id = ""; model.uploaderId = "000000000000000000000000"; - model.name = "IMDB model"; - model.description = "IMDB model"; + model.name = "IMDB model (public)"; + model.description = "IMDB model (public)"; model.dateCreated = DateTime.Now; model.lastUpdated = DateTime.Now; model.type = "regresioni"; @@ -579,8 +580,8 @@ namespace api.Services model._id = ""; model.uploaderId = "000000000000000000000000"; - model.name = "Churn model"; - model.description = "Churn model"; + model.name = "Churn model (public)"; + model.description = "Churn model (public)"; model.dateCreated = DateTime.Now; model.lastUpdated = DateTime.Now; model.type = "binarni-klasifikacioni"; diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts index 262b3b7d..e521f264 100644 --- a/frontend/src/app/_elements/folder/folder.component.ts +++ b/frontend/src/app/_elements/folder/folder.component.ts @@ -139,10 +139,7 @@ export class FolderComponent implements AfterViewInit { _initialized: boolean = false; refreshFiles(selectedDatasetId: string | null = null, selectedModelId: string | null = null) { - this.files = [] - this.filteredFiles.length = 0; - this.folders[TabType.NewFile] = []; - this.folders[TabType.File] = []; + this.tabsToShow.forEach(tab => { this.folders[tab] = []; }); @@ -202,6 +199,7 @@ export class FolderComponent implements AfterViewInit { refreshDatasets(selectedDatasetId: string | null) { this.datasetsService.getMyDatasets().subscribe((datasets) => { this.folders[TabType.MyDatasets] = datasets; + console.log(this.filteredFiles); if (selectedDatasetId) { this.selectFile(datasets.filter(x => x._id == selectedDatasetId)[0]); } @@ -238,7 +236,7 @@ export class FolderComponent implements AfterViewInit { 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."); + Shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se obradi."); this.refreshFiles(); }, () => { @@ -287,17 +285,20 @@ export class FolderComponent implements AfterViewInit { deleteFile(file: FolderFile, event: Event) { event.stopPropagation(); - //console.log('delete'); + this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1); + this.files.splice(this.files.indexOf(file), 1); switch (this.type) { case FolderType.Dataset: this.datasetsService.deleteDataset(file).subscribe((response) => { - this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1); - this.refreshFiles(null); + Shared.openDialog("Obaveštenje", "Uspešno ste obrisali odabrani izvor podataka."); + //this.filteredFiles.splice(this.files.indexOf(file), 1); + //this.refreshFiles(); }); break; case FolderType.Model: this.modelsService.deleteModel(file).subscribe((response) => { - this.refreshFiles(null); + Shared.openDialog("Obaveštenje", "Uspešno ste obrisali odabranu konfiguraciju neuronske mreže."); + //this.refreshFiles(); }); break; case FolderType.Experiment: @@ -317,13 +318,22 @@ export class FolderComponent implements AfterViewInit { (file).isPreProcess = true; (file).isPublic = false; this.datasetsService.stealDataset(file).subscribe((response) => { - this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1); + Shared.openDialog("Obaveštenje", "Uspešno ste dodali javni izvor podataka u vašu kolekciju."); this.refreshFiles(null); + }, (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."); + } }); break; case FolderType.Model: - this.modelsService.addModel(file).subscribe((response) => { + this.modelsService.stealModel(file).subscribe((response) => { + Shared.openDialog("Obaveštenje", "Uspešno ste dodali javnu konfiguraciju neuronske mreže u vašu kolekciju."); this.refreshFiles(null); + }, (error:any) => { + if (error.error == "Model already exisits or validation size is not between 0-1") { + Shared.openDialog("Obaveštenje", "Model sa ovim imenom postoji u vašoj kolekciji."); + } }); break; case FolderType.Experiment: diff --git a/frontend/src/app/_services/models.service.ts b/frontend/src/app/_services/models.service.ts index fc888556..5f7b1947 100644 --- a/frontend/src/app/_services/models.service.ts +++ b/frontend/src/app/_services/models.service.ts @@ -31,6 +31,10 @@ export class ModelsService { addModel(model: Model): Observable { return this.http.post(`${Configuration.settings.apiURL}/model/add`, model, { headers: this.authService.authHeader() }); } + + stealModel(model: Model): Observable { + return this.http.post(`${Configuration.settings.apiURL}/model/stealModel`, model, { headers: this.authService.authHeader() }); + } addDataset(dataset: Dataset): Observable { return this.http.post(`${Configuration.settings.apiURL}/dataset/add`, dataset, { headers: this.authService.authHeader() }); } -- cgit v1.2.3 From 40e4cf81dc29d2f3a44cd56a58a5181a64295d5f Mon Sep 17 00:00:00 2001 From: Danijel Anđelković Date: Thu, 19 May 2022 21:10:36 +0200 Subject: Dodao spiner dok se izvrsavaju neke akcije. Promenio brisanje u folderu tako da se prvo postavi pitanje korisniku da li ste sigurni... Dodao pracenje da li se model/dataset promenio i mogucnost da se updateuje isti. --- backend/api/api/Controllers/DatasetController.cs | 7 +- frontend/package-lock.json | 42 +++++++ frontend/package.json | 2 + .../src/app/_elements/folder/folder.component.css | 6 +- .../src/app/_elements/folder/folder.component.html | 61 ++++++---- .../src/app/_elements/folder/folder.component.ts | 124 ++++++++++++++++----- .../form-dataset/form-dataset.component.html | 63 ++++++----- .../form-dataset/form-dataset.component.ts | 74 ++++++------ .../_elements/form-model/form-model.component.html | 26 ++--- .../_elements/form-model/form-model.component.ts | 13 ++- .../app/_elements/spinner/spinner.component.css | 78 +++++++++++++ .../app/_elements/spinner/spinner.component.html | 3 + .../_elements/spinner/spinner.component.spec.ts | 25 +++++ .../src/app/_elements/spinner/spinner.component.ts | 15 +++ .../app/_pages/experiment/experiment.component.ts | 15 +-- frontend/src/app/_services/experiments.service.ts | 4 + frontend/src/app/_services/models.service.ts | 2 +- frontend/src/app/app.module.ts | 4 +- 18 files changed, 419 insertions(+), 145 deletions(-) create mode 100644 frontend/src/app/_elements/spinner/spinner.component.css create mode 100644 frontend/src/app/_elements/spinner/spinner.component.html create mode 100644 frontend/src/app/_elements/spinner/spinner.component.spec.ts create mode 100644 frontend/src/app/_elements/spinner/spinner.component.ts (limited to 'frontend/src/app/_services/models.service.ts') diff --git a/backend/api/api/Controllers/DatasetController.cs b/backend/api/api/Controllers/DatasetController.cs index 849d9884..c93ac9db 100644 --- a/backend/api/api/Controllers/DatasetController.cs +++ b/backend/api/api/Controllers/DatasetController.cs @@ -251,9 +251,14 @@ namespace api.Controllers return NotFound($"Dataset with ID = {id} or user with ID = {uploaderId} not found"); dataset.lastUpdated = DateTime.UtcNow; - _datasetService.Update(uploaderId, id, dataset); + if (!dataset.isPreProcess) + { + FileModel fileModel = _fileService.getFile(dataset.fileId); + _mlConnectionService.PreProcess(dataset, fileModel.path, uploaderId); + } + return Ok($"Dataset with ID = {id} updated"); } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ebe30390..7a0f7b3a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -33,6 +33,7 @@ "d3-graphviz": "^2.6.1", "file-saver": "^2.0.5", "jquery": "^3.6.0", + "lodash.isequal": "^4.5.0", "material-icons": "^1.10.8", "mdb-angular-ui-kit": "^2.0.0", "ng-multiselect-dropdown": "^0.3.8", @@ -55,6 +56,7 @@ "@types/d3-graphviz": "^2.6.7", "@types/file-saver": "^2.0.5", "@types/jasmine": "~3.10.0", + "@types/lodash.isequal": "^4.5.6", "@types/node": "^12.11.1", "jasmine-core": "~4.0.0", "karma": "~6.3.0", @@ -2821,6 +2823,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.14.182", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", + "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", + "dev": true + }, + "node_modules/@types/lodash.isequal": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz", + "integrity": "sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.2", "dev": true, @@ -6891,6 +6908,11 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, "node_modules/log-symbols": { "version": "4.1.0", "dev": true, @@ -12522,6 +12544,21 @@ "version": "7.0.9", "dev": true }, + "@types/lodash": { + "version": "4.14.182", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz", + "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", + "dev": true + }, + "@types/lodash.isequal": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz", + "integrity": "sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, "@types/mime": { "version": "1.3.2", "dev": true @@ -15138,6 +15175,11 @@ "version": "4.0.8", "dev": true }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, "log-symbols": { "version": "4.1.0", "dev": true, diff --git a/frontend/package.json b/frontend/package.json index 1596072d..5d32208b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -36,6 +36,7 @@ "d3-graphviz": "^2.6.1", "file-saver": "^2.0.5", "jquery": "^3.6.0", + "lodash.isequal": "^4.5.0", "material-icons": "^1.10.8", "mdb-angular-ui-kit": "^2.0.0", "ng-multiselect-dropdown": "^0.3.8", @@ -58,6 +59,7 @@ "@types/d3-graphviz": "^2.6.7", "@types/file-saver": "^2.0.5", "@types/jasmine": "~3.10.0", + "@types/lodash.isequal": "^4.5.6", "@types/node": "^12.11.1", "jasmine-core": "~4.0.0", "karma": "~6.3.0", diff --git a/frontend/src/app/_elements/folder/folder.component.css b/frontend/src/app/_elements/folder/folder.component.css index 1a1e70ec..2b7892a3 100644 --- a/frontend/src/app/_elements/folder/folder.component.css +++ b/frontend/src/app/_elements/folder/folder.component.css @@ -105,8 +105,9 @@ .bottom-button { font-size: large; position: relative; + text-align: center; background-color: var(--ns-primary); - width: 10rem; + width: 12rem; height: 2.3rem; border-color: var(--ns-primary); border-style: solid; @@ -170,7 +171,8 @@ .folder-inside { width: 100%; - min-height: 40rem; + min-height: 33rem; + height: 100%; max-height: 100%; } diff --git a/frontend/src/app/_elements/folder/folder.component.html b/frontend/src/app/_elements/folder/folder.component.html index 97ae0d23..da182945 100644 --- a/frontend/src/app/_elements/folder/folder.component.html +++ b/frontend/src/app/_elements/folder/folder.component.html @@ -29,47 +29,48 @@ -
- +
- + - - + + -->
- - -
- - + +
@@ -107,7 +108,7 @@ {{predictor.lastUpdated | date}}
-
@@ -132,14 +133,30 @@
- + + + \ No newline at end of file diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts index 254298fb..9d19d556 100644 --- a/frontend/src/app/_elements/folder/folder.component.ts +++ b/frontend/src/app/_elements/folder/folder.component.ts @@ -14,6 +14,7 @@ import { FormModelComponent } from '../form-model/form-model.component'; import { ActivatedRoute, Router } from '@angular/router'; import Predictor from 'src/app/_data/Predictor'; import FileSaver from 'file-saver'; +import isEqual from 'lodash.isequal'; @Component({ selector: 'app-folder', @@ -105,6 +106,7 @@ export class FolderComponent implements AfterViewInit { selectFile(file?: FolderFile) { this.formDataset.resetPagging(); this.selectedFile = file; + Object.assign(this.lastFileData, this.selectedFile); this.fileToDisplay = file; if (this.type == FolderType.Experiment && file) { this.router.navigate(['/experiment/' + file._id]); @@ -118,7 +120,7 @@ export class FolderComponent implements AfterViewInit { if (this.type == FolderType.Dataset) this.formDataset.loadExisting(); } - + goToExperimentPageWithPredictor(file: FolderFile, predictor: Predictor) { this.router.navigate(['/experiment/' + file._id + "/" + predictor._id]); } @@ -138,7 +140,7 @@ export class FolderComponent implements AfterViewInit { _initialized: boolean = false; refreshFiles(selectedDatasetId: string | null = null, selectedModelId: string | null = null) { - + this.tabsToShow.forEach(tab => { this.folders[tab] = []; }); @@ -221,7 +223,7 @@ export class FolderComponent implements AfterViewInit { this.predictorsForExp[exp._id].forEach(pred => { const model = this.folders[TabType.MyModels].find(model => model._id == pred.modelId); pred.name = model?.name!; - pred.lastUpdated = model?.lastUpdated!; + //pred.lastUpdated = model?.lastUpdated!; }) /* ------------------------------------------------ */ this.searchTermsChanged(); @@ -267,7 +269,12 @@ export class FolderComponent implements AfterViewInit { searchTermsChanged() { this.filteredFiles.length = 0; if (!this.files) return; - this.filteredFiles.push(...this.files.filter((file) => file.name.toLowerCase().includes(this.searchTerm.toLowerCase()))); + this.filteredFiles.push(...this.files.filter((file) => { + return (file.name.toLowerCase().includes(this.searchTerm.toLowerCase()) + && (!this.forExperiment + || this.type != FolderType.Model + || (this.type == FolderType.Model && (file).type == this.forExperiment.type))) + })); /*if (this.selectedFile) { if (!this.filteredFiles.includes(this.selectedFile)) { if (this.hoverTab === TabType.None && this.getFolderType(this.selectedTab) === this.type) { @@ -282,41 +289,102 @@ export class FolderComponent implements AfterViewInit { listView: boolean = true; - deleteFile(file: FolderFile, event: Event) { - event.stopPropagation(); - this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1); - this.files.splice(this.files.indexOf(file), 1); + loadingAction = false; + selectedFileHasChanges = false; + lastFileData = {}; + + onFileChange() { + console.log(this.selectedFile, this.lastFileData) + setTimeout(() => { + this.selectedFileHasChanges = !((this.selectedTab == TabType.NewFile) || isEqual(this.selectedFile, this.lastFileData)); + }); + } + + updateFile() { + const file = this.selectedFile; + this.loadingAction = true; switch (this.type) { case FolderType.Dataset: - this.datasetsService.deleteDataset(file).subscribe((response) => { - Shared.openDialog("Obaveštenje", "Uspešno ste obrisali odabrani izvor podataka."); - //this.filteredFiles.splice(this.files.indexOf(file), 1); - //this.refreshFiles(); + this.datasetsService.editDataset(file).subscribe((response) => { + this.fileUpdatedSuccess(); }); break; case FolderType.Model: - this.modelsService.deleteModel(file).subscribe((response) => { - Shared.openDialog("Obaveštenje", "Uspešno ste obrisali odabranu konfiguraciju neuronske mreže."); - //this.refreshFiles(); + this.modelsService.editModel(file).subscribe((response) => { + this.fileUpdatedSuccess(); }); + break; + } + } + + fileUpdatedSuccess() { + this.loadingAction = false; + this.selectedFileHasChanges = false; + Object.assign(this.lastFileData, this.selectedFile); + this.refreshFiles(); + } + + deleteFile(file: FolderFile, event: Event, deletePredictor: boolean = false) { + event.stopPropagation(); + + switch (this.type) { + case FolderType.Dataset: + const dataset = file; + Shared.openYesNoDialog("Obriši izvor podataka", "Eksperimenti i trenirani modeli nad ovim izvorom podataka će takođe biti obrisani, da li ste sigurni da želite da obrišete izvor: " + dataset.name + "?", () => { + this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1); + this.files.splice(this.files.indexOf(file), 1); + this.loadingAction = true; + this.datasetsService.deleteDataset(dataset).subscribe((response) => { + this.loadingAction = false; + }); + }) + break; + case FolderType.Model: + const model = file; + Shared.openYesNoDialog("Obriši konfiguraciju neuronske mreže", "Trenirani modeli za ovu konfiguraciju će takođe biti obrisani, da li ste sigurni da želite da obrišete konfiguraciju: " + model.name + "?", () => { + this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1); + this.files.splice(this.files.indexOf(file), 1); + this.loadingAction = true; + this.modelsService.deleteModel(file).subscribe((response) => { + this.loadingAction = false; + }); + }) + break; case FolderType.Experiment: - // this.experimentsService.deleteExperiment(file).subscribe((response) => { - // console.log(response); - // }); - //todo delete za predictor + if (deletePredictor) { + const predictor = file; + Shared.openYesNoDialog("Obriši trenirani model", "Da li ste sigurni da želite da obrišete trenirani model: " + predictor.name + "?", () => { + this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1); + this.files.splice(this.files.indexOf(file), 1); + this.loadingAction = true; + this.predictorsService.deletePredictor(predictor).subscribe((response) => { + this.loadingAction = false; + }); + }); + } else { + const experiment = file; + Shared.openYesNoDialog("Obriši eksperiment", "Trenirani modeli za ovaj eksperiment će takođe biti obrisani, da li ste sigurni da želite da obrišete eksperiment: " + experiment.name + "?", () => { + this.filteredFiles.splice(this.filteredFiles.indexOf(file), 1); + this.files.splice(this.files.indexOf(file), 1); + this.loadingAction = true; + this.experimentsService.deleteExperiment(experiment).subscribe((response) => { + this.loadingAction = false; + }); + }); + } break; } } downloadFile(file: FolderFile, event: Event) { event.stopPropagation(); - if (this.type==FolderType.Dataset) { - const fileId=(file).fileId; - const name=(file).name; - const ext=(file).extension; - if(fileId!=undefined) - this.datasetsService.downloadFile(fileId).subscribe((response)=>{ - FileSaver.saveAs(response,name+ext); + if (this.type == FolderType.Dataset) { + const fileId = (file).fileId; + const name = (file).name; + const ext = (file).extension; + if (fileId != undefined) + this.datasetsService.downloadFile(fileId).subscribe((response) => { + FileSaver.saveAs(response, name + ext); }); @@ -333,7 +401,7 @@ export class FolderComponent implements AfterViewInit { this.datasetsService.stealDataset(file).subscribe((response) => { Shared.openDialog("Obaveštenje", "Uspešno ste dodali javni izvor podataka u vašu kolekciju."); this.refreshFiles(null); - }, (error:any) => { + }, (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."); } @@ -343,7 +411,7 @@ export class FolderComponent implements AfterViewInit { this.modelsService.stealModel(file).subscribe((response) => { Shared.openDialog("Obaveštenje", "Uspešno ste dodali javnu konfiguraciju neuronske mreže u vašu kolekciju."); this.refreshFiles(null); - }, (error:any) => { + }, (error: any) => { if (error.error == "Model already exisits or validation size is not between 0-1") { Shared.openDialog("Obaveštenje", "Model sa ovim imenom postoji u vašoj kolekciji."); } 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 7be838f1..78bb4bd1 100644 --- a/frontend/src/app/_elements/form-dataset/form-dataset.component.html +++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.html @@ -1,49 +1,48 @@
- -
-
-
- -
-
+
+
+
+ +
+
-
-
- - Naziv - +
+
+ + Naziv + - - Naziv je obavezan - - -
-
-
- - Delimiter - - - {{ option }} - - - -
+ + Naziv je obavezan + +
+
+
+
+ + Delimiter + + + {{ option }} + + +
- +
+
file_upload - - + +
- +
{{(this.begin/10)+1}}...{{getPage()}}
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 19c0083c..35d68526 100644 --- a/frontend/src/app/_elements/form-dataset/form-dataset.component.ts +++ b/frontend/src/app/_elements/form-dataset/form-dataset.component.ts @@ -24,11 +24,12 @@ export class FormDatasetComponent { files: File[] = []; rowsNumber: number = 0; colsNumber: number = 0; - begin:number=0; - step:number=10; - existingFlag:boolean=false; + begin: number = 0; + step: number = 10; + existingFlag: boolean = false; @Input() dataset: Dataset; //dodaj ! potencijalno + @Output() editEvent = new EventEmitter(); tableData: TableData = new TableData(); @@ -43,31 +44,30 @@ export class FormDatasetComponent { } //@ViewChild('fileImportInput', { static: false }) fileImportInput: any; cemu je ovo sluzilo? - resetPagging(){ - this.begin=0; + resetPagging() { + this.begin = 0; } - goBack(){ - if(this.begin-10<0) - this.begin=0; - else - { - this.begin-=10; + goBack() { + if (this.begin - 10 < 0) + this.begin = 0; + else { + this.begin -= 10; this.loadExisting(); } } - goForward(){ - this.begin+=10; - if(this.dataset.rowCount{ - - this.datasetsService.getDatasetFilePaging(this.dataset.fileId,this.begin,this.step).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(header+'\n'+file, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter); - - } - else{ - this.begin-=10; - this.loadExisting(); - } + this.datasetsService.getDatasetHeader(this.dataset.fileId).subscribe((header: string | undefined) => { + + this.datasetsService.getDatasetFilePaging(this.dataset.fileId, this.begin, this.step).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(header + '\n' + file, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter); + + } + else { + this.begin -= 10; + this.loadExisting(); + } + }); }); - }); - + } /*exportAsXLSX():void { 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 c7a9d5ca..bed69998 100644 --- a/frontend/src/app/_elements/form-model/form-model.component.html +++ b/frontend/src/app/_elements/form-model/form-model.component.html @@ -5,13 +5,13 @@
Naziv - +
Tip problema - + {{ optionName }} @@ -24,7 +24,7 @@
Optimizacija - + {{ optionName }} @@ -35,7 +35,7 @@
Funkcija troška - + {{ optionName }} @@ -48,7 +48,7 @@
Funkcija aktivacije izlaznog sloja - + {{ optionName }} @@ -58,7 +58,7 @@
Stopa učenja - + {{ optionName }} @@ -71,14 +71,14 @@
Broj epoha - +
Broj uzoraka po iteraciji - + {{option}} @@ -102,7 +102,7 @@
- +
@@ -110,13 +110,13 @@
- +
- Nasumični redosled podataka + Nasumični redosled podataka
@@ -220,7 +220,7 @@ Regularizacija - + {{ optionName }} @@ -229,7 +229,7 @@ Stopa regularizacije - + {{ optionName }} 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 9e6082c4..196d575b 100644 --- a/frontend/src/app/_elements/form-model/form-model.component.ts +++ b/frontend/src/app/_elements/form-model/form-model.component.ts @@ -24,6 +24,8 @@ export class FormModelComponent implements AfterViewInit { this.forProblemType = ProblemType.BinaryClassification; } + @Output() editEvent = new EventEmitter(); + ngAfterViewInit(): void { } selectFormControl = new FormControl('', Validators.required); @@ -80,6 +82,7 @@ export class FormModelComponent implements AfterViewInit { this.newModel.layers.splice(this.newModel.layers.length - 1, 1); this.newModel.hiddenLayers -= 1; this.updateGraph(); + this.editEvent.emit(); } } @@ -89,6 +92,7 @@ export class FormModelComponent implements AfterViewInit { this.newModel.hiddenLayers += 1; this.updateGraph(); + this.editEvent.emit(); } } @@ -100,6 +104,7 @@ export class FormModelComponent implements AfterViewInit { if (this.newModel.layers[index].neurons > 1) { this.newModel.layers[index].neurons -= 1; this.updateGraph(); + this.editEvent.emit(); } } @@ -107,6 +112,7 @@ export class FormModelComponent implements AfterViewInit { if (this.newModel.layers[index].neurons < 18) { this.newModel.layers[index].neurons += 1; this.updateGraph(); + this.editEvent.emit(); } } @@ -119,22 +125,26 @@ export class FormModelComponent implements AfterViewInit { for (let i = 0; i < this.newModel.layers.length; i++) { this.newModel.layers[i].activationFunction = this.selectedActivation; } + this.editEvent.emit(); } changeAllRegularisation() { for (let i = 0; i < this.newModel.layers.length; i++) { this.newModel.layers[i].regularisation = this.selectedRegularisation; } + this.editEvent.emit(); } changeAllRegularisationRate() { for (let i = 0; i < this.newModel.layers.length; i++) { this.newModel.layers[i].regularisationRate = this.selectedRegularisationRate; } + this.editEvent.emit(); } changeAllNumberOfNeurons() { for (let i = 0; i < this.newModel.layers.length; i++) { this.newModel.layers[i].neurons = this.selectedNumberOfNeurons; - this.updateGraph(); } + this.updateGraph(); + this.editEvent.emit(); } updateTestSet(event: MatSliderChange) { this.testSetDistribution = event.value!; @@ -149,5 +159,6 @@ export class FormModelComponent implements AfterViewInit { updateValidation(event: MatSliderChange) { this.validationSize = event.value!; + this.editEvent.emit(); } } diff --git a/frontend/src/app/_elements/spinner/spinner.component.css b/frontend/src/app/_elements/spinner/spinner.component.css new file mode 100644 index 00000000..78adc872 --- /dev/null +++ b/frontend/src/app/_elements/spinner/spinner.component.css @@ -0,0 +1,78 @@ +.wrap { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.loader { + color: #ffffff; + font-size: 20px; + margin: auto; + width: 1em; + height: 1em; + border-radius: 50%; + text-indent: -9999em; + -webkit-animation: load4 1.3s infinite linear; + animation: load4 1.3s infinite linear; + -webkit-transform: scale(0.2); + -ms-transform: scale(0.2); + transform: scale(0.2); +} + +@-webkit-keyframes load4 { + 0%, + 100% { + box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0; + } + 12.5% { + box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; + } + 25% { + box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; + } + 37.5% { + box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em; + } + 50% { + box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em; + } + 62.5% { + box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em; + } + 75% { + box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0; + } + 87.5% { + box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em; + } +} + +@keyframes load4 { + 0%, + 100% { + box-shadow: 0 -3em 0 0.2em, 2em -2em 0 0em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 0; + } + 12.5% { + box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; + } + 25% { + box-shadow: 0 -3em 0 -0.5em, 2em -2em 0 0, 3em 0 0 0.2em, 2em 2em 0 0, 0 3em 0 -1em, -2em 2em 0 -1em, -3em 0 0 -1em, -2em -2em 0 -1em; + } + 37.5% { + box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em; + } + 50% { + box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em; + } + 62.5% { + box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em; + } + 75% { + box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0; + } + 87.5% { + box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em; + } +} \ No newline at end of file diff --git a/frontend/src/app/_elements/spinner/spinner.component.html b/frontend/src/app/_elements/spinner/spinner.component.html new file mode 100644 index 00000000..c655abf0 --- /dev/null +++ b/frontend/src/app/_elements/spinner/spinner.component.html @@ -0,0 +1,3 @@ +
+
Loading...
+
\ No newline at end of file diff --git a/frontend/src/app/_elements/spinner/spinner.component.spec.ts b/frontend/src/app/_elements/spinner/spinner.component.spec.ts new file mode 100644 index 00000000..061f78d5 --- /dev/null +++ b/frontend/src/app/_elements/spinner/spinner.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SpinnerComponent } from './spinner.component'; + +describe('SpinnerComponent', () => { + let component: SpinnerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ SpinnerComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SpinnerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/spinner/spinner.component.ts b/frontend/src/app/_elements/spinner/spinner.component.ts new file mode 100644 index 00000000..f0080edd --- /dev/null +++ b/frontend/src/app/_elements/spinner/spinner.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-spinner', + templateUrl: './spinner.component.html', + styleUrls: ['./spinner.component.css'] +}) +export class SpinnerComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_pages/experiment/experiment.component.ts b/frontend/src/app/_pages/experiment/experiment.component.ts index 22894131..161c619a 100644 --- a/frontend/src/app/_pages/experiment/experiment.component.ts +++ b/frontend/src/app/_pages/experiment/experiment.component.ts @@ -59,13 +59,13 @@ export class ExperimentComponent implements AfterViewInit, OnInit { this.folderDataset.selectFile(this.dataset); //sad 3. i 4. korak da se ucitaju //this.predictorsService.getPredictor(predictorId!).subscribe((response) => { - let predictor = response; - //this.modelsService.getModelById(predictor.modelId).subscribe((response) => { - this.modelsService.getModelById("62853d70696d62ceeb8db7cd").subscribe((response) => { - //imamo model - this.folderModel.formModel.newModel = response; - //this.metricView.update(predictor.metrics); - }); + let predictor = response; + //this.modelsService.getModelById(predictor.modelId).subscribe((response) => { + this.modelsService.getModelById("62853d70696d62ceeb8db7cd").subscribe((response) => { + //imamo model + this.folderModel.formModel.newModel = response; + //this.metricView.update(predictor.metrics); + }); //}); }); }); @@ -208,6 +208,7 @@ export class ExperimentComponent implements AfterViewInit, OnInit { this.columnTable.loaded = false; this.dataset = undefined; this.experiment.datasetId = ''; + this.step1 = false; return; } const d = dataset; diff --git a/frontend/src/app/_services/experiments.service.ts b/frontend/src/app/_services/experiments.service.ts index f4473c8c..4b209ff0 100644 --- a/frontend/src/app/_services/experiments.service.ts +++ b/frontend/src/app/_services/experiments.service.ts @@ -27,4 +27,8 @@ export class ExperimentsService { updateExperiment(experiment: Experiment): Observable { return this.http.put(`${Configuration.settings.apiURL}/experiment/` + experiment._id, experiment, { headers: this.authService.authHeader() }); } + + deleteExperiment(experiment: Experiment) { + return this.http.delete(`${Configuration.settings.apiURL}/experiment/` + experiment._id, { 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 f5d95ec1..2b8fe8f2 100644 --- a/frontend/src/app/_services/models.service.ts +++ b/frontend/src/app/_services/models.service.ts @@ -51,7 +51,7 @@ export class ModelsService { } editModel(model: Model): Observable { - return this.http.put(`${Configuration.settings.apiURL}/model/`, model, { headers: this.authService.authHeader() }); + return this.http.put(`${Configuration.settings.apiURL}/model/` + model.name, model, { headers: this.authService.authHeader() }); } deleteModel(model: Model) { diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index d44bf6ad..89d53115 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -52,6 +52,7 @@ import { DoughnutChartComponent } from './_elements/_charts/doughnut-chart/dough 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'; export function initializeApp(appConfig: Configuration) { return () => appConfig.load(); @@ -91,7 +92,8 @@ export function initializeApp(appConfig: Configuration) { HeatmapComponent, MetricViewComponent, LineChartComponent, - SaveExperimentDialogComponent + SaveExperimentDialogComponent, + SpinnerComponent ], imports: [ BrowserModule, -- cgit v1.2.3