diff options
55 files changed, 1076 insertions, 844 deletions
diff --git a/backend/api/api/Controllers/DatasetController.cs b/backend/api/api/Controllers/DatasetController.cs index 375b5bfd..add85aba 100644 --- a/backend/api/api/Controllers/DatasetController.cs +++ b/backend/api/api/Controllers/DatasetController.cs @@ -25,11 +25,7 @@ namespace api.Controllers _fileService = fileService; jwtToken = Token; } - - // GET: api/<DatasetController>/mydatasets - [HttpGet("mydatasets")] - [Authorize(Roles = "User,Guest")] - public ActionResult<List<Dataset>> Get() + public string getUsername() { string username; var header = Request.Headers[HeaderNames.Authorization]; @@ -42,8 +38,21 @@ namespace api.Controllers return null; } else + return null; + + return username; + } + + // GET: api/<DatasetController>/mydatasets + [HttpGet("mydatasets")] + [Authorize(Roles = "User,Guest")] + public ActionResult<List<Dataset>> Get() + { + string username = getUsername(); + + if (username == null) return BadRequest(); - //U slucaju da je korisnik gost vrati dataSetove igrannonice + if (username == "") return _datasetService.GetGuestDatasets(); @@ -60,22 +69,13 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult<List<Dataset>> SortDatasets(bool ascdsc, int latest) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); List<Dataset> lista = _datasetService.SortDatasets(username, ascdsc, latest); - if (latest == 0) return lista; else @@ -100,17 +100,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult<List<Dataset>> Search(string name) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); //ako bude trebao ID, samo iz baze uzeti @@ -125,17 +117,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult<Dataset> Get(string name) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var dataset = _datasetService.GetOneDataset(username, name); @@ -145,18 +129,6 @@ namespace api.Controllers return dataset; } - /*za pretragu vratiti dataset koji je public - public ActionResult<Dataset> Get(string name) - { - var dataset = _datasetService.GetOneDataset(username, name); - - if (dataset == null) - return NotFound($"Dataset with name = {name} or user with username = {username} not found"); - - return dataset; - } - */ - // POST api/<DatasetController>/add [HttpPost("add")] [Authorize(Roles = "User,Guest")] @@ -183,7 +155,6 @@ namespace api.Controllers return NotFound($"Dateset with name = {dataset.name} exisits"); else { - FileModel fileModel = _fileService.getFile(dataset.fileId); dataset.isPreProcess = false; _datasetService.Create(dataset); @@ -198,17 +169,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult Put(string name, [FromBody] Dataset dataset) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var existingDataset = _datasetService.GetOneDataset(username, name); @@ -229,17 +192,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult Delete(string name) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var dataset = _datasetService.GetOneDataset(username, name); diff --git a/backend/api/api/Controllers/ExperimentController.cs b/backend/api/api/Controllers/ExperimentController.cs index 0063c532..3fa02943 100644 --- a/backend/api/api/Controllers/ExperimentController.cs +++ b/backend/api/api/Controllers/ExperimentController.cs @@ -25,10 +25,7 @@ namespace api.Controllers _mlConnectionService = mlConnectionService; _fileService = fileService; } - - [HttpPost("add")] - [Authorize(Roles = "User,Guest")] - public async Task<ActionResult<Experiment>> Post([FromBody] Experiment experiment) + public string getUserId() { string uploaderId; var header = Request.Headers[HeaderNames.Authorization]; @@ -38,15 +35,27 @@ namespace api.Controllers var parameter = headerValue.Parameter; uploaderId = jwtToken.TokenToId(parameter); if (uploaderId == null) - return BadRequest(); + return null; } else + return null; + + return uploaderId; + } + + [HttpPost("add")] + [Authorize(Roles = "User,Guest")] + public async Task<ActionResult<Experiment>> Post([FromBody] Experiment experiment) + { + string uploaderId = getUserId(); + + if (uploaderId == null) return BadRequest(); experiment.uploaderId = uploaderId; var existingExperiment = _experimentService.Get(uploaderId, experiment.name); if(existingExperiment != null) - return NotFound($"Experiment with name = {experiment.name} exisits"); + return NotFound($"Experiment with this name exists"); _experimentService.Create(experiment); return Ok(experiment); } @@ -55,17 +64,9 @@ namespace api.Controllers [Authorize(Roles = "User,Guest")] public async Task<ActionResult<Experiment>> Get(string id) { - string uploaderId; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - uploaderId = jwtToken.TokenToId(parameter); - if (uploaderId == null) - return null; - } - else + string uploaderId = getUserId(); + + if (uploaderId == null) return BadRequest(); var experiment = _experimentService.Get(id); @@ -75,28 +76,17 @@ namespace api.Controllers return Ok(experiment); } - [HttpPost("getMyExperiments")] + [HttpGet("getMyExperiments")] [Authorize(Roles = "User,Guest")] public async Task<ActionResult<List<Experiment>>> getMyExperiments() { - string uploaderId; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - uploaderId = jwtToken.TokenToId(parameter); - if (uploaderId == null) - return null; - } - else + string uploaderId = getUserId(); + + if (uploaderId == null) return BadRequest(); + var experiments=_experimentService.GetMyExperiments(uploaderId); return Ok(experiments); - } - - - } } diff --git a/backend/api/api/Controllers/FileController.cs b/backend/api/api/Controllers/FileController.cs index 0be480f2..b9b31500 100644 --- a/backend/api/api/Controllers/FileController.cs +++ b/backend/api/api/Controllers/FileController.cs @@ -25,18 +25,12 @@ namespace api.Controllers _fileservice = fileService; } - [HttpPost("h5")] - [Authorize(Roles = "User,Guest")] - public async Task<ActionResult<string>> H5Upload([FromForm] IFormFile file) + public string getUserId() { - - //get username from jwtToken string uploaderId; - string folderName; var header = Request.Headers[HeaderNames.Authorization]; if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) { - var scheme = headerValue.Scheme; var parameter = headerValue.Parameter; uploaderId = _token.TokenToId(parameter); @@ -44,7 +38,25 @@ namespace api.Controllers return null; } else + return null; + + return uploaderId; + } + + [HttpPost("h5")] + [Authorize(Roles = "User,Guest")] + public async Task<ActionResult<string>> H5Upload([FromForm] IFormFile file) + { + + //get username from jwtToken + + string folderName; + + string uploaderId = getUserId(); + + if (uploaderId == null) return BadRequest(); + if (uploaderId == "") { folderName = "TempFiles"; @@ -101,18 +113,9 @@ namespace api.Controllers public ActionResult<string> CsvRead(bool hasHeader, string fileId) { - string uploaderId; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { + string uploaderId = getUserId(); - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - uploaderId = _token.TokenToId(parameter); - if (uploaderId == null) - return null; - } - else + if (uploaderId == null) return BadRequest(); //String csvContent = System.IO.File.ReadAllText(fileModel.path); @@ -121,9 +124,9 @@ namespace api.Controllers if (hasHeader) - return String.Join("", System.IO.File.ReadLines(filePath).Take(11)); + return String.Join("\n", System.IO.File.ReadLines(filePath).Take(11)); else - return String.Join("", System.IO.File.ReadLines(filePath).Take(10)); + return String.Join("\n", System.IO.File.ReadLines(filePath).Take(10)); } @@ -135,18 +138,12 @@ namespace api.Controllers { //get username from jwtToken - string uploaderId; + string folderName; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - uploaderId = _token.TokenToId(parameter); - if (uploaderId == null) - return null; - }else + string uploaderId = getUserId(); + + if (uploaderId == null) return BadRequest(); if (uploaderId == "") @@ -215,18 +212,9 @@ namespace api.Controllers public async Task<ActionResult> DownloadH5(string id) { //Get Username - string uploaderId; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { + string uploaderId = getUserId(); - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - uploaderId = _token.TokenToId(parameter); - if (uploaderId == null) - return null; - } - else + if (uploaderId == null) return BadRequest(); string filePath = _fileservice.GetFilePath(id, uploaderId); @@ -242,18 +230,9 @@ namespace api.Controllers public async Task<ActionResult> DownloadFile(string id) { //Get Username - string uploaderId; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { + string uploaderId = getUserId(); - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - uploaderId = _token.TokenToId(parameter); - if (uploaderId == null) - return null; - } - else + if (uploaderId == null) return BadRequest(); string filePath = _fileservice.GetFilePath(id, uploaderId); diff --git a/backend/api/api/Controllers/ModelController.cs b/backend/api/api/Controllers/ModelController.cs index 618b15e8..29911e07 100644 --- a/backend/api/api/Controllers/ModelController.cs +++ b/backend/api/api/Controllers/ModelController.cs @@ -33,9 +33,7 @@ namespace api.Controllers _mlConnectionService = mlConnectionService; } - [HttpPost("sendModel")] - [Authorize(Roles = "User,Guest")] - public async Task<ActionResult<string>> Test([FromBody] string modelId,string experimentId) + public string getUserId() { string uploaderId; var header = Request.Headers[HeaderNames.Authorization]; @@ -45,10 +43,43 @@ namespace api.Controllers var parameter = headerValue.Parameter; uploaderId = jwtToken.TokenToId(parameter); if (uploaderId == null) - return BadRequest(); + return null; } else + return null; + + return uploaderId; + } + public string getUsername() + { + string username; + var header = Request.Headers[HeaderNames.Authorization]; + if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) + { + var scheme = headerValue.Scheme; + var parameter = headerValue.Parameter; + username = jwtToken.TokenToUsername(parameter); + if (username == null) + return null; + } + else + return null; + + return username; + } + + [HttpPost("trainModel")] + [Authorize(Roles = "User,Guest")] + public async Task<ActionResult<string>> Test([FromBody] TrainModelObject trainModelObject) + { + string experimentId = trainModelObject.ExperimentId; + string modelId = trainModelObject.ModelId; + + string uploaderId = getUserId(); + + if (uploaderId == null) return BadRequest(); + var experiment=_experimentService.Get(experimentId); var dataset = _datasetService.GetOneDataset(experiment.datasetId); var filepath = _fileService.GetFilePath(dataset.fileId, uploaderId); @@ -62,17 +93,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult<List<Model>> Get() { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); return _modelService.GetMyModels(username); @@ -84,17 +107,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult<Model> Get(string name) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var model = _modelService.GetOneModel(username, name); @@ -116,17 +131,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult<List<Model>> GetLatestModels(int latest) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); //ako bude trebao ID, samo iz baze uzeti @@ -142,8 +149,6 @@ namespace api.Controllers } - - // POST api/<ModelController>/add [HttpPost("add")] [Authorize(Roles = "User,Guest")] @@ -176,26 +181,14 @@ namespace api.Controllers } } - - - - // PUT api/<ModelController>/{name} [HttpPut("{name}")] [Authorize(Roles = "User")] public ActionResult Put(string name, [FromBody] Model model) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); @@ -213,17 +206,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult Delete(string name) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var model = _modelService.GetOneModel(username, name); @@ -238,4 +223,11 @@ namespace api.Controllers } } + + public class TrainModelObject + { + public string ModelId { get; set; } + public string ExperimentId { get; set; } + + } } diff --git a/backend/api/api/Controllers/PredictorController.cs b/backend/api/api/Controllers/PredictorController.cs index b9215822..233ea401 100644 --- a/backend/api/api/Controllers/PredictorController.cs +++ b/backend/api/api/Controllers/PredictorController.cs @@ -1,7 +1,6 @@ using api.Models; using api.Services; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; using System.Net.Http.Headers; @@ -22,13 +21,9 @@ namespace api.Controllers _predictorService = predictorService; jwtToken = Token; _mlConnectionService = mlConnectionService; - } - // GET: api/<PredictorController>/mypredictors - [HttpGet("mypredictors")] - [Authorize(Roles = "User")] - public ActionResult<List<Predictor>> Get() + public string getUsername() { string username; var header = Request.Headers[HeaderNames.Authorization]; @@ -41,10 +36,24 @@ namespace api.Controllers return null; } else + return null; + + return username; + } + + // GET: api/<PredictorController>/mypredictors + [HttpGet("mypredictors")] + [Authorize(Roles = "User")] + public ActionResult<List<Predictor>> Get() + { + string username = getUsername(); + + if (username == null) return BadRequest(); return _predictorService.GetMyPredictors(username); } + // GET: api/<PredictorController>/publicpredictors [HttpGet("publicpredictors")] public ActionResult<List<Predictor>> GetPublicPredictors() @@ -52,29 +61,17 @@ namespace api.Controllers return _predictorService.GetPublicPredictors(); } - - //SEARCH za predictore (public ili private sa ovim imenom ) // GET api/<PredictorController>/search/{name} [HttpGet("search/{name}")] [Authorize(Roles = "User")] public ActionResult<List<Predictor>> Search(string name) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); - //ako bude trebao ID, samo iz baze uzeti - return _predictorService.SearchPredictors(name, username); } @@ -83,20 +80,10 @@ namespace api.Controllers [Authorize(Roles = "User,Guest")] public ActionResult<Predictor> GetPredictor(string id) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else - return BadRequest(); + string username = getUsername(); - //ako bude trebao ID, samo iz baze uzeti + if (username == null) + return BadRequest(); Predictor predictor = _predictorService.GetPredictor(username, id); @@ -106,24 +93,14 @@ namespace api.Controllers return predictor; } - - //da li da se odvoji search za public i posebno za private? // GET api/<PredictorController>/{name} [HttpGet("{name}")] [Authorize(Roles = "User")] public ActionResult<Predictor> Get(string name) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var predictor = _predictorService.GetOnePredictor(username, name); @@ -143,20 +120,10 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult<List<Predictor>> SortPredictors(bool ascdsc, int latest) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else - return BadRequest(); + string username = getUsername(); - //ako bude trebao ID, samo iz baze uzeti + if (username == null) + return BadRequest(); List<Predictor> lista = _predictorService.SortPredictors(username, ascdsc, latest); @@ -172,6 +139,7 @@ namespace api.Controllers return novaLista; } } + // POST api/<PredictorController>/add [HttpPost("add")] [Authorize(Roles = "User")] @@ -194,18 +162,9 @@ namespace api.Controllers [Authorize(Roles = "User,Guest")] public ActionResult UsePredictor(String id, [FromBody] PredictorColumns[] inputs) { + string username = getUsername(); - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + if (username == null) return BadRequest(); Predictor predictor = _predictorService.GetPredictor(username, id); @@ -215,23 +174,14 @@ namespace api.Controllers return NoContent(); } - // PUT api/<PredictorController>/{name} [HttpPut("{name}")] [Authorize(Roles = "User")] public ActionResult Put(string name, [FromBody] Predictor predictor) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var existingPredictor = _predictorService.GetOnePredictor(username, name); @@ -245,30 +195,14 @@ namespace api.Controllers return Ok($"Predictor with name = {name} updated"); } - // odraditi pretragu predictora - //potrebna public i private pretraga - //prvo da napakuje svoje pa onda ostale - // - //isto odraditi i za datasetove - // - - // DELETE api/<PredictorController>/name [HttpDelete("{name}")] [Authorize(Roles = "User")] public ActionResult Delete(string name) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var predictor = _predictorService.GetOnePredictor(username, name); @@ -281,9 +215,5 @@ namespace api.Controllers return Ok($"Predictor with name = {name} deleted"); } - - - - } } diff --git a/backend/api/api/Controllers/UserController.cs b/backend/api/api/Controllers/UserController.cs index 782a02cf..9f736679 100644 --- a/backend/api/api/Controllers/UserController.cs +++ b/backend/api/api/Controllers/UserController.cs @@ -22,6 +22,23 @@ namespace api.Controllers this.userService = userService; jwtToken = token; } + public string getUsername() + { + string username; + var header = Request.Headers[HeaderNames.Authorization]; + if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) + { + var scheme = headerValue.Scheme; + var parameter = headerValue.Parameter; + username = jwtToken.TokenToUsername(parameter); + if (username == null) + return null; + } + else + return null; + + return username; + } // GET: api/<UserController> [HttpGet] @@ -36,17 +53,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult<User> MyProfilePage() { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var user = userService.GetUserUsername(username); @@ -61,7 +70,6 @@ namespace api.Controllers [HttpPost] public ActionResult<User> Post([FromBody] User user) { - var existingUser = userService.GetUserUsername(user.Username); if (existingUser != null) @@ -81,20 +89,10 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult PutPass([FromBody] string[] Password) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else - return BadRequest(); - + string username = getUsername(); + if (username == null) + return BadRequest(); User user = new User(); @@ -122,17 +120,9 @@ namespace api.Controllers [HttpPut("changeinfo")] public ActionResult Put([FromBody] User user) { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); return Ok(userService.Update(username, user)); @@ -143,17 +133,9 @@ namespace api.Controllers [Authorize(Roles = "User")] public ActionResult Delete() { - string username; - var header = Request.Headers[HeaderNames.Authorization]; - if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) - { - var scheme = headerValue.Scheme; - var parameter = headerValue.Parameter; - username = jwtToken.TokenToUsername(parameter); - if (username == null) - return null; - } - else + string username = getUsername(); + + if (username == null) return BadRequest(); var user = userService.GetUserUsername(username); diff --git a/backend/api/api/Models/Dataset.cs b/backend/api/api/Models/Dataset.cs index 12dcfa08..47814449 100644 --- a/backend/api/api/Models/Dataset.cs +++ b/backend/api/api/Models/Dataset.cs @@ -25,6 +25,7 @@ namespace api.Models public bool hasHeader { get; set; } public ColumnInfo[] columnInfo { get; set; } + public int rowCount { get; set; } public int nullCols { get; set; } public int nullRows { get; set; } public bool isPreProcess { get; set; } diff --git a/backend/api/api/Models/Experiment.cs b/backend/api/api/Models/Experiment.cs index d5ce7627..bf029116 100644 --- a/backend/api/api/Models/Experiment.cs +++ b/backend/api/api/Models/Experiment.cs @@ -10,6 +10,7 @@ namespace api.Models public string _id { get; set; } public string name { get; set; } public string description { get; set; } + public string encoding { get; set; } public List<string> ModelIds { get; set; } public string datasetId { get; set; } public string uploaderId { get; set; } diff --git a/backend/api/api/Models/Model.cs b/backend/api/api/Models/Model.cs index f0e4d346..72f989a6 100644 --- a/backend/api/api/Models/Model.cs +++ b/backend/api/api/Models/Model.cs @@ -23,7 +23,6 @@ namespace api.Models //Neural net training public string type { get; set; } - public string encoding { get; set; } public string optimizer { get; set; } public string lossFunction { get; set; } //public int inputNeurons { get; set; } diff --git a/backend/api/api/Models/Predictor.cs b/backend/api/api/Models/Predictor.cs index 911a9aee..b1d6444b 100644 --- a/backend/api/api/Models/Predictor.cs +++ b/backend/api/api/Models/Predictor.cs @@ -24,7 +24,7 @@ namespace api.Models /* { - "_id" : "6244950f26cf2385bc29ba28", + "_id" : "", "username" : "ivan123", "name" : "Neki prediktor", "description" : "Neki opis prediktora koji je unet tamo", @@ -32,9 +32,10 @@ namespace api.Models "proba2", "proba3" ], - "output" : "", + "output" : "izlaz", "isPublic" : true, - "accessibleByLink" : false, - "dateCreated" : "232" + "accessibleByLink" : true, + "dateCreated" : "2022-04-11T20:33:26.937+00:00", + "experimentId" : "Neki id eksperimenta" } */
\ No newline at end of file diff --git a/backend/api/api/Services/IPredictorService.cs b/backend/api/api/Services/IPredictorService.cs index 7ea8d85e..729dd0b6 100644 --- a/backend/api/api/Services/IPredictorService.cs +++ b/backend/api/api/Services/IPredictorService.cs @@ -14,7 +14,6 @@ namespace api.Services Predictor Create(Predictor predictor); void Update(string username, string name, Predictor predictor); void Delete(string username, string name); - bool CheckDb(); } } diff --git a/backend/api/api/Services/PredictorService.cs b/backend/api/api/Services/PredictorService.cs index 22432ec2..b89eaded 100644 --- a/backend/api/api/Services/PredictorService.cs +++ b/backend/api/api/Services/PredictorService.cs @@ -45,12 +45,11 @@ namespace api.Services return _predictor.Find(predictor => predictor._id == id && (predictor.username == username || predictor.isPublic == true)).FirstOrDefault(); } - //last private models + public List<Predictor> SortPredictors(string username, bool ascdsc, int latest) { List<Predictor> list = _predictor.Find(predictor => predictor.username == username).ToList(); - if (ascdsc) list = list.OrderBy(predictor => predictor.dateCreated).ToList(); else @@ -66,17 +65,6 @@ namespace api.Services public void Update(string username, string name, Predictor predictor) { _predictor.ReplaceOne(predictor => predictor.username == username && predictor.name == name, predictor); - - } - public bool CheckDb() - { - Predictor? predictor = null; - predictor = _predictor.Find(predictor => predictor.username == "igrannonica").FirstOrDefault(); - - if (predictor != null) - return false; - else - return true; } } } diff --git a/backend/microservice/api/ml_service.py b/backend/microservice/api/ml_service.py index b5f5e9bf..4d2212f7 100644 --- a/backend/microservice/api/ml_service.py +++ b/backend/microservice/api/ml_service.py @@ -101,99 +101,103 @@ class TrainingResultRegression: class TrainingResult: metrics: dict ''' -def train(dataset, params, callback): - problem_type = params["type"] - data = pd.DataFrame() - for col in params["inputColumns"]: - data[col]=dataset[col] +def train(dataset, paramsModel, paramsExperiment, callback): + problem_type = paramsModel["type"] + dataModel = pd.DataFrame() + dataExperiment = pd.DataFrame() + for col in paramsModel["inputColumns"]: + dataModel[col]=dataset[col] + for col in paramsExperiment["inputColumns"]: + dataExperiment[col]=dataset[col] - print(data.head()) - output_column = params["columnToPredict"] - data[output_column] = dataset[output_column] + print(dataModel.head()) + output_column_model = paramsModel["columnToPredict"] + output_column_experiment = paramsExperiment["outputColumn"] + dataModel[output_column_model] = dataset[output_column_model] # # Brisanje null kolona / redova / zamena #nullreplace=[ # {"column":"Embarked","value":"C","deleteRow":false,"deleteCol":true}, # {"column": "Cabin","value":"C123","deleteRow":"0","deleteCol":"0"}] - null_value_options = params["nullValues"] - null_values_replacers = params["nullValuesReplacers"] + null_value_options = paramsModel["nullValues"] + null_values_replacers = paramsModel["nullValuesReplacers"] if(null_value_options=='replace'): print("replace null") # TODO elif(null_value_options=='delete_rows'): - data=data.dropna() + dataModel=dataModel.dropna() elif(null_value_options=='delete_columns'): - data=data.dropna() + dataModel=dataModel.dropna() # #print(data.isnull().any()) # # Brisanje kolona koje ne uticu na rezultat # - num_rows=data.shape[0] - for col in data.columns: - if((data[col].nunique()==(num_rows)) and (data[col].dtype==np.object_)): - data.pop(col) + num_rows=dataModel.shape[0] + for col in dataModel.columns: + if((dataModel[col].nunique()==(num_rows)) and (dataModel[col].dtype==np.object_)): + dataModel.pop(col) # # Enkodiranje # https://www.analyticsvidhya.com/blog/2020/08/types-of-categorical-data-encoding/ # - encoding=params["encoding"] + encoding=paramsModel["encoding"] if(encoding=='label'): encoder=LabelEncoder() - for col in data.columns: - if(data[col].dtype==np.object_): - data[col]=encoder.fit_transform(data[col]) + for col in dataModel.columns: + if(dataModel[col].dtype==np.object_): + dataModel[col]=encoder.fit_transform(dataModel[col]) elif(encoding=='onehot'): category_columns=[] - for col in data.columns: - if(data[col].dtype==np.object_): + for col in dataModel.columns: + if(dataModel[col].dtype==np.object_): category_columns.append(col) - data=pd.get_dummies(data, columns=category_columns, prefix=category_columns) + dataModel=pd.get_dummies(dataModel, columns=category_columns, prefix=category_columns) elif(encoding=='ordinal'): encoder = OrdinalEncoder() - for col in data.columns: - if(data[col].dtype==np.object_): - data[col]=encoder.fit_transform(data[col]) + for col in dataModel.columns: + if(dataModel[col].dtype==np.object_): + dataModel[col]=encoder.fit_transform(dataModel[col]) elif(encoding=='hashing'): category_columns=[] - for col in data.columns: - if(data[col].dtype==np.object_): + for col in dataModel.columns: + if(dataModel[col].dtype==np.object_): category_columns.append(col) encoder=ce.HashingEncoder(cols=category_columns, n_components=len(category_columns)) - encoder.fit_transform(data) + encoder.fit_transform(dataModel) elif(encoding=='binary'): category_columns=[] - for col in data.columns: - if(data[col].dtype==np.object_): + for col in dataModel.columns: + if(dataModel[col].dtype==np.object_): category_columns.append(col) encoder=ce.BinaryEncoder(cols=category_columns, return_df=True) - encoder.fit_transform(data) + encoder.fit_transform(dataModel) elif(encoding=='baseN'): category_columns=[] - for col in data.columns: - if(data[col].dtype==np.object_): + for col in dataModel.columns: + if(dataModel[col].dtype==np.object_): category_columns.append(col) encoder=ce.BaseNEncoder(cols=category_columns, return_df=True, base=5) - encoder.fit_transform(data) + encoder.fit_transform(dataModel) # # Input - output # x_columns = [] - for col in data.columns: - if(col!=output_column): + for col in dataModel.columns: + if(col!=output_column_model): x_columns.append(col) - x = data[x_columns].values - y = data[output_column].values + x = dataModel[x_columns].values + y = dataModel[output_column_model].values print(x_columns) print(x) # # Podela na test i trening skupove # - test=params["randomTestSetDistribution"] - randomOrder = params["randomOrder"] + test=paramsModel["randomTestSetDistribution"] + randomOrder = paramsModel["randomOrder"] if(randomOrder): random=123 else: @@ -213,60 +217,60 @@ def train(dataset, params, callback): # Treniranje modela # # - hidden_layer_neurons = params["hiddenLayerNeurons"] + hidden_layer_neurons = paramsModel["hiddenLayerNeurons"] if(problem_type=='multi-klasifikacioni'): - func=params['hiddenLayerActivationFunctions'] - output_func = params["outputLayerActivationFunction"] - optimizer = params["optimizer"] - metrics=params['metrics'] - loss_func=params["lossFunction"] - batch_size = params["batchSize"] - epochs = params["epochs"] - inputDim = len(data.columns) - 1 + func=paramsModel['hiddenLayerActivationFunctions'] + output_func = paramsModel["outputLayerActivationFunction"] + optimizer = paramsModel["optimizer"] + metrics=paramsModel['metrics'] + loss_func=paramsModel["lossFunction"] + batch_size=paramsModel["batchSize"] + epochs=paramsModel["epochs"] + inputDim=len(dataModel.columns) - 1 ''' classifier=tf.keras.Sequential() - classifier.add(tf.keras.layers.Dense(units=len(data.columns),input_dim=inputDim))#input layer + classifier.add(tf.keras.layers.Dense(units=len(dataModel.columns),input_dim=inputDim))#input layer for f in func:#hidden layers classifier.add(tf.keras.layers.Dense(hidden_layer_neurons,activation=f)) - numberofclasses=len(output_column.unique()) + numberofclasses=len(output_column_model.unique()) classifier.add(tf.keras.layers.Dense(numberofclasses,activation=output_func))#output layer ''' model=tf.keras.Sequential() model.add(tf.keras.layers.Dense(1,input_dim=x_train.shape[1]))#input layer model.add(tf.keras.layers.Dense(1, activation='sigmoid')) - model.add(tf.keras.layers.Dense(len(output_column.unique())+1, activation='softmax')) + model.add(tf.keras.layers.Dense(len(output_column_model.unique())+1, activation='softmax')) classifier.compile(optimizer=optimizer, loss=loss_func,metrics=metrics) history=classifier.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, callbacks=callback(x_test, y_test)) else: classifier=tf.keras.Sequential() - for func in params["hiddenLayerActivationFunctions"]: - layers = params["hiddenLayers"] - for numNeurons in params["hiddenLayerNeurons"]: + for func in paramsModel["hiddenLayerActivationFunctions"]: + layers = paramsModel["hiddenLayers"] + for numNeurons in paramsModel["hiddenLayerNeurons"]: classifier.add(tf.keras.layers.Dense(units=numNeurons,activation=func)) - output_func = params["outputLayerActivationFunction"] + output_func = paramsModel["outputLayerActivationFunction"] if(problem_type!="regresioni"): classifier.add(tf.keras.layers.Dense(units=1,activation=output_func)) else: classifier.add(tf.keras.layers.Dense(units=1)) - optimizer = params["optimizer"] - metrics=params['metrics'] - loss_func=params["lossFunction"] + optimizer = paramsModel["optimizer"] + metrics=paramsModel['metrics'] + loss_func=paramsModel["lossFunction"] classifier.compile(optimizer=optimizer, loss=loss_func,metrics=metrics) - batch_size = params["batchSize"] - epochs = params["epochs"] + batch_size = paramsModel["batchSize"] + epochs = paramsModel["epochs"] history=classifier.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, callbacks=callback(x_test, y_test), validation_split=0.2) # TODO params["validationSplit"] # # Test # - model_name = params['_id'] + model_name = paramsModel['_id'] #y_pred=classifier.predict(x_test) if(problem_type == "regresioni"): y_pred=classifier.predict(x_test) @@ -375,8 +379,8 @@ def manageH5(datain,params,h5model): data = pd.DataFrame() for col in params["inputColumns"]: data[col]=dataset[col] - output_column = params["columnToPredict"] - data[output_column] = dataset[output_column] + output_column_model = params["columnToPredict"] + data[output_column_model] = dataset[output_column_model] # # Brisanje null kolona / redova / zamena #nullreplace=[ @@ -450,10 +454,10 @@ def manageH5(datain,params,h5model): # x_columns = [] for col in data.columns: - if(col!=output_column): + if(col!=output_column_model): x_columns.append(col) x = data[x_columns].values - y = data[output_column].values + y = data[output_column_model].values y_pred=h5model.predict_classes(x)
\ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a82131d5..20a636e3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -453,6 +453,7 @@ "version": "13.2.5", "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.2.5.tgz", "integrity": "sha512-Xd8xj2Z0ilA4TJAM/JkTtA1CAa6SuebFsEEvabHCRO5MDvtdsIUP91ADUZIqDHy7qe6Qift/rAVN2PXxT2aaNA==", + "dev": true, "dependencies": { "@babel/core": "^7.17.2", "chokidar": "^3.0.0", @@ -482,6 +483,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.0" }, @@ -493,6 +495,7 @@ "version": "7.17.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.5.tgz", "integrity": "sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA==", + "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -522,6 +525,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -530,6 +534,7 @@ "version": "7.17.3", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.3.tgz", "integrity": "sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==", + "dev": true, "dependencies": { "@babel/types": "^7.17.0", "jsesc": "^2.5.1", @@ -543,6 +548,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -782,6 +788,7 @@ "version": "7.16.12", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", "@babel/generator": "^7.16.8", @@ -811,6 +818,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -819,6 +827,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -827,6 +836,7 @@ "version": "7.16.8", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "dev": true, "dependencies": { "@babel/types": "^7.16.8", "jsesc": "^2.5.1", @@ -840,6 +850,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -3366,6 +3377,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3639,6 +3651,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, "engines": { "node": ">=8" } @@ -3735,6 +3748,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -3912,6 +3926,7 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, "funding": [ { "type": "individual", @@ -4930,6 +4945,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, "engines": { "node": ">= 0.6.0" } @@ -5906,6 +5922,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6074,6 +6091,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -6183,6 +6201,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -6775,6 +6794,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6828,6 +6848,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -6844,6 +6865,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -6870,6 +6892,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } @@ -7735,6 +7758,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -7746,6 +7770,7 @@ "version": "0.25.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, "dependencies": { "sourcemap-codec": "^1.4.4" } @@ -8387,6 +8412,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9082,6 +9108,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -9896,6 +9923,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -9906,7 +9934,8 @@ "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true }, "node_modules/regenerate": { "version": "1.4.2", @@ -10326,6 +10355,7 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -10710,7 +10740,8 @@ "node_modules/sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true }, "node_modules/spdy": { "version": "4.0.2", @@ -11111,6 +11142,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -11202,6 +11234,7 @@ "version": "4.5.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11841,7 +11874,8 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", @@ -12176,6 +12210,7 @@ "version": "13.2.5", "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.2.5.tgz", "integrity": "sha512-Xd8xj2Z0ilA4TJAM/JkTtA1CAa6SuebFsEEvabHCRO5MDvtdsIUP91ADUZIqDHy7qe6Qift/rAVN2PXxT2aaNA==", + "dev": true, "requires": { "@babel/core": "^7.17.2", "chokidar": "^3.0.0", @@ -12193,6 +12228,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.0" } @@ -12201,6 +12237,7 @@ "version": "7.17.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.5.tgz", "integrity": "sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA==", + "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -12222,7 +12259,8 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -12230,6 +12268,7 @@ "version": "7.17.3", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.3.tgz", "integrity": "sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==", + "dev": true, "requires": { "@babel/types": "^7.17.0", "jsesc": "^2.5.1", @@ -12239,7 +12278,8 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -12384,6 +12424,7 @@ "version": "7.16.12", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", + "dev": true, "requires": { "@babel/code-frame": "^7.16.7", "@babel/generator": "^7.16.8", @@ -12405,12 +12446,14 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -12418,6 +12461,7 @@ "version": "7.16.8", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "dev": true, "requires": { "@babel/types": "^7.16.8", "jsesc": "^2.5.1", @@ -12427,7 +12471,8 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -13605,8 +13650,7 @@ "version": "13.2.5", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.2.5.tgz", "integrity": "sha512-obiPvwPe+UJUO8cfNbBxukLKG30F+gLF5/erexwklRknJzS4KP8ciH2on6XlTuXUahpDjbO0pffugFE2I/IszQ==", - "dev": true, - "requires": {} + "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -14206,8 +14250,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} + "dev": true }, "adjust-sourcemap-loader": { "version": "4.0.0", @@ -14330,6 +14373,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -14527,7 +14571,8 @@ "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true }, "bl": { "version": "4.1.0", @@ -14598,8 +14643,7 @@ "bootstrap": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", - "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", - "requires": {} + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==" }, "brace-expansion": { "version": "1.1.11", @@ -14614,6 +14658,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -14742,6 +14787,7 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -14769,8 +14815,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "dev": true, - "requires": {} + "dev": true }, "clean-stack": { "version": "2.2.0", @@ -15248,8 +15293,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "dev": true, - "requires": {} + "dev": true }, "css-select": { "version": "4.2.1", @@ -15515,7 +15559,8 @@ "dependency-graph": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==" + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true }, "destroy": { "version": "1.0.4", @@ -16182,6 +16227,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -16304,6 +16350,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "optional": true }, "function-bind": { @@ -16379,6 +16426,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -16635,8 +16683,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} + "dev": true }, "ieee754": { "version": "1.2.1", @@ -16833,6 +16880,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "requires": { "binary-extensions": "^2.0.0" } @@ -16864,7 +16912,8 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -16875,6 +16924,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -16894,7 +16944,8 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-path-cwd": { "version": "2.2.0", @@ -17318,8 +17369,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", - "dev": true, - "requires": {} + "dev": true }, "karma-source-map-support": { "version": "1.4.0", @@ -17541,6 +17591,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "requires": { "yallist": "^4.0.0" } @@ -17549,6 +17600,7 @@ "version": "0.25.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, "requires": { "sourcemap-codec": "^1.4.4" } @@ -18024,7 +18076,8 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "normalize-range": { "version": "0.1.2", @@ -18557,7 +18610,8 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true }, "pify": { "version": "2.3.0", @@ -18673,8 +18727,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", - "dev": true, - "requires": {} + "dev": true }, "postcss-custom-properties": { "version": "12.1.4", @@ -18744,15 +18797,13 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "requires": {} + "dev": true }, "postcss-gap-properties": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-image-set-function": { "version": "4.0.6", @@ -18778,8 +18829,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-lab-function": { "version": "4.1.1", @@ -18806,22 +18856,19 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true, - "requires": {} + "dev": true }, "postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -18865,15 +18912,13 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", - "dev": true, - "requires": {} + "dev": true }, "postcss-page-break": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-place": { "version": "7.0.4", @@ -18938,8 +18983,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-selector-not": { "version": "5.0.0", @@ -19113,6 +19157,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "requires": { "picomatch": "^2.2.1" } @@ -19120,7 +19165,8 @@ "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true }, "regenerate": { "version": "1.4.2", @@ -19395,8 +19441,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -19425,6 +19470,7 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -19740,7 +19786,8 @@ "sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true }, "spdy": { "version": "4.0.2", @@ -19948,8 +19995,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -20023,6 +20069,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "requires": { "is-number": "^7.0.0" } @@ -20091,7 +20138,8 @@ "typescript": { "version": "4.5.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==" + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "dev": true }, "ua-parser-js": { "version": "0.7.31", @@ -20300,8 +20348,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -20537,8 +20584,7 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true, - "requires": {} + "dev": true }, "y18n": { "version": "5.0.8", @@ -20548,7 +20594,8 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "yaml": { "version": "1.10.2", diff --git a/frontend/src/app/Shared.ts b/frontend/src/app/Shared.ts index 7be29cbf..59a2716d 100644 --- a/frontend/src/app/Shared.ts +++ b/frontend/src/app/Shared.ts @@ -2,6 +2,7 @@ import { ElementRef } from "@angular/core"; import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { AlertDialogComponent } from './_modals/alert-dialog/alert-dialog.component'; +import { YesNoDialogComponent } from './_modals/yes-no-dialog/yes-no-dialog.component'; class Shared { constructor( @@ -26,6 +27,18 @@ class Shared { }); } } + openYesNoDialog(title: string, message: string,yesFunction:Function): void { + + if (this.dialog) { + const dialogRef = this.dialog.open(YesNoDialogComponent, { + width: '350px', + data: { title: title, message: message,yesFunction} + }); + dialogRef.afterClosed().subscribe(res => { + //nesto + }); + } + } } export default new Shared(false);
\ No newline at end of file diff --git a/frontend/src/app/_data/Dataset.ts b/frontend/src/app/_data/Dataset.ts index 87f27d12..732d1c56 100644 --- a/frontend/src/app/_data/Dataset.ts +++ b/frontend/src/app/_data/Dataset.ts @@ -15,6 +15,7 @@ export default class Dataset { public hasHeader: boolean = true, public columnInfo: ColumnInfo[] = [], + public rowCount: number = 0, public nullRows: number = 0, public nullCols: number = 0, public preview: string[][] = [[]] diff --git a/frontend/src/app/_data/Experiment.ts b/frontend/src/app/_data/Experiment.ts index a5aad218..453f6ca0 100644 --- a/frontend/src/app/_data/Experiment.ts +++ b/frontend/src/app/_data/Experiment.ts @@ -2,7 +2,7 @@ export default class Experiment { _id: string = ''; uploaderId: string = ''; constructor( - public name: string = 'Novi experiment', + public name: string = 'Novi eksperiment', public description: string = '', public datasetId: string = '', public inputColumns: string[] = [], @@ -10,7 +10,16 @@ export default class Experiment { public nullValues: NullValueOptions = NullValueOptions.DeleteRows, public nullValuesReplacers: NullValReplacer[] = [], public dateCreated: Date = new Date(), - public lastUpdated: Date = new Date() + public lastUpdated: Date = new Date(), + public modelIds: string[] = [], + + // Test set settings + public randomOrder: boolean = true, + public randomTestSet: boolean = true, + public randomTestSetDistribution: number = 0.1, //0.1-0.9 (10% - 90%) JESTE OVDE ZAKUCANO 10, AL POSLATO JE KAO 0.1 BACK-U + + //TODO - za svaku kolonu se bira enkoding + public encoding: Encoding = Encoding.Label ) { } } @@ -32,4 +41,28 @@ export class NullValReplacer { "column": string; "option": NullValueOptions; "value": string; +} + +export enum Encoding { + Label = 'label', + OneHot = 'onehot', + Ordinal = 'ordinal', + Hashing = 'hashing', + Binary = 'binary', + BaseN = 'baseN' + /* + BackwardDifference = 'backward difference', + CatBoost = 'cat boost', + Count = 'count', + GLMM = 'glmm', + Target = 'target', + Helmert = 'helmert', + JamesStein = 'james stein', + LeaveOneOut = 'leave one out', + MEstimate = 'MEstimate', + Sum = 'sum', + Polynomial = 'polynomial', + WOE = 'woe', + Quantile = 'quantile' + */ }
\ No newline at end of file diff --git a/frontend/src/app/_data/Model.ts b/frontend/src/app/_data/Model.ts index 9ea437b1..8a85e296 100644 --- a/frontend/src/app/_data/Model.ts +++ b/frontend/src/app/_data/Model.ts @@ -7,16 +7,10 @@ export default class Model { public description: string = '', public dateCreated: Date = new Date(), public lastUpdated: Date = new Date(), - public experimentId: string = '', - - // Test set settings - public randomOrder: boolean = true, - public randomTestSet: boolean = true, - public randomTestSetDistribution: number = 0.1, //0.1-0.9 (10% - 90%) JESTE OVDE ZAKUCANO 10, AL POSLATO JE KAO 0.1 BACK-U + //public experimentId: string = '', // Neural net training settings public type: ProblemType = ProblemType.Regression, - public encoding: Encoding = Encoding.Label, public optimizer: Optimizer = Optimizer.Adam, public lossFunction: LossFunction = LossFunction.MeanSquaredError, public inputNeurons: number = 1, @@ -39,29 +33,6 @@ export enum ProblemType { // replaceMissing srednja vrednost mean, median, najcesca vrednost (mode) // removeOutliers -export enum Encoding { - Label = 'label', - OneHot = 'onehot', - Ordinal = 'ordinal', - Hashing = 'hashing', - Binary = 'binary', - BaseN = 'baseN' - /* - BackwardDifference = 'backward difference', - CatBoost = 'cat boost', - Count = 'count', - GLMM = 'glmm', - Target = 'target', - Helmert = 'helmert', - JamesStein = 'james stein', - LeaveOneOut = 'leave one out', - MEstimate = 'MEstimate', - Sum = 'sum', - Polynomial = 'polynomial', - WOE = 'woe', - Quantile = 'quantile' - */ -} export enum ActivationFunction { // linear 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 index eb68b54c..bff8b022 100644 --- 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 @@ -41,9 +41,9 @@ </div> <div class="px-5 mt-5"> - <app-datatable [data]="csvRecords" [hasHeader]="dataset.hasHeader"></app-datatable> + <app-datatable [tableData]="tableData"></app-datatable> </div> -<div class="d-flex flex-row align-items-center justify-content-center w-100"> +<div class="d-flex flex-row align-items-center justify-content-center w-100 my-2"> <button (click)="uploadDataset()" class="btn btn-lg col-4" style="background-color:#003459; color:white;">Dodaj izvor podataka</button> </div> diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts index 7421fbcf..3adc16f3 100644 --- a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts +++ b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts @@ -4,7 +4,8 @@ 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 } from '../datatable/datatable.component'; +import { DatatableComponent, TableData } from '../datatable/datatable.component'; +import { CsvParseService } from 'src/app/_services/csv-parse.service'; @Component({ selector: 'app-add-new-dataset', @@ -14,13 +15,10 @@ import { DatatableComponent } from '../datatable/datatable.component'; export class AddNewDatasetComponent { @Output() newDatasetAdded = new EventEmitter<string>(); - @ViewChild(DatatableComponent) datatable?: DatatableComponent; + @ViewChild(DatatableComponent) datatable!: DatatableComponent; delimiterOptions: Array<string> = [",", ";", "\t", "razmak", "|"]; //podrazumevano "," - //hasHeader: boolean = true; - hasInput: boolean = false; - csvRecords: any[] = []; files: File[] = []; rowsNumber: number = 0; @@ -28,7 +26,9 @@ export class AddNewDatasetComponent { dataset: Dataset; //dodaj ! potencijalno - constructor(private ngxCsvParser: NgxCsvParser, private modelsService: ModelsService, private datasetsService: DatasetsService) { + tableData: TableData = new TableData(); + + constructor(private modelsService: ModelsService, private datasetsService: DatasetsService, private csv: CsvParseService) { this.dataset = new Dataset(); } @@ -39,12 +39,13 @@ export class AddNewDatasetComponent { if (this.files.length == 0 || this.files[0] == null) { //console.log("NEMA FAJLA"); //this.loaded.emit("not loaded"); - this.hasInput = false; + this.tableData.hasInput = false; return; } else - this.hasInput = true; + this.tableData.hasInput = true; + this.tableData.loaded = false; this.update(); } @@ -53,34 +54,27 @@ export class AddNewDatasetComponent { if (this.files.length < 1) return; - this.datatable!.loaded = false; - this.datatable!.hasInput = this.hasInput; - - this.ngxCsvParser.parse(this.files[0], { header: false, delimiter: (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter }) - .pipe().subscribe((result) => { - - console.log('Result', result); - if (result.constructor === Array) { - if(this.dataset.hasHeader) - this.csvRecords = result.splice(0,11); - else - this.csvRecords=result.splice(0,10); - if (this.dataset.hasHeader) - this.rowsNumber = this.csvRecords.length - 1; - else - this.rowsNumber = this.csvRecords.length; - this.colsNumber = this.csvRecords[0].length; - - if (this.dataset.hasHeader) - this.dataset.header = this.csvRecords[0]; - - this.datatable!.data = this.csvRecords; - this.datatable!.hasHeader = this.dataset.hasHeader; - this.datatable!.loaded = true; - } - }, (error: NgxCSVParserError) => { - console.log('Error', error); - }); + const fileReader = new FileReader(); + fileReader.onload = (e) => { + if (typeof fileReader.result === 'string') { + const result = this.csv.csvToArray(fileReader.result, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter) + + if (this.dataset.hasHeader) + this.csvRecords = result.splice(0, 11); + else + this.csvRecords = result.splice(0, 10); + + this.colsNumber = result[0].length; + this.rowsNumber = result.length; + + this.tableData.data = this.csvRecords + this.tableData.hasHeader = this.dataset.hasHeader; + this.tableData.loaded = true; + this.tableData.numCols = this.colsNumber; + this.tableData.numRows = this.rowsNumber; + } + } + fileReader.readAsText(this.files[0]); } checkAccessible() { @@ -96,17 +90,17 @@ export class AddNewDatasetComponent { this.modelsService.uploadData(this.files[0]).subscribe((file) => { //console.log('ADD MODEL: STEP 2 - ADD DATASET WITH FILE ID ' + file._id); - this.dataset.fileId = file._id; - this.dataset.username = shared.username; - - this.datasetsService.addDataset(this.dataset).subscribe((dataset) => { - this.newDatasetAdded.emit("added"); - shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira."); - }, (error) => { - shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset."); - }); //kraj addDataset subscribe + this.dataset.fileId = file._id; + this.dataset.username = shared.username; + + this.datasetsService.addDataset(this.dataset).subscribe((dataset) => { + this.newDatasetAdded.emit("added"); + shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira."); + }, (error) => { + shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset."); + }); //kraj addDataset subscribe }, (error) => { - + }); //kraj uploadData subscribe } diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.html b/frontend/src/app/_elements/dataset-load/dataset-load.component.html index 09487b3b..6ab58021 100644 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.html +++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.html @@ -30,7 +30,7 @@ </ul> </div> <div class="px-5 mt-5"> - <app-datatable [data]="datasetFile" [hasHeader]="datasetHasHeader"></app-datatable> + <app-datatable [tableData]="tableData"></app-datatable> </div> </div> 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 7329033c..dc78ab77 100644 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts +++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts @@ -3,7 +3,7 @@ import { AddNewDatasetComponent } from '../add-new-dataset/add-new-dataset.compo import { ModelsService } from 'src/app/_services/models.service'; import shared from 'src/app/Shared'; import Dataset from 'src/app/_data/Dataset'; -import { DatatableComponent } from 'src/app/_elements/datatable/datatable.component'; +import { DatatableComponent, TableData } from 'src/app/_elements/datatable/datatable.component'; import { DatasetsService } from 'src/app/_services/datasets.service'; import { CsvParseService } from 'src/app/_services/csv-parse.service'; import { Output, EventEmitter } from '@angular/core'; @@ -17,8 +17,8 @@ export class DatasetLoadComponent { @Output() selectedDatasetChangeEvent = new EventEmitter<Dataset>(); - @ViewChild(AddNewDatasetComponent) addNewDatasetComponent?: AddNewDatasetComponent; - @ViewChild(AddNewDatasetComponent) datatable?: DatatableComponent; + @ViewChild(AddNewDatasetComponent) addNewDatasetComponent!: AddNewDatasetComponent; + @ViewChild(AddNewDatasetComponent) datatable!: DatatableComponent; datasetLoaded: boolean = false; selectedDatasetLoaded: boolean = false; @@ -27,10 +27,8 @@ export class DatasetLoadComponent { myDatasets?: Dataset[]; existingDatasetSelected: boolean = false; selectedDataset?: Dataset; - otherDataset?: Dataset; - otherDatasetFile?: any[]; - datasetFile?: any[]; - datasetHasHeader?: boolean = true; + + tableData: TableData = new TableData(); term: string = ""; @@ -63,27 +61,20 @@ export class DatasetLoadComponent { this.selectedDataset = dataset; this.selectedDatasetLoaded = false; this.existingDatasetSelected = true; - this.datasetHasHeader = this.selectedDataset.hasHeader; + this.tableData.hasHeader = this.selectedDataset.hasHeader; + + this.tableData.hasInput = true; + this.tableData.loaded = false; this.datasets.getDatasetFile(dataset.fileId).subscribe((file: string | undefined) => { if (file) { - console.log(file); - this.datatable!.hasInput = true; - this.datatable!.loaded = true; - this.datasetFile = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter == "") ? "," : dataset.delimiter); - /*for (let i = this.datasetFile.length - 1; i >= 0; i--) { //moguce da je vise redova na kraju fajla prazno i sl. - if (this.datasetFile[i].length != this.datasetFile[0].length) - this.datasetFile[i].pop(); - else - break; //nema potrebe dalje - }*/ - console.log(this.datatable!.data); - console.log(this.datasetFile); - console.log(this.datatable!.hasInput); + this.tableData.loaded = true; + this.tableData.numRows = this.selectedDataset!.rowCount; + this.tableData.numCols = this.selectedDataset!.columnInfo.length; + this.tableData.data = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter == "") ? "," : dataset.delimiter); //this.resetCbsAndRbs(); //TREBA DA SE DESI //this.refreshThreeNullValueRadioOptions(); //TREBA DA SE DESI this.selectedDatasetLoaded = true; - //this.scrollToNextForm(); this.selectedDatasetChangeEvent.emit(this.selectedDataset); } @@ -91,17 +82,7 @@ export class DatasetLoadComponent { } resetSelectedDataset(): boolean { - const temp = this.selectedDataset; - this.selectedDataset = this.otherDataset; - this.otherDataset = temp; - const tempFile = this.datasetFile; - this.datasetFile = this.otherDatasetFile; - this.otherDatasetFile = tempFile; - this.selectedDatasetChangeEvent.emit(this.selectedDataset); - return true; } - - } diff --git a/frontend/src/app/_elements/datatable/datatable.component.html b/frontend/src/app/_elements/datatable/datatable.component.html index b6cbd303..8db62aff 100644 --- a/frontend/src/app/_elements/datatable/datatable.component.html +++ b/frontend/src/app/_elements/datatable/datatable.component.html @@ -1,39 +1,43 @@ -PRE IFA -{{hasInput}} -<div *ngIf="data && hasInput"> - PROSLO IF - <div class="table-responsive" style="height: 34rem; overflow: auto; border-radius: 5px;" class="mh-5"> - <div *ngIf="!loaded" style="background-color: #003459; width: 100%; height: 100%;" +<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"> <app-loading></app-loading> </div> - <div *ngIf="loaded"> - <table *ngIf="data.length > 0 && hasHeader && data[0].length > 0" class="table table-bordered table-light"> - <thead> - <tr> - <th *ngFor="let item of data[0]; let i = index">{{item}}</th> - </tr> - </thead> - <tbody> - <tr *ngFor="let row of data | slice:1"> - <td *ngFor="let col of row">{{col}}</td> - </tr> - </tbody> - </table> - - <table *ngIf="data.length > 0 && !hasHeader && data[0].length > 0" class="table table-bordered table-light"> - <tbody> - <tr *ngFor="let row of data"> - <td *ngFor="let col of row">{{col}}</td> - </tr> - </tbody> - </table> + <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"> + <thead> + <tr> + <th *ngFor="let item of tableData.data[0]; let i = index">{{item}}</th> + </tr> + </thead> + <tbody> + <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> </div> - - <div id="info" *ngIf="data.length > 0 && data[0].length > 0"> - <br> - <span *ngIf="hasHeader">{{data.length - 1}} x {{data[0].length}}</span> - <span *ngIf="!hasHeader">{{data.length}} x {{data[0].length}}</span> - </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 19fb204e..82374f4d 100644 --- a/frontend/src/app/_elements/datatable/datatable.component.ts +++ b/frontend/src/app/_elements/datatable/datatable.component.ts @@ -7,12 +7,7 @@ import { Component, Input, OnInit } from '@angular/core'; }) export class DatatableComponent implements OnInit { - @Input() hasHeader?: boolean = true; - - @Input() data?: any[] = []; - - hasInput = false; - loaded = false; + @Input() tableData!: TableData; constructor() { } @@ -20,3 +15,14 @@ export class DatatableComponent implements OnInit { } } + +export class TableData { + constructor( + public hasHeader = true, + public hasInput = false, + public loaded = false, + public numRows = 0, + public numCols = 0, + public data?: any[][] + ) { } +} diff --git a/frontend/src/app/_elements/item-dataset/item-dataset.component.css b/frontend/src/app/_elements/item-dataset/item-dataset.component.css index e69de29b..dc851671 100644 --- a/frontend/src/app/_elements/item-dataset/item-dataset.component.css +++ b/frontend/src/app/_elements/item-dataset/item-dataset.component.css @@ -0,0 +1,23 @@ +.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 index dc4221f4..11ff61c3 100644 --- a/frontend/src/app/_elements/item-dataset/item-dataset.component.html +++ b/frontend/src/app/_elements/item-dataset/item-dataset.component.html @@ -1,16 +1,41 @@ <div class="card" style="min-width: 12rem;"> - <div class="card-header"> - {{dataset.name}} +<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> - <div class="card-body overflow-hidden"> - <p class="card-text"> - {{dataset.description}} - </p> - <table class="table table-bordered table-sm"> - <thead> - <th scope="col" *ngFor="let column of dataset.header">{{column}}</th> - <!-- treba da se namesti da kad nema hedere, da korisnik unese nazive kolona pa da se taj heder prikaze --> - </thead> - </table> - </div> -</div>
\ No newline at end of file + <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 index e12de34d..44b95310 100644 --- a/frontend/src/app/_elements/item-dataset/item-dataset.component.ts +++ b/frontend/src/app/_elements/item-dataset/item-dataset.component.ts @@ -9,7 +9,32 @@ import Dataset from 'src/app/_data/Dataset'; 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.css b/frontend/src/app/_elements/item-experiment/item-experiment.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_elements/item-experiment/item-experiment.component.css diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.html b/frontend/src/app/_elements/item-experiment/item-experiment.component.html new file mode 100644 index 00000000..51fbfef3 --- /dev/null +++ b/frontend/src/app/_elements/item-experiment/item-experiment.component.html @@ -0,0 +1,10 @@ +<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.spec.ts b/frontend/src/app/_elements/item-experiment/item-experiment.component.spec.ts new file mode 100644 index 00000000..1da7d05d --- /dev/null +++ b/frontend/src/app/_elements/item-experiment/item-experiment.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ItemExperimentComponent } from './item-experiment.component'; + +describe('ItemExperimentComponent', () => { + let component: ItemExperimentComponent; + let fixture: ComponentFixture<ItemExperimentComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ItemExperimentComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemExperimentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/item-experiment/item-experiment.component.ts b/frontend/src/app/_elements/item-experiment/item-experiment.component.ts new file mode 100644 index 00000000..31900d35 --- /dev/null +++ b/frontend/src/app/_elements/item-experiment/item-experiment.component.ts @@ -0,0 +1,15 @@ +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 index e69de29b..5ea24c72 100644 --- a/frontend/src/app/_elements/item-model/item-model.component.css +++ b/frontend/src/app/_elements/item-model/item-model.component.css @@ -0,0 +1,23 @@ +.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 index 695c580e..447f023e 100644 --- a/frontend/src/app/_elements/item-model/item-model.component.html +++ b/frontend/src/app/_elements/item-model/item-model.component.html @@ -1,13 +1,58 @@ <div class="card" style="min-width: 12rem;"> - <div class="card-header"> - {{model.name}} + <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"> + <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"> - {{"Opis: "+ model.description}}<br> - {{"Datum kreiranja: " + model.dateCreated}}<br> - {{"Poslednje ažuriranje: " + model.lastUpdated}}<br> + {{model.description}} </p> - <app-graph [model]="model"></app-graph> + <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 index 7f85f43f..b837667b 100644 --- a/frontend/src/app/_elements/item-model/item-model.component.ts +++ b/frontend/src/app/_elements/item-model/item-model.component.ts @@ -9,10 +9,25 @@ import Model from 'src/app/_data/Model'; 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/model-load/model-load.component.html b/frontend/src/app/_elements/model-load/model-load.component.html index 0923c895..833b7181 100644 --- a/frontend/src/app/_elements/model-load/model-load.component.html +++ b/frontend/src/app/_elements/model-load/model-load.component.html @@ -85,18 +85,6 @@ <div class="row p-2"> <div class="col-1"> </div> - <div class="col-3"> - <label for="encoding" class="col-form-label">Enkoding: </label> - </div> - <div class="col-2"> - <select id=encodingOptions class="form-control" name="encoding" [(ngModel)]="newModel.encoding"> - <option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <div class="col-1"> </div> <div class="col-3"> @@ -149,43 +137,6 @@ </select> </div> <div class="col-1"></div> - <div class="col-3 mt-2"> - <label for="type" class="form-check-label">Nasumičan redosled podataka?</label> - <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="newModel.randomOrder" - type="checkbox" value="" checked> - </div> - </div> - <div class="border m-3"> - <div class="row p-2 m-2"> - <div class="col-4"> - <label for="splitYesNo" class="form-check-label"> - <h3>Podela test skupa: - <input id="splitYesNo" class="form-check-input" type="checkbox" - [checked]="newModel.randomTestSet" - (change)="newModel.randomTestSet = !newModel.randomTestSet"> - </h3> - </label> - </div> - <div class="col-8"> - trening - <mat-slider style="width: 85%;" min="10" max="90" step="10" value="10" - name="randomTestSetDistribution" thumbLabel [disabled]="!newModel.randomTestSet" - [(ngModel)]="tempTestSetDistribution"> - </mat-slider> - test - </div> - </div> - - <div class="row p-2 mx-2"> - <div class="col-4"> - <label for="percentage" class="form-label">Procenat podataka koji se uzima za trening - skup:</label> - </div> - <div class="col-2"> - <input id="percentage" type="number" class="form-control" min="10" max="90" step="10" value="90" - [(ngModel)]="tempTestSetDistribution" [disabled]="!newModel.randomTestSet"> - </div> - </div> </div> <h3>Aktivacione funkcije:</h3> 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 ca6b8ea5..abf19d75 100644 --- a/frontend/src/app/_elements/model-load/model-load.component.ts +++ b/frontend/src/app/_elements/model-load/model-load.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, ViewChild, Output, EventEmitter } from '@angular/core'; import Shared from 'src/app/Shared'; -import Model, { ActivationFunction, Encoding, LossFunction, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType } from 'src/app/_data/Model'; +import Model, { ActivationFunction, LossFunction, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType } from 'src/app/_data/Model'; import { ModelsService } from 'src/app/_services/models.service'; import { GraphComponent } from '../graph/graph.component'; @@ -20,7 +20,6 @@ export class ModelLoadComponent implements OnInit { selectedModel?: Model; ProblemType = ProblemType; - Encoding = Encoding; ActivationFunction = ActivationFunction; metrics: any = Metrics; LossFunction = LossFunction; @@ -32,7 +31,6 @@ export class ModelLoadComponent implements OnInit { term: string = ""; selectedProblemType: string = ''; selectedMetrics = []; - tempTestSetDistribution = 90; lossFunction: any = LossFunction; showMyModels: boolean = true; @@ -64,7 +62,6 @@ export class ModelLoadComponent implements OnInit { uploadModel() { //console.log(this.selectedModel); this.getMetrics(); - this.newModel.randomTestSetDistribution = 1 - Math.round(this.tempTestSetDistribution / 100 * 10) / 10; this.newModel.username = Shared.username; this.modelsService.addModel(this.newModel).subscribe((response) => { diff --git a/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.css b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.css diff --git a/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html new file mode 100644 index 00000000..06e74093 --- /dev/null +++ b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html @@ -0,0 +1,8 @@ +<h2 mat-dialog-title class="text-muted">{{data.title}}</h2> +<div mat-dialog-content class="mt-4" style="color: rgb(81, 76, 76);"> + {{data.message}} +</div> +<div mat-dialog-actions class="d-flex justify-content-center mt-4"> + <button mat-button cdkFocusInitial (click)="onYesClick()" style="background-color: lightgray;">Da</button> + <button mat-button cdkFocusInitial (click)="onNoClick()" style="background-color: lightgray;">Ne</button> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.spec.ts b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.spec.ts new file mode 100644 index 00000000..eecf6468 --- /dev/null +++ b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import {YesNoDialogComponent } from './yes-no-dialog.component'; + +describe('AlertDialogComponent', () => { + let component: YesNoDialogComponent; + let fixture: ComponentFixture<YesNoDialogComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ YesNoDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(YesNoDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.ts b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.ts new file mode 100644 index 00000000..de1cdd4f --- /dev/null +++ b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { Inject} from '@angular/core'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; + +interface DialogData { + title: string; + message: string; + yesFunction:Function; +} + +@Component({ + selector: 'app-yes-no-dialog', + templateUrl: './yes-no-dialog.component.html', + styleUrls: ['./yes-no-dialog.component.css'] +}) +export class YesNoDialogComponent { + + constructor( + public dialogRef: MatDialogRef<YesNoDialogComponent>, + @Inject(MAT_DIALOG_DATA) public data: DialogData, + //public dialog: MatDialog + ) {} + + onNoClick(): void { + this.dialogRef.close(); + } + onYesClick():void{ + this.data.yesFunction(); + this.dialogRef.close(); + } + + +} diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.css b/frontend/src/app/_pages/my-datasets/my-datasets.component.css index e69de29b..57889937 100644 --- a/frontend/src/app/_pages/my-datasets/my-datasets.component.css +++ b/frontend/src/app/_pages/my-datasets/my-datasets.component.css @@ -0,0 +1,8 @@ +#header { + background-color: #003459; + padding-top: 20px; + padding-bottom: 15px; + text-align: center; + color: white; + border-radius: 5px; +}
\ No newline at end of file diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.html b/frontend/src/app/_pages/my-datasets/my-datasets.component.html index d996bf31..0c83dc85 100644 --- a/frontend/src/app/_pages/my-datasets/my-datasets.component.html +++ b/frontend/src/app/_pages/my-datasets/my-datasets.component.html @@ -1,5 +1,8 @@ +<div id="header"> + <h1>Moji setovi podataka</h1> +</div> <div id="wrapper"> - <div id="container" class="container p-5" style="background-color: white; min-height: 100%;"> + <div id="container" class="container p-5" style="background-color: rgba(255, 255, 255, 0.8); min-height: 100%;"> <div class="row mt-3 mb-2 d-flex justify-content-center"> <div class="col-sm-6" style="margin-bottom: 10px;"> @@ -12,9 +15,10 @@ <div class="panel-footer row"><!-- panel-footer --> <div class="col-xs-6 text-center"> <div> - <button type="button" class="btn btn-default btn-lg" mat-raised-button color="primary" (click)="deleteThisDataset(dataset)">Obriši - <span class="glyphicon glyphicon-chevron-right"></span> - </button> + <div> + <button (click)="deleteThisDataset(dataset)" mat-raised-button color="warn" style="min-width: 10rem;float: right" ><mat-icon>delete</mat-icon></button> + </div> + </div> </div> </div><!-- end panel-footer --> @@ -24,6 +28,7 @@ <h2>Nema rezultata</h2> </div> </div> + </div> diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.ts b/frontend/src/app/_pages/my-datasets/my-datasets.component.ts index 1551a3c8..19a6832b 100644 --- a/frontend/src/app/_pages/my-datasets/my-datasets.component.ts +++ b/frontend/src/app/_pages/my-datasets/my-datasets.component.ts @@ -5,6 +5,7 @@ import Dataset from 'src/app/_data/Dataset'; import { JwtHelperService } from '@auth0/angular-jwt'; import { CookieService } from 'ngx-cookie-service'; import shared from 'src/app/Shared'; +import { share } from 'rxjs'; @Component({ selector: 'app-my-datasets', @@ -41,17 +42,17 @@ export class MyDatasetsComponent implements OnInit { */ deleteThisDataset(dataset: Dataset): void{ - console.log("OK"); + shared.openYesNoDialog('Brisanje seta podataka','Da li ste sigurni da želite da obrišete ovaj set podataka?',() => { this.datasetsS.deleteDataset(dataset).subscribe((response) => { console.log("OBRISANO JE", response); //na kraju uspesnog this.getAllMyDatasets(); }, (error) =>{ if (error.error == "Dataset with name = {name} deleted") { - alert("Greška pri brisanju dataseta!"); + shared.openDialog("Greška","Greška pri brisanju dataseta!"); } }); - + }); } getAllMyDatasets(): void{ @@ -61,5 +62,6 @@ deleteThisDataset(dataset: Dataset): void{ console.log(this.myDatasets); }); } + } diff --git a/frontend/src/app/_pages/my-models/my-models.component.css b/frontend/src/app/_pages/my-models/my-models.component.css index e69de29b..19d29595 100644 --- a/frontend/src/app/_pages/my-models/my-models.component.css +++ b/frontend/src/app/_pages/my-models/my-models.component.css @@ -0,0 +1,12 @@ +button{ + margin-left: 5%; + margin-right: 5%; +} +#header { + background-color: #003459; + padding-top: 20px; + padding-bottom: 15px; + text-align: center; + color: white; + border-radius: 5px; +}
\ No newline at end of file diff --git a/frontend/src/app/_pages/my-models/my-models.component.html b/frontend/src/app/_pages/my-models/my-models.component.html index 4aebc1f2..b0e9c4ef 100644 --- a/frontend/src/app/_pages/my-models/my-models.component.html +++ b/frontend/src/app/_pages/my-models/my-models.component.html @@ -1,5 +1,8 @@ +<div id="header"> + <h1>Moji modeli</h1> +</div> <div id="wrapper"> - <div id="container" class="container p-5" style="background-color: white; min-height: 100%;"> + <div id="container" class="container p-5" style="background-color: rgba(255, 255, 255, 0.8); min-height: 100%;"> <div class="row mt-3 mb-2 d-flex justify-content-center"> <div class="col-sm-6" style="margin-bottom: 10px;"> @@ -12,12 +15,12 @@ <div class="panel-footer row"><!-- panel-footer --> <div class="col-xs-6 text-center"> <div> - <button type="button" class="btn btn-default btn-lg" (click)="deleteThisModel(model)" mat-raised-button color="primary">Koristi - <span class="glyphicon glyphicon-search"></span> - </button> - <button type="button" class="btn btn-default btn-lg" mat-raised-button color="primary" (click)="deleteThisModel(model)">Obriši - <span class="glyphicon glyphicon-chevron-right"></span> - </button> + <button type="button" class="btn btn-default btn-lg"style="min-width: 7rem;float: left;" (click)="deleteThisModel(model)" mat-raised-button color="primary">Koristi + <span class="glyphicon glyphicon-search"></span> + </button> + <button (click)="deleteThisModel(model)" mat-raised-button color="warn" style="min-width: 7rem;float: right" ><mat-icon>delete</mat-icon></button> + + </div> </div> </div><!-- end panel-footer --> diff --git a/frontend/src/app/_pages/my-models/my-models.component.ts b/frontend/src/app/_pages/my-models/my-models.component.ts index 6086b1b1..f3877e2d 100644 --- a/frontend/src/app/_pages/my-models/my-models.component.ts +++ b/frontend/src/app/_pages/my-models/my-models.component.ts @@ -32,7 +32,7 @@ export class MyModelsComponent implements OnInit { */ deleteThisModel(model: Model): void{ - console.log("OK"); + shared.openYesNoDialog('Brisanje seta podataka','Da li ste sigurni da želite da obrišete model?',() => { this.modelsS.deleteModel(model).subscribe((response) => { console.log("OBRISANOOO JEE", response); //na kraju uspesnog @@ -42,9 +42,10 @@ deleteThisModel(model: Model): void{ shared.openDialog("Obaveštenje", "Greška prilikom brisanja modela."); } }); - + }); } + getAllMyModels(): void{ this.modelsS.getMyModels().subscribe(m => { diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.html b/frontend/src/app/_pages/my-predictors/my-predictors.component.html index 3746d35e..d38f93e4 100644 --- a/frontend/src/app/_pages/my-predictors/my-predictors.component.html +++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.html @@ -1,7 +1,7 @@ <div id="header"> <h1>Trenirani modeli</h1> </div> -<div id="container" style="background-color:rgba(255, 255, 255, 0.747);"> +<div id="container" style="background-color:rgba(255, 255, 255, 0.8);"> <div class="row" *ngFor="let predictor of predictors"> <div class="left"> <app-item-predictor [predictor]="predictor"></app-item-predictor> diff --git a/frontend/src/app/_services/experiments.service.ts b/frontend/src/app/_services/experiments.service.ts index 60d1bfb2..ecb3e262 100644 --- a/frontend/src/app/_services/experiments.service.ts +++ b/frontend/src/app/_services/experiments.service.ts @@ -15,4 +15,8 @@ export class ExperimentsService { addExperiment(experiment: Experiment): Observable<any> { return this.http.post(`${API_SETTINGS.apiURL}/experiment/add`, experiment, { headers: this.authService.authHeader() }); } + + getMyExperiments(): Observable<Experiment[]> { + return this.http.get<Experiment[]>(`${API_SETTINGS.apiURL}/experiment/getmyexperiments`, { headers: this.authService.authHeader() }); + } } diff --git a/frontend/src/app/_services/models.service.ts b/frontend/src/app/_services/models.service.ts index 3fbad109..9a1e71da 100644 --- a/frontend/src/app/_services/models.service.ts +++ b/frontend/src/app/_services/models.service.ts @@ -35,8 +35,8 @@ export class ModelsService { addDataset(dataset: Dataset): Observable<any> { return this.http.post(`${API_SETTINGS.apiURL}/dataset/add`, dataset, { headers: this.authService.authHeader() }); } - trainModel(model: Model): Observable<any> { - return this.http.post(`${API_SETTINGS.apiURL}/model/sendmodel`, model, { headers: this.authService.authHeader(), responseType: 'text' }); + trainModel(modelId: string, experimentId: string): Observable<any> { + return this.http.post(`${API_SETTINGS.apiURL}/model/trainmodel`, { ModelId: modelId, ExperimentId: experimentId }, { headers: this.authService.authHeader(), responseType: 'text' }); } getMyDatasets(): Observable<Dataset[]> { diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 1b91a1ac..f57441c0 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -45,6 +45,8 @@ import { AlertDialogComponent } from './_modals/alert-dialog/alert-dialog.compon import { AddNewDatasetComponent } from './_elements/add-new-dataset/add-new-dataset.component'; import { GraphComponent } from './_elements/graph/graph.component'; import { TrainingComponent } from './training/training.component'; +import { ItemExperimentComponent } from './_elements/item-experiment/item-experiment.component'; +import { YesNoDialogComponent } from './_modals/yes-no-dialog/yes-no-dialog.component'; @NgModule({ declarations: [ @@ -80,6 +82,8 @@ import { TrainingComponent } from './training/training.component'; AddNewDatasetComponent, GraphComponent, TrainingComponent, + ItemExperimentComponent, + YesNoDialogComponent ], imports: [ BrowserModule, diff --git a/frontend/src/app/experiment/experiment.component.html b/frontend/src/app/experiment/experiment.component.html index 42797579..36cf9eda 100644 --- a/frontend/src/app/experiment/experiment.component.html +++ b/frontend/src/app/experiment/experiment.component.html @@ -13,7 +13,7 @@ </div> <div id="carouselExampleControls" class="carousel slide" data-bs-wrap="false" data-bs-ride="carousel" data-bs-interval="false"> - <div class="carousel-inner border"> + <div class="carousel-inner"> <div class="carousel-item active"> <h2>1. Izvor podataka</h2> <app-dataset-load (selectedDatasetChangeEvent)="updateDataset($event)"></app-dataset-load> @@ -21,7 +21,16 @@ <div class="carousel-item"> <h2>2. Preprocesiranje</h2> - <h3>Biranje ulaznih i izlaznih kolona:</h3> + + <label for="name" class="col-form-label">Naziv eksperimenta:</label> + <input type="text" class="form-control mb-1" name="name" placeholder="Naziv..." [(ngModel)]="experiment.name"> + + <label for="desc" class="col-sm-2 col-form-label">Opis:</label> + <div> + <textarea class="form-control" name="desc" rows="3" [(ngModel)]="experiment.description"></textarea> + </div> + + <h3 class="mt-3">Biranje ulaznih i izlaznih kolona:</h3> <div *ngIf="selectedDataset"> <div class="row"> <div class="col d-flex justify-content-center"> @@ -31,8 +40,9 @@ <div *ngFor="let item of selectedDataset.columnInfo; let i = index"> <input class="form-check-input" type="checkbox" value="{{item.columnName}}" id="cb_{{item.columnName}}" name="cbsNew" - [checked]="this.selectedOutputColumnVal != item.columnName" - [disabled]="this.selectedOutputColumnVal == item.columnName"> + [checked]="experiment.outputColumn != item.columnName" + [disabled]="experiment.outputColumn == item.columnName" + (click)="checkedColumnsChanged(item, 0)"> <label class="form-check-label" for="cb_{{item.columnName}}"> {{item.columnName}} </label> @@ -44,8 +54,11 @@ <div id="divOutputs" class="form-check mt-2"> <br> <div *ngFor="let item of selectedDataset.columnInfo; let i = index"> - <input class="form-check-input" type="radio" value="{{item.columnName}}" id="rb_{{item.columnName}}" - name="rbsNew" (change)="this.selectedOutputColumnVal = item.columnName"> + <input class="form-check-input" type="radio" value="{{item.columnName}}" + id="rb_{{item.columnName}}" name="rbsNew" + [(ngModel)]="this.experiment.outputColumn" + (change)="experiment.outputColumn = item.columnName" + (click)="checkedColumnsChanged(item, 1);"> <label class="form-check-label" for="rb_{{item.columnName}}"> {{item.columnName}} </label> @@ -54,11 +67,12 @@ </div> </div> </div> + <br> <h3>Popunjavanje nedostajućih vrednosti:</h3> <div class="form-check" *ngIf="selectedDataset"> <input type="radio" [(ngModel)]="experiment.nullValues" [value]="NullValueOptions.DeleteRows" - class="form-check-input" value="deleteRows" name="fillMissing" id="delRows" checked data-bs-toggle="collapse" - data-bs-target="#fillMissingCustom.show"> + class="form-check-input" value="deleteRows" name="fillMissing" id="delRows" checked + data-bs-toggle="collapse" data-bs-target="#fillMissingCustom.show"> <label for="delRows" class="form-check-label">Obriši sve redove sa nedostajućim vrednostima ({{selectedDataset.nullRows}} / TODO)</label><br> <input type="radio" [(ngModel)]="experiment.nullValues" [value]="NullValueOptions.DeleteColumns" @@ -66,29 +80,38 @@ data-bs-target="#fillMissingCustom.show"> <label for="delCols" class="form-check-label">Obriši sve kolone sa nedostajućim vrednostima ({{selectedDataset.nullCols}} / TODO)</label><br> - <input type="radio" [(ngModel)]="experiment.nullValues" [value]="NullValueOptions.Replace" class="form-check-input" - name="fillMissing" id="replace" data-bs-toggle="collapse" data-bs-target="#fillMissingCustom:not(.show)"> + <input type="radio" [(ngModel)]="experiment.nullValues" [value]="NullValueOptions.Replace" + class="form-check-input" name="fillMissing" id="replace" data-bs-toggle="collapse" + data-bs-target="#fillMissingCustom:not(.show)"> <label for="replace" class="form-check-label">Izabraću vrednosti koje će da zamene nedostajuće vrednosti za svaku kolonu...</label><br><br> <div class="collapse" id="fillMissingCustom"> <div> <label for="columnReplacers" class="form-label">Unesite zamenu za svaku kolonu:</label> + <div class="my-3" *ngIf="getSelectedNullColumnsArray().length > 0" > + <label class="text-center form-control mx-3 text-secondary"> + Kolone <span style="font-style: italic;" *ngFor="let colname of getSelectedNullColumnsArray(); let i = index"> + <span *ngIf="i != getSelectedNullColumnsArray().length - 1">{{colname}}, </span> + <span *ngIf="i == getSelectedNullColumnsArray().length - 1">{{colname}} </span> + </span> + nemaju nedostajućih vrednosti za popunjavanje. + </label> + </div> <div id="columnReplacers"> - <div *ngFor="let column of selectedDataset.columnInfo; let i = index" class="my-3"> - <div *ngIf="getInputById('cb_'+column.columnName).checked || selectedOutputColumnVal == column.columnName" - class=""> + <div *ngFor="let column of selectedColumnsInfoArray; let i = index" class="my-3"> + <div *ngIf="column.numNulls > 0"> <span class="w-20 mx-3"> - {{column.columnName}} <span class="small" style="color:gray;">({{column.numNulls}} - null) + {{column.columnName}} <span class="small" style="color:gray;">({{column.numNulls}} null) </span> </span> - - <label *ngIf="column.numNulls <= 0" class="text-center form-control mx-3 text-secondary"> + + <label *ngIf="column.numNulls <= 0" + class="text-center form-control mx-3 text-secondary"> Ova kolona nema nedostajućih vrednosti. </label> - + <div *ngIf="column.numNulls > 0" class="d-flex flex-row justify-content-end"> <div class="flex-grow-3 mx-3 me-auto"> <div class="input-group"> @@ -100,20 +123,22 @@ </label> </div> <input type="text" class="form-control" [id]="'fillText_'+column.columnName" - (keyup)="checkFillColRadio(column.columnName)" placeholder="Unesi vrednost..."> - + (keyup)="checkFillColRadio(column.columnName)" + placeholder="Unesi vrednost..."> + <div class="input-group-append"> <select [id]="'replaceOptions'+i" class="form-control btn-primary" - *ngIf="column.isNumber" (change)="replace($event, column);"> + *ngIf="column.isNumber" (change)="replace($event, column); checkFillColRadio(column.columnName);"> <option *ngFor="let option of Object.keys(ReplaceWith); let optionName of Object.values(ReplaceWith)" [value]="option"> {{ optionName }} </option> </select> - <select [id]="'replaceOptions'+i" class="form-control btn-outline-primary" + <select [id]="'replaceOptions'+i" + class="form-control btn-outline-primary" *ngIf="!column.isNumber && column.numNulls > 0" - (change)="replace($event, column);"> + (change)="replace($event, column); checkFillColRadio(column.columnName);"> <option *ngFor="let option of column.uniqueValues" [value]="option"> {{ option }} </option> @@ -121,7 +146,7 @@ </div> </div> </div> - + <div class="flex-shrink-1 mx-3"> <div class="input-group"> <label class="form-control" [for]="'delCol_'+column.columnName">Izbriši @@ -131,7 +156,7 @@ (change)="emptyFillTextInput(column.columnName)"></label> </div> </div> - + <div class="flex-shrink-1 mx-3"> <div class="input-group"> <label class="form-control" [for]="'delRows_'+column.columnName">Izbriši @@ -196,6 +221,60 @@ </div> </div> + <div id="randomOptions"> + <div class="col-3 mt-2"> + <label for="type" class="form-check-label">Nasumičan redosled podataka?</label> + <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="experiment.randomOrder" + type="checkbox" value="" checked> + </div> + <div class="border m-3"> + <div class="row p-2 m-2"> + <div class="col-4"> + <label for="splitYesNo" class="form-check-label"> + <h3>Podela test skupa: + <input id="splitYesNo" class="form-check-input" type="checkbox" + [checked]="experiment.randomTestSet" + (change)="experiment.randomTestSet = !experiment.randomTestSet"> + </h3> + </label> + </div> + <div class="col-8"> + trening + <mat-slider style="width: 85%;" min="10" max="90" step="10" value="10" + name="randomTestSetDistribution" thumbLabel [disabled]="!experiment.randomTestSet" + [(ngModel)]="tempTestSetDistribution"> + </mat-slider> + test + </div> + </div> + + <div class="row p-2 mx-2"> + <div class="col-4"> + <label for="percentage" class="form-label">Procenat podataka koji se uzima za trening + skup:</label> + </div> + <div class="col-2"> + <input id="percentage" type="number" class="form-control" min="10" max="90" step="10" value="90" + [(ngModel)]="tempTestSetDistribution" [disabled]="!experiment.randomTestSet"> + </div> + </div> + </div> + </div> + + <div id="encodingForColumns"> + <div class="col-3"> + <label for="encoding" class="col-form-label">Enkoding: </label> + </div> + <div class="col-2"> + <select id=encodingOptions class="form-control" name="encoding" [(ngModel)]="experiment.encoding"> + <option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + </div> + </div> diff --git a/frontend/src/app/experiment/experiment.component.ts b/frontend/src/app/experiment/experiment.component.ts index 7ccca528..8a1b7d70 100644 --- a/frontend/src/app/experiment/experiment.component.ts +++ b/frontend/src/app/experiment/experiment.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import Experiment, { NullValReplacer, NullValueOptions, ReplaceWith } from '../_data/Experiment'; +import Experiment, { NullValReplacer, NullValueOptions, ReplaceWith, Encoding } from '../_data/Experiment'; import Model from '../_data/Model'; import Dataset, { ColumnInfo } from '../_data/Dataset'; import { ModelsService } from '../_services/models.service'; @@ -20,12 +20,15 @@ export class ExperimentComponent implements OnInit { NullValueOptions = NullValueOptions; ReplaceWith = ReplaceWith; + Encoding = Encoding; Object = Object; selectedColumnsInfoArray: ColumnInfo[] = []; - selectedOutputColumnVal: string = ''; + //selectedOutputColumnVal: string = ''; selectedNullColumnsArray: string[] = []; + tempTestSetDistribution = 90; + constructor(private modelsService: ModelsService, private experimentsService: ExperimentsService) { } ngOnInit(): void { @@ -36,12 +39,7 @@ export class ExperimentComponent implements OnInit { this.selectedDataset = dataset; this.selectedColumnsInfoArray = this.selectedDataset.columnInfo; this.selectedNullColumnsArray = []; - console.log("array:", this.selectedColumnsInfoArray); - } - - updateModel(model: Model) { - //console.log(model); - this.selectedModel = model; + //console.log("array:", this.selectedColumnsInfoArray); } getInputById(id: string): HTMLInputElement { @@ -71,9 +69,9 @@ export class ExperimentComponent implements OnInit { checkedColumnsChanged(checkedColumnInfo: ColumnInfo, buttonType: number) { //0-input,1-output let col = this.selectedColumnsInfoArray.find(x => x.columnName == checkedColumnInfo.columnName); if (buttonType == 0) { //inputCol - if (col == undefined) + if (col == undefined) this.selectedColumnsInfoArray.push(checkedColumnInfo); - else + else this.selectedColumnsInfoArray = this.selectedColumnsInfoArray.filter(x => x.columnName != checkedColumnInfo.columnName); } else { //outputCol @@ -81,7 +79,7 @@ export class ExperimentComponent implements OnInit { this.selectedColumnsInfoArray.push(checkedColumnInfo); } //console.log(this.selectedColumnsInfoArray); - } + } replace(event: Event, column: ColumnInfo) { let option = (<HTMLInputElement>event.target).value; @@ -170,21 +168,24 @@ export class ExperimentComponent implements OnInit { Shared.openDialog("Greška", "Molimo Vas da izaberete ulazne kolone."); return; } - + this.experiment._id = ''; this.experiment.uploaderId = ''; this.experiment.datasetId = this.selectedDataset._id; - + let pom = this.selectedColumnsInfoArray.filter(x => x.columnName != this.experiment.outputColumn); for (let i = 0; i < pom.length; i++) this.experiment.inputColumns.push(pom[i].columnName); - this.selectedColumnsInfoArray = this.selectedColumnsInfoArray.filter(x => x.numNulls > 0); - //TREBAJU MI NULLVALUESREPLACERI + //this.experiment.outputColumn = this.selectedOutputColumnVal; + + this.selectedColumnsInfoArray = this.selectedColumnsInfoArray.filter(x => x.numNulls > 0); //obavezno this.experiment.nullValuesReplacers = this.getNullValuesReplacersArray(); - console.log("Eksperiment:", this.experiment); - + this.experiment.randomTestSetDistribution = 1 - Math.round(this.tempTestSetDistribution / 100 * 10) / 10; + + //console.log("Eksperiment:", this.experiment); + this.experimentsService.addExperiment(this.experiment).subscribe((response) => { this.experiment = response; @@ -193,25 +194,9 @@ export class ExperimentComponent implements OnInit { Shared.openDialog("Obaveštenje", "Eksperiment je uspešno kreiran."); }, (error) => { - - }); - } - - trainModel() { - this.trainingResult = undefined; - //console.log('Training model...', this.selectedModel); - if (!this.selectedDataset) { - Shared.openDialog('Greška', 'Izvor podataka nije izabran!'); - return; - } - // TODO proveri nullValues - if (!this.selectedModel) { - Shared.openDialog('Greška', 'Model nije izabran!'); - return; - } - this.modelsService.trainModel(this.selectedModel).subscribe((response: any) => { - console.log('Train model complete!', response); - this.trainingResult = response; + if (error.error == "Experiment with this name exists") { + Shared.openDialog("Greška", "Eksperiment sa unetim nazivom već postoji u Vašoj kolekciji. Unesite neki drugi naziv."); + } }); } } diff --git a/frontend/src/app/training/training.component.css b/frontend/src/app/training/training.component.css index ee4b0448..490c56b5 100644 --- a/frontend/src/app/training/training.component.css +++ b/frontend/src/app/training/training.component.css @@ -29,7 +29,7 @@ border-color: #003459; } -.selectedDatasetClass { +.selectedExperimentClass { /*border-color: 2px solid #003459;*/ background-color: lightblue; } diff --git a/frontend/src/app/training/training.component.html b/frontend/src/app/training/training.component.html index 1939d3cf..2e574c12 100644 --- a/frontend/src/app/training/training.component.html +++ b/frontend/src/app/training/training.component.html @@ -4,13 +4,25 @@ <div id="wrapper"> <div id="container" class="container p-5" style="background-color: white; min-height: 100%;"> -<h2>1. Izaberi eksperiment</h2> -TODO +<h2>1. Izaberite eksperiment iz kolekcije</h2> +<div class="px-5 my-2"> + <input type="text" class="form-control" placeholder="Pretraga" + [(ngModel)]="term"> +</div> +<div class="overflow-auto" style="max-height: 500px;"> + <ul class="list-group"> + <li class="list-group-item p-3" *ngFor="let experiment of myExperiments|filter:term" + [ngClass]="{'selectedExperimentClass': this.selectedExperiment == experiment}"> + <app-item-experiment [experiment]="experiment" + (click)="selectThisExperiment(experiment);"></app-item-experiment> + </li> + </ul> +</div> -<h2>2.Izaberi model</h2> +<h2>2.Izaberite model</h2> <app-model-load (selectedModelChangeEvent)="selectModel($event)"></app-model-load> -<h2>3. Treniraj model</h2> +<h2>3. Trenirajte model</h2> <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="trainModel();">Treniraj model</button> diff --git a/frontend/src/app/training/training.component.ts b/frontend/src/app/training/training.component.ts index cb6c304c..4f20bc87 100644 --- a/frontend/src/app/training/training.component.ts +++ b/frontend/src/app/training/training.component.ts @@ -1,29 +1,55 @@ import { Component, OnInit } from '@angular/core'; +import Shared from '../Shared'; import Experiment from '../_data/Experiment'; import Model from '../_data/Model'; +import { DatasetsService } from '../_services/datasets.service'; +import { ExperimentsService } from '../_services/experiments.service'; +import { ModelsService } from '../_services/models.service'; @Component({ selector: 'app-training', templateUrl: './training.component.html', styleUrls: ['./training.component.css'] }) -export class TrainingComponent implements OnInit { - - constructor() { } - - ngOnInit(): void { - } +export class TrainingComponent{ + myExperiments?: Experiment[]; selectedExperiment?: Experiment; selectedModel?: Model; trainingResult: any; - selectModel($model: Model) { + term: string = ""; + + constructor(private modelsService: ModelsService, private datasetsService: DatasetsService, private experimentsService: ExperimentsService) { + this.experimentsService.getMyExperiments().subscribe((experiments) => { + this.myExperiments = experiments; + console.log(this.myExperiments); + }); + } + + selectThisExperiment(experiment: Experiment) { + this.selectedExperiment = experiment; + } + selectModel(model: Model) { + this.selectedModel = model; } trainModel() { - //eksperiment i model moraju da budu izabrani + this.trainingResult = undefined; + + if (this.selectedExperiment == undefined) { + Shared.openDialog("Greška", "Molimo Vas da izaberete eksperiment iz kolekcije."); + return; + } + if (this.selectedModel == undefined) { + Shared.openDialog("Greška", "Molimo Vas da izaberete model."); + return; + } + this.modelsService.trainModel(this.selectedModel._id, this.selectedExperiment._id).subscribe((response: any) => { + console.log('Train model complete!', response); + this.trainingResult = response; + }); } } |