aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--backend/api/api/.gitignore1
-rw-r--r--backend/api/api/Controllers/DatasetController.cs78
-rw-r--r--backend/api/api/Controllers/FileController.cs24
-rw-r--r--backend/api/api/Controllers/ModelController.cs91
-rw-r--r--backend/api/api/Controllers/PredictorController.cs143
-rw-r--r--backend/api/api/Models/ColumnEncoding.cs14
-rw-r--r--backend/api/api/Models/ColumnInfo.cs14
-rw-r--r--backend/api/api/Models/Dataset.cs2
-rw-r--r--backend/api/api/Models/Experiment.cs3
-rw-r--r--backend/api/api/Models/Model.cs2
-rw-r--r--backend/api/api/Models/Predictor.cs38
-rw-r--r--backend/api/api/Program.cs14
-rw-r--r--backend/api/api/Properties/launchSettings.json46
-rw-r--r--backend/api/api/Services/DatasetService.cs28
-rw-r--r--backend/api/api/Services/FillAnEmptyDb.cs262
-rw-r--r--backend/api/api/Services/IDatasetService.cs12
-rw-r--r--backend/api/api/Services/IMlConnectionService.cs3
-rw-r--r--backend/api/api/Services/IModelService.cs11
-rw-r--r--backend/api/api/Services/IPredictorService.cs25
-rw-r--r--backend/api/api/Services/MlApiCheckActionFilter.cs50
-rw-r--r--backend/api/api/Services/MlConnectionService.cs24
-rw-r--r--backend/api/api/Services/ModelService.cs26
-rw-r--r--backend/api/api/Services/PredictorService.cs28
-rw-r--r--backend/api/api/Services/TempRemovalService.cs6
-rw-r--r--backend/api/api/Services/UserService.cs12
-rw-r--r--backend/api/api/UploadedFiles/Igrannonica/iris.csv151
-rw-r--r--backend/api/api/api.csproj1
-rw-r--r--backend/api/api/appsettings.json42
-rw-r--r--backend/microservice/api/config.py2
-rw-r--r--backend/microservice/api/controller.py94
-rw-r--r--backend/microservice/api/newmlservice.py217
-rw-r--r--frontend/package-lock.json243
-rw-r--r--frontend/package.json5
-rw-r--r--frontend/src/app/_data/Dataset.ts2
-rw-r--r--frontend/src/app/_data/Experiment.ts13
-rw-r--r--frontend/src/app/_data/Model.ts10
-rw-r--r--frontend/src/app/_data/Notification.ts1
-rw-r--r--frontend/src/app/_data/Predictor.ts3
-rw-r--r--frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html50
-rw-r--r--frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts11
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.html30
-rw-r--r--frontend/src/app/_elements/dataset-load/dataset-load.component.ts37
-rw-r--r--frontend/src/app/_elements/item-predictor/item-predictor.component.html35
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.css0
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.html5
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/line-chart/line-chart.component.ts88
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.css0
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.html5
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.spec.ts25
-rw-r--r--frontend/src/app/_elements/metric-view/metric-view.component.ts49
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.html76
-rw-r--r--frontend/src/app/_elements/model-load/model-load.component.ts48
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.html34
-rw-r--r--frontend/src/app/_elements/navbar/navbar.component.ts8
-rw-r--r--frontend/src/app/_elements/notifications/notifications.component.html16
-rw-r--r--frontend/src/app/_elements/notifications/notifications.component.ts26
-rw-r--r--frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html6
-rw-r--r--frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts2
-rw-r--r--frontend/src/app/_pages/home/home.component.html1
-rw-r--r--frontend/src/app/_pages/my-models/my-models.component.html2
-rw-r--r--frontend/src/app/_pages/my-models/my-models.component.ts8
-rw-r--r--frontend/src/app/_pages/my-predictors/my-predictors.component.html27
-rw-r--r--frontend/src/app/_pages/my-predictors/my-predictors.component.ts38
-rw-r--r--frontend/src/app/_services/auth.service.ts7
-rw-r--r--frontend/src/app/_services/datasets.service.ts4
-rw-r--r--frontend/src/app/_services/models.service.ts13
-rw-r--r--frontend/src/app/_services/predictors.service.ts2
-rw-r--r--frontend/src/app/_services/signal-r.service.ts11
-rw-r--r--frontend/src/app/app-routing.module.ts2
-rw-r--r--frontend/src/app/app.component.ts1
-rw-r--r--frontend/src/app/app.module.ts13
-rw-r--r--frontend/src/app/experiment/experiment.component.css5
-rw-r--r--frontend/src/app/experiment/experiment.component.html355
-rw-r--r--frontend/src/app/experiment/experiment.component.ts50
-rw-r--r--frontend/src/app/mixed-chart/mixed-chart.component.css0
-rw-r--r--frontend/src/app/mixed-chart/mixed-chart.component.html2
-rw-r--r--frontend/src/app/mixed-chart/mixed-chart.component.spec.ts25
-rw-r--r--frontend/src/app/mixed-chart/mixed-chart.component.ts56
-rw-r--r--frontend/src/app/point-linechart/point-linechart.component.css0
-rw-r--r--frontend/src/app/point-linechart/point-linechart.component.html2
-rw-r--r--frontend/src/app/point-linechart/point-linechart.component.spec.ts25
-rw-r--r--frontend/src/app/point-linechart/point-linechart.component.ts57
-rw-r--r--frontend/src/app/training/training.component.html63
-rw-r--r--frontend/src/app/training/training.component.ts64
86 files changed, 2077 insertions, 1077 deletions
diff --git a/.gitignore b/.gitignore
index afaf09d3..bdd28b3f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,7 @@ backend/microservice/temp/
backend/microservice/api/__pycache__/
production/app/node_modules/
production/app/dist/
+backend/microservice/api/temp/
+backend/microservice/Boston.csv
+backend/microservice/diamonds.csv
+backend/microservice/IMDB-Movie-Data.csv
diff --git a/backend/api/api/.gitignore b/backend/api/api/.gitignore
index 17288e4c..3c45979c 100644
--- a/backend/api/api/.gitignore
+++ b/backend/api/api/.gitignore
@@ -4,6 +4,7 @@
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
##Ignore contents for UploadedFiles Folder
+PredictorFiles/*
UploadedFiles/*
!UploadedFiles/Igrannonica
TempFiles/*
diff --git a/backend/api/api/Controllers/DatasetController.cs b/backend/api/api/Controllers/DatasetController.cs
index add85aba..0a9fe0bd 100644
--- a/backend/api/api/Controllers/DatasetController.cs
+++ b/backend/api/api/Controllers/DatasetController.cs
@@ -25,22 +25,23 @@ namespace api.Controllers
_fileService = fileService;
jwtToken = Token;
}
- public string getUsername()
+
+ public string getUserId()
{
- string username;
+ string userId;
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)
+ userId = jwtToken.TokenToId(parameter);
+ if (userId == null)
return null;
}
else
return null;
- return username;
+ return userId;
}
// GET: api/<DatasetController>/mydatasets
@@ -48,17 +49,17 @@ namespace api.Controllers
[Authorize(Roles = "User,Guest")]
public ActionResult<List<Dataset>> Get()
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- if (username == "")
+ if (userId == "")
return _datasetService.GetGuestDatasets();
//ako bude trebao ID, samo iz baze uzeti
- return _datasetService.GetMyDatasets(username);
+ return _datasetService.GetMyDatasets(userId);
}
// GET: api/<DatasetController>/datesort/{ascdsc}/{latest}
@@ -69,12 +70,12 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult<List<Dataset>> SortDatasets(bool ascdsc, int latest)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- List<Dataset> lista = _datasetService.SortDatasets(username, ascdsc, latest);
+ List<Dataset> lista = _datasetService.SortDatasets(userId, ascdsc, latest);
if (latest == 0)
return lista;
@@ -100,14 +101,7 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult<List<Dataset>> Search(string name)
{
- string username = getUsername();
-
- if (username == null)
- return BadRequest();
-
- //ako bude trebao ID, samo iz baze uzeti
-
- return _datasetService.SearchDatasets(name, username);
+ return _datasetService.SearchDatasets(name);
}
@@ -117,12 +111,12 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult<Dataset> Get(string name)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- var dataset = _datasetService.GetOneDataset(username, name);
+ var dataset = _datasetService.GetOneDataset(userId, name);
if (dataset == null)
return NotFound($"Dataset with name = {name} not found or dataset is not public or not preprocessed");
@@ -134,25 +128,15 @@ namespace api.Controllers
[Authorize(Roles = "User,Guest")]
public async Task<ActionResult<Dataset>> Post([FromBody] Dataset dataset)
{
- 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
- return BadRequest();
+ string uploaderId = getUserId();
+
//da li ce preko tokena da se ubaci username ili front salje
//dataset.username = usernameToken;
//username = "" ako je GUEST DODAO
- var existingDataset = _datasetService.GetOneDataset(dataset.username, dataset.name);
+ var existingDataset = _datasetService.GetOneDataset(dataset.uploaderId, dataset.name);
if (existingDataset != null)
- return NotFound($"Dateset with name = {dataset.name} exisits");
+ return NotFound($"Dataset with this name already exists");
else
{
FileModel fileModel = _fileService.getFile(dataset.fileId);
@@ -169,20 +153,20 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult Put(string name, [FromBody] Dataset dataset)
{
- string username = getUsername();
+ string uploaderId = getUserId();
- if (username == null)
+ if (uploaderId == null)
return BadRequest();
- var existingDataset = _datasetService.GetOneDataset(username, name);
+ var existingDataset = _datasetService.GetOneDataset(uploaderId, name);
//ne mora da se proverava
if (existingDataset == null)
- return NotFound($"Dataset with name = {name} or user with username = {username} not found");
+ return NotFound($"Dataset with name = {name} or user with ID = {uploaderId} not found");
dataset.lastUpdated = DateTime.UtcNow;
- _datasetService.Update(username, name, dataset);
+ _datasetService.Update(uploaderId, name, dataset);
return Ok($"Dataset with name = {name} updated");
}
@@ -192,17 +176,17 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult Delete(string name)
{
- string username = getUsername();
+ string uploaderId = getUserId();
- if (username == null)
+ if (uploaderId == null)
return BadRequest();
- var dataset = _datasetService.GetOneDataset(username, name);
+ var dataset = _datasetService.GetOneDataset(uploaderId, name);
if (dataset == null)
- return NotFound($"Dataset with name = {name} or user with username = {username} not found");
+ return NotFound($"Dataset with name = {name} or user with ID = {uploaderId} not found");
- _datasetService.Delete(dataset.username, dataset.name);
+ _datasetService.Delete(dataset.uploaderId, dataset.name);
return Ok($"Dataset with name = {name} deleted");
diff --git a/backend/api/api/Controllers/FileController.cs b/backend/api/api/Controllers/FileController.cs
index b9b31500..68d2ebed 100644
--- a/backend/api/api/Controllers/FileController.cs
+++ b/backend/api/api/Controllers/FileController.cs
@@ -44,27 +44,13 @@ namespace api.Controllers
}
[HttpPost("h5")]
- [Authorize(Roles = "User,Guest")]
- public async Task<ActionResult<string>> H5Upload([FromForm] IFormFile file)
+ public async Task<ActionResult<string>> H5Upload([FromForm] IFormFile file,[FromForm] string uploaderId)
{
//get username from jwtToken
- string folderName;
-
- string uploaderId = getUserId();
+ string folderName="PredictorFiles";
- if (uploaderId == null)
- return BadRequest();
-
- if (uploaderId == "")
- {
- folderName = "TempFiles";
- }
- else
- {
- folderName = "UploadedFiles";
- }
//Check filetype
@@ -75,7 +61,7 @@ namespace api.Controllers
{
return BadRequest("Wrong file type");
}
- var folderPath = Path.Combine(Directory.GetCurrentDirectory(), folderName, uploaderId);
+ var folderPath = Path.Combine(Directory.GetCurrentDirectory(), folderName,uploaderId);
//Check Directory
if (!Directory.Exists(folderPath))
{
@@ -98,14 +84,14 @@ namespace api.Controllers
await file.CopyToAsync(stream);
}
FileModel fileModel = new FileModel();
- fileModel.type = "h5";
+ fileModel.type = ".h5";
fileModel.path = fullPath;
fileModel.uploaderId = uploaderId;
fileModel.date = DateTime.Now.ToUniversalTime();
fileModel = _fileservice.Create(fileModel);
- return Ok(fileModel);
+ return Ok(fileModel._id);
}
[HttpGet("csvread/{hasHeader}/{fileId}")]
diff --git a/backend/api/api/Controllers/ModelController.cs b/backend/api/api/Controllers/ModelController.cs
index c31b24e1..fb30a7a2 100644
--- a/backend/api/api/Controllers/ModelController.cs
+++ b/backend/api/api/Controllers/ModelController.cs
@@ -56,23 +56,6 @@ namespace api.Controllers
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")]
@@ -81,28 +64,29 @@ namespace api.Controllers
string experimentId = trainModelObject.ExperimentId;
string modelId = trainModelObject.ModelId;
- string uploaderId = getUserId();
+ string userId = getUserId();
- if (uploaderId == null)
+ if (userId == null)
return BadRequest();
var experiment=_experimentService.Get(experimentId);
var dataset = _datasetService.GetOneDataset(experiment.datasetId);
- var filepath = _fileService.GetFilePath(dataset.fileId, uploaderId);
+ var filepath = _fileService.GetFilePath(dataset.fileId, userId);
var model = _modelService.GetOneModel(modelId);
- _mlService.TrainModel(model,experiment,filepath,dataset,uploaderId);//To do Obavestiti korisnika kada se model istrenira
+ _mlService.TrainModel(model,experiment,filepath,dataset, userId);//To do Obavestiti korisnika kada se model istrenira
return Ok();
}
[HttpPost("epoch")]
+ [ServiceFilter(typeof(MlApiCheckActionFilter))]
public async Task<ActionResult<string>> Epoch([FromBody] Epoch info)
{
var model=_modelService.GetOneModel(info.ModelId);
- var user = _userService.GetUserByUsername(model.username);
+ var user = _userService.GetUserById(model.uploaderId);
if (ChatHub.CheckUser(user._id))
- await _ichat.Clients.Client(ChatHub.Users[user._id]).SendAsync("NotifyEpoch",info.ModelId,info.Stat,model.epochs);
+ await _ichat.Clients.Client(ChatHub.Users[user._id]).SendAsync("NotifyEpoch",model.name,info.ModelId,info.Stat,model.epochs,info.EpochNum);
return Ok();
}
@@ -117,12 +101,30 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult<List<Model>> Get()
{
- string username = getUsername();
-
- if (username == null)
+ string uploaderId = getUserId();
+
+ if (uploaderId == null)
return BadRequest();
- return _modelService.GetMyModels(username);
+ return _modelService.GetMyModels(uploaderId);
+ }
+
+ // GET: api/<ModelController>/mymodels
+ [HttpGet("mymodelsbytype/{problemtype}")]
+ [Authorize(Roles = "User")]
+ public ActionResult<List<Model>> GetMyModelsByType(string problemType)
+ {
+ string uploaderId = getUserId();
+
+ if (uploaderId == null)
+ return BadRequest();
+
+ List<Model> modeli = _modelService.GetMyModelsByType(uploaderId, problemType);
+
+ if (modeli == null)
+ return NoContent();
+ else
+ return modeli;
}
// vraca svoj model prema nekom imenu
@@ -131,12 +133,12 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult<Model> Get(string name)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- var model = _modelService.GetOneModel(username, name);
+ var model = _modelService.GetOneModel(userId, name);
if (model == null)
return NotFound($"Model with name = {name} not found");
@@ -155,14 +157,14 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult<List<Model>> GetLatestModels(int latest)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
//ako bude trebao ID, samo iz baze uzeti
- List<Model> lista = _modelService.GetLatestModels(username);
+ List<Model> lista = _modelService.GetLatestModels(userId);
List<Model> novaLista = new List<Model>();
@@ -185,7 +187,7 @@ namespace api.Controllers
/*if (_modelService.CheckHyperparameters(1, model.hiddenLayerNeurons, model.hiddenLayers, model.outputNeurons) == false)
return BadRequest("Bad parameters!");*/
- var existingModel = _modelService.GetOneModel(model.username, model.name);
+ var existingModel = _modelService.GetOneModel(model.uploaderId, model.name);
if (existingModel != null && !overwrite)
return NotFound($"Model with name = {model.name} exisits");
@@ -209,18 +211,18 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult Put(string name, [FromBody] Model model)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- var existingModel = _modelService.GetOneModel(username, name);
+ var existingModel = _modelService.GetOneModel(userId, name);
if (existingModel == null)
- return NotFound($"Model with name = {name} or user with username = {username} not found");
+ return NotFound($"Model with name = {name} or user with ID = {userId} not found");
- _modelService.Update(username, name, model);
+ _modelService.Update(userId, name, model);
return NoContent();
}
@@ -229,17 +231,17 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult Delete(string name)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- var model = _modelService.GetOneModel(username, name);
+ var model = _modelService.GetOneModel(userId, name);
if (model == null)
- return NotFound($"Model with name = {name} or user with username = {username} not found");
+ return NotFound($"Model with name = {name} or user with ID = {userId} not found");
- _modelService.Delete(model.username, model.name);
+ _modelService.Delete(model.uploaderId, model.name);
return Ok($"Model with name = {name} deleted");
@@ -256,6 +258,7 @@ namespace api.Controllers
public class Epoch
{
public string ModelId { get; set; }
+ public int EpochNum { get; set; }
public string Stat { get; set; }
}
}
diff --git a/backend/api/api/Controllers/PredictorController.cs b/backend/api/api/Controllers/PredictorController.cs
index 233ea401..dd5aa5fd 100644
--- a/backend/api/api/Controllers/PredictorController.cs
+++ b/backend/api/api/Controllers/PredictorController.cs
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
using System.Net.Http.Headers;
using System.Diagnostics;
+using Microsoft.AspNetCore.SignalR;
namespace api.Controllers
{
@@ -15,30 +16,38 @@ namespace api.Controllers
private readonly IPredictorService _predictorService;
private IJwtToken jwtToken;
private readonly IMlConnectionService _mlConnectionService;
+ private readonly IExperimentService _experimentService;
+ private readonly IUserService _userService;
+ private readonly IHubContext<ChatHub> _ichat;
+ private readonly IModelService _modelService;
- public PredictorController(IPredictorService predictorService, IConfiguration configuration, IJwtToken Token, IMlConnectionService mlConnectionService)
+ public PredictorController(IPredictorService predictorService, IConfiguration configuration, IJwtToken Token, IMlConnectionService mlConnectionService, IExperimentService experimentService,IUserService userService, IHubContext<ChatHub> ichat,IModelService modelService)
{
_predictorService = predictorService;
jwtToken = Token;
_mlConnectionService = mlConnectionService;
+ _experimentService = experimentService;
+ _userService = userService;
+ _ichat = ichat;
+ _modelService = modelService;
}
- public string getUsername()
+ public string getUserId()
{
- string username;
+ string uploaderId;
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)
+ uploaderId = jwtToken.TokenToId(parameter);
+ if (uploaderId == null)
return null;
}
else
return null;
- return username;
+ return uploaderId;
}
// GET: api/<PredictorController>/mypredictors
@@ -46,12 +55,12 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult<List<Predictor>> Get()
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- return _predictorService.GetMyPredictors(username);
+ return _predictorService.GetMyPredictors(userId);
}
// GET: api/<PredictorController>/publicpredictors
@@ -63,29 +72,30 @@ namespace api.Controllers
//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 = getUsername();
+
+ //[HttpGet("search/{name}")]
+ //[Authorize(Roles = "User")]
+ //public ActionResult<List<Predictor>> Search(string name)
+ //{
+ // string username = getUsername();
- if (username == null)
- return BadRequest();
+ // if (username == null)
+ // return BadRequest();
- return _predictorService.SearchPredictors(name, username);
- }
+ // return _predictorService.SearchPredictors(name, username);
+ //}
// GET api/<PredictorController>/getpredictor/{name}
[HttpGet("getpredictor/{id}")]
[Authorize(Roles = "User,Guest")]
public ActionResult<Predictor> GetPredictor(string id)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- Predictor predictor = _predictorService.GetPredictor(username, id);
+ Predictor predictor = _predictorService.GetPredictor(userId, id);
if (predictor == null)
return NotFound($"Predictor with id = {id} not found");
@@ -96,17 +106,18 @@ namespace api.Controllers
// GET api/<PredictorController>/{name}
[HttpGet("{name}")]
[Authorize(Roles = "User")]
- public ActionResult<Predictor> Get(string name)
+ public ActionResult<Predictor> Get(string id)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- var predictor = _predictorService.GetOnePredictor(username, name);
+ //treba userId da se salje GetOnePredictor
+ var predictor = _predictorService.GetOnePredictor(id);
if (predictor == null)
- return NotFound($"Predictor with name = {name} not found or predictor is not public");
+ return NotFound($"Predictor with id = {id} not found or predictor is not public");
return predictor;
}
@@ -120,12 +131,12 @@ namespace api.Controllers
[Authorize(Roles = "User")]
public ActionResult<List<Predictor>> SortPredictors(bool ascdsc, int latest)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- List<Predictor> lista = _predictorService.SortPredictors(username, ascdsc, latest);
+ List<Predictor> lista = _predictorService.SortPredictors(userId, ascdsc, latest);
if(latest == 0)
return lista;
@@ -142,77 +153,83 @@ namespace api.Controllers
// POST api/<PredictorController>/add
[HttpPost("add")]
- [Authorize(Roles = "User")]
- public ActionResult<Predictor> Post([FromBody] Predictor predictor)
+ public async Task<ActionResult<Predictor>> Post([FromBody] Predictor predictor)
{
- var existingPredictor = _predictorService.GetOnePredictor(predictor.username, predictor.name);
-
- if (existingPredictor != null)
- return NotFound($"Predictor with name = {predictor.name} exisits");
- else
- {
- _predictorService.Create(predictor);
-
- return CreatedAtAction(nameof(Get), new { id = predictor._id }, predictor);
- }
+ var user=_userService.GetUserById(predictor.uploaderId);
+ predictor.dateCreated = DateTime.Now.ToUniversalTime();
+ var model = _modelService.GetOneModel(predictor.modelId);
+ if (model == null || user==null)
+ return BadRequest("Model not found or user doesnt exist");
+ _predictorService.Create(predictor);
+ if (ChatHub.CheckUser(user._id))
+ await _ichat.Clients.Client(ChatHub.Users[user._id]).SendAsync("NotifyPredictor", predictor._id,model.name);
+ return CreatedAtAction(nameof(Get), new { id = predictor._id }, predictor);
+
}
// POST api/<PredictorController>/usepredictor {predictor,inputs}
[HttpPost("usepredictor/{id}")]
[Authorize(Roles = "User,Guest")]
- public ActionResult UsePredictor(String id, [FromBody] PredictorColumns[] inputs)
+ public async Task<ActionResult> UsePredictor(String id, [FromBody] PredictorColumns[] inputs)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- Predictor predictor = _predictorService.GetPredictor(username, id);
-
+ Predictor predictor = _predictorService.GetPredictor(userId, id);
+
+ Experiment e = _experimentService.Get(predictor.experimentId);
+
+ string result = await _mlConnectionService.Predict(predictor, e, inputs);
+
+ //salji ml
+
+ /*
foreach(PredictorColumns i in inputs)
- Debug.WriteLine(i.value.ToString());
- return NoContent();
+ Debug.WriteLine(i.value.ToString());*/
+ return Ok(result);
}
// PUT api/<PredictorController>/{name}
[HttpPut("{name}")]
[Authorize(Roles = "User")]
- public ActionResult Put(string name, [FromBody] Predictor predictor)
+ public ActionResult Put(string id, [FromBody] Predictor predictor)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- var existingPredictor = _predictorService.GetOnePredictor(username, name);
+ var existingPredictor = _predictorService.GetOnePredictor(id);
//ne mora da se proverava
if (existingPredictor == null)
- return NotFound($"Predictor with name = {name} or user with username = {username} not found");
+ return NotFound($"Predictor with id = {id} or user with ID = {userId} not found");
- _predictorService.Update(username, name, predictor);
+ _predictorService.Update(id, predictor);
- return Ok($"Predictor with name = {name} updated");
+ return Ok($"Predictor with id = {id} updated");
}
// DELETE api/<PredictorController>/name
- [HttpDelete("{name}")]
+ [HttpDelete("{id}")]
[Authorize(Roles = "User")]
- public ActionResult Delete(string name)
+ public ActionResult Delete(string id)
{
- string username = getUsername();
+ string userId = getUserId();
- if (username == null)
+ if (userId == null)
return BadRequest();
- var predictor = _predictorService.GetOnePredictor(username, name);
+ var predictor = _predictorService.GetOnePredictor(id);
if (predictor == null)
- return NotFound($"Predictor with name = {name} or user with username = {username} not found");
+ return NotFound($"Predictor with id = {id} or user with ID = {userId} not found");
- _predictorService.Delete(predictor.username, predictor.name);
+ _predictorService.Delete(id, userId);
- return Ok($"Predictor with name = {name} deleted");
+ return Ok($"Predictor with id = {id} deleted");
}
}
diff --git a/backend/api/api/Models/ColumnEncoding.cs b/backend/api/api/Models/ColumnEncoding.cs
new file mode 100644
index 00000000..2a2fce8b
--- /dev/null
+++ b/backend/api/api/Models/ColumnEncoding.cs
@@ -0,0 +1,14 @@
+namespace api.Models
+{
+ public class ColumnEncoding
+ {
+ public ColumnEncoding(string columnName, string encoding)
+ {
+ this.columnName = columnName;
+ this.encoding = encoding;
+ }
+
+ public string columnName { get; set; }
+ public string encoding { get; set; }
+ }
+}
diff --git a/backend/api/api/Models/ColumnInfo.cs b/backend/api/api/Models/ColumnInfo.cs
index 99418732..04450fef 100644
--- a/backend/api/api/Models/ColumnInfo.cs
+++ b/backend/api/api/Models/ColumnInfo.cs
@@ -2,6 +2,20 @@
{
public class ColumnInfo
{
+ public ColumnInfo() { }
+
+ public ColumnInfo(string columnName, bool isNumber, int numNulls, float mean, float min, float max, float median, string[] uniqueValues)
+ {
+ this.columnName = columnName;
+ this.isNumber = isNumber;
+ this.numNulls = numNulls;
+ this.mean = mean;
+ this.min = min;
+ this.max = max;
+ this.median = median;
+ this.uniqueValues = uniqueValues;
+ }
+
public string columnName { get; set; }
public bool isNumber { get; set; }
public int numNulls { get; set; }
diff --git a/backend/api/api/Models/Dataset.cs b/backend/api/api/Models/Dataset.cs
index 47814449..cc7185f0 100644
--- a/backend/api/api/Models/Dataset.cs
+++ b/backend/api/api/Models/Dataset.cs
@@ -7,7 +7,7 @@ namespace api.Models
public class Dataset
{
public Dataset() { }
- public string username { get; set; }
+ public string uploaderId { get; set; }
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]//mongo data type to .net
diff --git a/backend/api/api/Models/Experiment.cs b/backend/api/api/Models/Experiment.cs
index bf029116..f7bec083 100644
--- a/backend/api/api/Models/Experiment.cs
+++ b/backend/api/api/Models/Experiment.cs
@@ -10,7 +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 string type { get; set; }
public List<string> ModelIds { get; set; }
public string datasetId { get; set; }
public string uploaderId { get; set; }
@@ -21,6 +21,7 @@ namespace api.Models
public float randomTestSetDistribution { get; set; }
public string nullValues { get; set; }
public NullValues[] nullValuesReplacers { get; set; }
+ public ColumnEncoding[] encodings { get; set; }
}
}
diff --git a/backend/api/api/Models/Model.cs b/backend/api/api/Models/Model.cs
index 72ee093b..a9dbfbdd 100644
--- a/backend/api/api/Models/Model.cs
+++ b/backend/api/api/Models/Model.cs
@@ -9,7 +9,7 @@ namespace api.Models
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]//mongo data type to .net
public string _id { get; set; }
- public string username { get; set; }
+ public string uploaderId { get; set; }
public string name { get; set; }
diff --git a/backend/api/api/Models/Predictor.cs b/backend/api/api/Models/Predictor.cs
index b1d6444b..342c5b5d 100644
--- a/backend/api/api/Models/Predictor.cs
+++ b/backend/api/api/Models/Predictor.cs
@@ -9,25 +9,37 @@ namespace api.Models
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]//mongo data type to .net
public string _id { get; set; }
- public string username { get; set; }
- public string name { get; set; }
- public string description { get; set; }
+ public string uploaderId { get; set; }
+ //public string name { get; set; }
+ //public string description { get; set; }
public string[] inputs { get; set; }
public string output { get; set; }
public bool isPublic { get; set; }
public bool accessibleByLink { get; set; }
public DateTime dateCreated { get; set; }
public string experimentId { get; set; }
-
+ public string modelId { get; set; }
+ public string h5FileId { get; set; }
+ public Metric[] metrics { get; set; }
}
+
+ public class Metric
+ {
+ string Name { get; set; }
+ string JsonValue { get; set; }
+
+ }
}
-/*
+/**
+* Paste one or more documents here
+
{
- "_id" : "",
- "username" : "ivan123",
- "name" : "Neki prediktor",
- "description" : "Neki opis prediktora koji je unet tamo",
+ "_id": {
+ "$oid": "625dc348b7856ace8a6f8702"
+
+ },
+ "uploaderId" : "6242ea59486c664208d4255c",
"inputs": ["proba",
"proba2",
"proba3"
@@ -36,6 +48,8 @@ namespace api.Models
"isPublic" : true,
"accessibleByLink" : true,
"dateCreated" : "2022-04-11T20:33:26.937+00:00",
- "experimentId" : "Neki id eksperimenta"
-}
-*/ \ No newline at end of file
+ "experimentId" : "Neki id eksperimenta",
+ "modelId" : "Neki id eksperimenta",
+ "h5FileId" : "Neki id eksperimenta",
+ "metrics" : [{ }]
+}*/ \ No newline at end of file
diff --git a/backend/api/api/Program.cs b/backend/api/api/Program.cs
index 576093fe..56abc016 100644
--- a/backend/api/api/Program.cs
+++ b/backend/api/api/Program.cs
@@ -39,7 +39,14 @@ builder.Services.AddScoped<IExperimentService, ExperimentService>();
builder.Services.AddHostedService<TempFileService>();
builder.Services.AddHostedService<FillAnEmptyDb>();
-
+//Ml Api Ip Filter
+builder.Services.AddScoped<MlApiCheckActionFilter>(container =>
+{
+ var loggerFactory = container.GetRequiredService<ILoggerFactory>();
+ var logger=loggerFactory.CreateLogger<MlApiCheckActionFilter>();
+ var MlIp = builder.Configuration.GetValue<string>("AppSettings:MlIp");
+ return new MlApiCheckActionFilter(MlIp, logger);
+});
@@ -64,11 +71,12 @@ builder.Services.Configure<FormOptions>(x =>
builder.Services.AddSignalR();
builder.Services.AddControllers();
-
+string frontApi = builder.Configuration.GetValue<string>("AppSettings:FrontApi");
+string mlApi = builder.Configuration.GetValue<string>("AppSettings:MlApi");
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder
- .WithOrigins("http://localhost:4200")
+ .WithOrigins(frontApi, mlApi)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
diff --git a/backend/api/api/Properties/launchSettings.json b/backend/api/api/Properties/launchSettings.json
index 5c9dd2a8..f6c3fe8b 100644
--- a/backend/api/api/Properties/launchSettings.json
+++ b/backend/api/api/Properties/launchSettings.json
@@ -1,31 +1,23 @@
{
- "$schema": "https://json.schemastore.org/launchsettings.json",
- "iisSettings": {
- "windowsAuthentication": false,
- "anonymousAuthentication": true,
- "iisExpress": {
- "applicationUrl": "http://localhost:61918",
- "sslPort": 0
- }
- },
- "profiles": {
- "api": {
- "commandName": "Project",
- "dotnetRunMessages": true,
- "launchBrowser": false,
- "launchUrl": "",
- "applicationUrl": "http://localhost:5283",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:61918",
+ "sslPort": 0
+ }
},
- "IIS Express": {
- "commandName": "IISExpress",
- "launchBrowser": false,
- "launchUrl": "",
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
- }
+ "profiles": {
+ "api": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "launchUrl": "",
+ "applicationUrl": "http://localhost:5283",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
}
- }
}
diff --git a/backend/api/api/Services/DatasetService.cs b/backend/api/api/Services/DatasetService.cs
index 176ab424..f260a1ba 100644
--- a/backend/api/api/Services/DatasetService.cs
+++ b/backend/api/api/Services/DatasetService.cs
@@ -14,7 +14,7 @@ namespace api.Services
_dataset = database.GetCollection<Dataset>(settings.DatasetCollectionName);
}
- public List<Dataset> SearchDatasets(string name, string username)
+ public List<Dataset> SearchDatasets(string name)
{
return _dataset.Find(dataset => dataset.name == name && dataset.isPublic == true && dataset.isPreProcess).ToList();
}
@@ -27,27 +27,27 @@ namespace api.Services
}
//brisanje odredjenog name-a
- public void Delete(string username, string name)
+ public void Delete(string userId, string id)
{
- _dataset.DeleteOne(dataset => (dataset.username == username && dataset.name == name));
+ _dataset.DeleteOne(dataset => (dataset.uploaderId == userId && dataset._id == id));
}
- public List<Dataset> GetMyDatasets(string username)
+ public List<Dataset> GetMyDatasets(string userId)
{
- return _dataset.Find(dataset => dataset.username == username && dataset.isPreProcess).ToList();
+ return _dataset.Find(dataset => dataset.uploaderId == userId && dataset.isPreProcess).ToList();
}
public List<Dataset> GetGuestDatasets()
{
//Join Igranonica public datasetove sa svim temp uploadanim datasetovima
- List<Dataset> datasets= _dataset.Find(dataset => dataset.username == "Igrannonica" && dataset.isPublic == true && dataset.isPreProcess).ToList();
- datasets.AddRange(_dataset.Find(dataset => dataset.username == "" && dataset.isPreProcess).ToList());
+ List<Dataset> datasets= _dataset.Find(dataset => dataset.uploaderId == "Igrannonica" && dataset.isPublic == true && dataset.isPreProcess).ToList();
+ datasets.AddRange(_dataset.Find(dataset => dataset.uploaderId == "" && dataset.isPreProcess).ToList());
return datasets;
}
//poslednji datasetovi
- public List<Dataset> SortDatasets(string username, bool ascdsc, int latest)
+ public List<Dataset> SortDatasets(string userId, bool ascdsc, int latest)
{
- List<Dataset> list = _dataset.Find(dataset => dataset.username == username && dataset.isPreProcess).ToList();
+ List<Dataset> list = _dataset.Find(dataset => dataset.uploaderId == userId && dataset.isPreProcess).ToList();
if(ascdsc)
list = list.OrderBy(dataset => dataset.lastUpdated).ToList();
@@ -62,9 +62,9 @@ namespace api.Services
return _dataset.Find(dataset => dataset.isPublic == true && dataset.isPreProcess).ToList();
}
- public Dataset GetOneDataset(string username, string name)
+ public Dataset GetOneDataset(string userId, string name)
{
- return _dataset.Find(dataset => dataset.username == username && dataset.name == name && dataset.isPreProcess).FirstOrDefault();
+ return _dataset.Find(dataset => dataset.uploaderId == userId && dataset.name == name && dataset.isPreProcess).FirstOrDefault();
}
//odraditi za pretragu getOne
@@ -74,9 +74,9 @@ namespace api.Services
}
//ako je potrebno da se zameni name ili ekstenzija
- public void Update(string username, string name, Dataset dataset )
+ public void Update(string userId, string id, Dataset dataset )
{
- _dataset.ReplaceOne(dataset => dataset.username == username && dataset.name == name, dataset);
+ _dataset.ReplaceOne(dataset => dataset.uploaderId == userId && dataset._id == id, dataset);
}
public void Update(Dataset dataset)
{
@@ -85,7 +85,7 @@ namespace api.Services
public string GetDatasetId(string fileId)
{
- Dataset dataset = _dataset.Find(dataset => dataset.fileId == fileId && dataset.username == "Igrannonica").FirstOrDefault();
+ Dataset dataset = _dataset.Find(dataset => dataset.fileId == fileId && dataset.uploaderId == "Igrannonica").FirstOrDefault();
return dataset._id;
}
diff --git a/backend/api/api/Services/FillAnEmptyDb.cs b/backend/api/api/Services/FillAnEmptyDb.cs
index 33c1ada6..6d5683bd 100644
--- a/backend/api/api/Services/FillAnEmptyDb.cs
+++ b/backend/api/api/Services/FillAnEmptyDb.cs
@@ -1,5 +1,6 @@
using api.Interfaces;
using api.Models;
+using Microsoft.AspNetCore.SignalR;
using MongoDB.Driver;
namespace api.Services
@@ -32,10 +33,10 @@ namespace api.Services
if (_fileService.CheckDb())
{
- /*
+
FileModel file = new FileModel();
- string folderName = "UploadedFiles/Igrannonica";
+ string folderName = "UploadedFiles";
var folderPath = Path.Combine(Directory.GetCurrentDirectory(), folderName, "Igrannonica");
var fullPath = Path.Combine(folderPath, "titanic.csv");
@@ -51,9 +52,9 @@ namespace api.Services
Dataset dataset = new Dataset();
dataset._id = "";
- dataset.username = "Igrannonica";
+ dataset.uploaderId = "Igrannonica";
dataset.name = "Titanik dataset";
- dataset.description = "Opis dataseta 1";
+ dataset.description = "Titanik dataset";
dataset.header = new string[] { "PassengerId", "Survived", "Pclass", "Name", "Sex", "Age", "SibSp", "Parch", "Ticket", "Fare", "Cabin", "Embarked" };
dataset.fileId = _fileService.GetFileId(fullPath);
dataset.extension = ".csv";
@@ -64,9 +65,25 @@ namespace api.Services
dataset.delimiter = "";
dataset.hasHeader = true;
dataset.columnInfo = new ColumnInfo[] { };
+ dataset.columnInfo = new[]
+ {
+ new ColumnInfo( "PassengerId", true, 0, 446, 1, 891, 446, new string[]{ }),
+ new ColumnInfo( "Survived", true, 0, 0.38383838534355164f, 0, 1, 0, new string[]{ }),
+ new ColumnInfo( "Pclass", true, 0, 2.3086419105529785f, 1, 3, 3, new string[]{ }),
+ new ColumnInfo( "Name", false, 0, 0, 0, 0, 0, new string[]{"Braund, Mr. Owen Harris", "Boulos, Mr. Hanna", "Frolicher-Stehli, Mr. Maxmillian", "Gilinski, Mr. Eliezer", "Murdlin, Mr. Joseph", "Rintamaki, Mr. Matti", "Stephenson, Mrs. Walter Bertram (Martha Eustis)", "Elsbury, Mr. William James", "Bourke, Miss. Mary", "Chapman, Mr. John Henry"}),
+ new ColumnInfo( "Sex", false, 0, 0, 0, 0, 0, new string[]{ "male", "female" }),
+ new ColumnInfo( "Age", true, 177, 29.69911766052246f, 0.41999998688697815f, 80, 28, new string[]{ }),
+ new ColumnInfo( "SibSp", true, 0, 0.523007869720459f, 0, 8, 0, new string[]{ }),
+ new ColumnInfo( "Parch", true, 0, 0.3815937042236328f, 0, 6, 0, new string[]{ }),
+ new ColumnInfo( "Ticket", false, 0, 0, 0, 0, 0, new string[]{ "347082", "CA. 2343", "1601", "3101295", "CA 2144", "347088", "S.O.C. 14879", "382652", "LINE", "PC 17757" }),
+ new ColumnInfo( "Fare", true, 0, 32.20420837402344f, 0, 512.3292236328125f, 14.45419979095459f, new string[]{ }),
+ new ColumnInfo( "Cabin", false, 687, 0, 0, 0, 0, new string[]{ "B96 B98", "G6", "C23 C25 C27", "C22 C26", "F33", "F2", "E101", "D", "C78", "C93" }),
+ new ColumnInfo( "Embarked", false, 2, 0.3815937042236328f, 0, 6, 0, new string[]{ "S", "C", "Q" }),
+ };
+ dataset.rowCount = 891;
dataset.nullCols = 3;
dataset.nullRows = 708;
- dataset.isPreProcess = false;
+ dataset.isPreProcess = true;
_datasetService.Create(dataset);
@@ -74,25 +91,22 @@ namespace api.Services
Model model = new Model();
model._id = "";
- model.username = "Igrannonica";
- model.name = "Titanik model";
- model.description = "Opis modela 1";
+ model.uploaderId = "Igrannonica";
+ model.name = "Model Titanik";
+ model.description = "Model Titanik";
model.dateCreated = DateTime.Now;
model.lastUpdated = DateTime.Now;
- model.experimentId = "";
- model.type = "regresioni";
- model.encoding = "label";
+ model.type = "binarni-klasifikacioni";
model.optimizer = "Adam";
model.lossFunction = "mean_squared_error";
- model.hiddenLayerNeurons = 1;
- model.hiddenLayers = 1;
- model.batchSize = 5;
+ model.hiddenLayerNeurons = 3;
+ model.hiddenLayers = 5;
+ model.batchSize = 8;
model.outputNeurons = 0;
- model.hiddenLayerActivationFunctions = new string[] { "sigmoid" };
+ model.hiddenLayerActivationFunctions = new string[] { "relu", "relu", "relu", "relu", "relu" };
model.outputLayerActivationFunction = "sigmoid";
model.metrics = new string[] { };
model.epochs = 5;
- model.isTrained = false;
_modelService.Create(model);
@@ -100,18 +114,35 @@ namespace api.Services
Experiment experiment = new Experiment();
experiment._id = "";
+ experiment.name = "Eksperiment Titanik";
+ experiment.description = "Binarno klasifikacioni, label";
+ experiment.ModelIds = new string[] { }.ToList();
experiment.datasetId = _datasetService.GetDatasetId(dataset.fileId);
experiment.uploaderId = "Igrannonica";
- experiment.inputColumns = new string[] { };
- experiment.outputColumn = "";
- experiment.randomOrder = false;
- experiment.randomTestSet = false;
- experiment.randomTestSetDistribution = 0;
- experiment.nullValues = "";
+ experiment.inputColumns = new string[] { "Embarked" };
+ experiment.outputColumn = "Survived";
+ experiment.randomOrder = true;
+ experiment.randomTestSet = true;
+ experiment.randomTestSetDistribution = 0.30000001192092896f;
+ experiment.nullValues = "delete_rows";
experiment.nullValuesReplacers = new NullValues[] { };
+ experiment.encodings = new[]
+ {
+ new ColumnEncoding( "Survived", "label" ),
+ new ColumnEncoding("Embarked", "label" )
+ };
_experimentService.Create(experiment);
+ var experiment1 = _experimentService.Get(experiment._id);
+ var dataset1 = _datasetService.GetOneDataset(experiment.datasetId);
+ var filepath1 = _fileService.GetFilePath(dataset.fileId, "Igrannonica");
+ var model1 = _modelService.GetOneModel(model._id);
+
+
+ //_mlService.TrainModel(model1, experiment1, filepath1, dataset1, "Igrannonica");
+
+ /*
Predictor predictor = new Predictor();
@@ -127,7 +158,7 @@ namespace api.Services
predictor.experimentId = "0";
//izmeni experiment id
- _predictorService.Create(predictor);
+ _predictorService.Create(predictor);*/
//--------------------------------------------------------------------
@@ -142,14 +173,13 @@ namespace api.Services
_fileService.Create(file);
-
+
dataset = new Dataset();
dataset._id = "";
- dataset.username = "Igrannonica";
+ dataset.uploaderId = "Igrannonica";
dataset.name = "Diamonds dataset";
- dataset.description = "Opis dataseta 2";
- dataset.header = new string[] { "carat", "cut", "color", "clarity", "depth", "table", "price", "x", "y", "z" };
+ dataset.description = "Diamonds dataset";
dataset.fileId = _fileService.GetFileId(fullPath);
dataset.extension = ".csv";
dataset.isPublic = true;
@@ -157,57 +187,91 @@ namespace api.Services
dataset.dateCreated = DateTime.Now;
dataset.lastUpdated = DateTime.Now;
dataset.delimiter = "";
- dataset.hasHeader = true;
- dataset.columnInfo = new ColumnInfo[] { };
+ dataset.hasHeader = true;
+ dataset.columnInfo = new[]
+ {
+ new ColumnInfo( "Unnamed: 0", true, 0, 26969.5f, 0, 53939, 26969.5f, new string[]{ }),
+ new ColumnInfo( "carat", true, 0, 0.7979397773742676f, 0.20000000298023224f, 5.010000228881836f, 0.699999988079071f, new string[]{ }),
+ new ColumnInfo( "cut", false, 0, 0, 0, 0, 0, new string[]{ "Ideal", "Premium", "Very Good", "Good", "Fair" }),
+ new ColumnInfo( "color", false, 0, 0, 0, 0, 0, new string[]{"G", "E", "F", "H", "D", "I", "I", "J"}),
+ new ColumnInfo( "clarity", false, 0, 0, 0, 0, 0, new string[]{ "SI1", "VS2","SI2", "VS1", "VVS2", "VVS1", "IF", "I1" }),
+ new ColumnInfo( "depth", true, 0, 61.74940490722656f, 43, 79, 61.79999923706055f, new string[]{ }),
+ new ColumnInfo( "table", true, 0, 57.457183837890625f, 43, 95, 57, new string[]{ }),
+ new ColumnInfo( "price", true, 0, 3932.7998046875f, 326, 18823, 2401, new string[]{ }),
+ new ColumnInfo( "x", true, 0, 5.731157302856445f, 0, 10.739999771118164f, 5.699999809265137f, new string[]{ }),
+ new ColumnInfo( "y", true, 0, 5.73452615737915f, 0, 58.900001525878906f, 5.710000038146973f, new string[]{ }),
+ new ColumnInfo( "z", true, 0, 3.538733720779419f, 0, 31.799999237060547f, 3.5299999713897705f, new string[]{ })
+ };
+ dataset.rowCount = 53940;
dataset.nullCols = 0;
dataset.nullRows = 0;
- dataset.isPreProcess = false;
+ dataset.isPreProcess = true;
_datasetService.Create(dataset);
- */
- /*
+
+
model = new Model();
model._id = "";
- model.username = "Igrannonica";
- model.name = "Igrannonica model 2";
- model.description = "Opis modela 2";
+ model.uploaderId = "Igrannonica";
+ model.name = "Diamonds model";
+ model.description = "Diamonds model";
model.dateCreated = DateTime.Now;
model.lastUpdated = DateTime.Now;
- model.experimentId = "";
- model.type = "";
- model.encoding = "";
- model.optimizer = "";
- model.lossFunction = "";
- model.hiddenLayerNeurons = 0;
- model.hiddenLayers = 0;
- model.batchSize = 0;
+ model.type = "regresioni";
+ model.optimizer = "Adam";
+ model.lossFunction = "mean_absolute_error";
+ model.hiddenLayerNeurons = 2;
+ model.hiddenLayers = 4;
+ model.batchSize = 5;
model.outputNeurons = 0;
- model.hiddenLayerActivationFunctions = new string[] { "sigmoid" };
- model.outputLayerActivationFunction = "";
+ model.hiddenLayerActivationFunctions = new string[] { "relu", "relu", "relu", "relu" };
+ model.outputLayerActivationFunction = "relu";
model.metrics = new string[] { };
- model.epochs = 0;
- model.isTrained = false;
+ model.epochs = 5;
_modelService.Create(model);
-
+
experiment = new Experiment();
experiment._id = "";
+ experiment.name = "Diamonds eksperiment";
+ experiment.description = "Diamonds eksperiment";
+ experiment.ModelIds = new string[] { }.ToList();
experiment.datasetId = _datasetService.GetDatasetId(dataset.fileId);
experiment.uploaderId = "Igrannonica";
- experiment.inputColumns = new string[] { };
- experiment.outputColumn = "";
- experiment.randomOrder = false;
- experiment.randomTestSet = false;
- experiment.randomTestSetDistribution = 0;
- experiment.nullValues = "";
- experiment.nullValuesReplacers = new NullValues[] { };
-
+ experiment.inputColumns = new string[] { "Unnamed: 0", "carat", "cut", "color", "clarity", "depth", "table", "x", "y", "z" };
+ experiment.outputColumn = "price";
+ experiment.randomOrder = true;
+ experiment.randomTestSet = true;
+ experiment.randomTestSetDistribution = 0.30000001192092896f;
+ experiment.nullValues = "delete_rows";
+ experiment.nullValuesReplacers = new NullValues[] { };
+ experiment.encodings = new[]
+ {
+ new ColumnEncoding( "Unnamed: 0", "label" ),
+ new ColumnEncoding( "carat", "label" ),
+ new ColumnEncoding( "cut", "label" ),
+ new ColumnEncoding( "color", "label" ),
+ new ColumnEncoding( "clarity", "label" ),
+ new ColumnEncoding( "depth", "label" ),
+ new ColumnEncoding( "table", "label" ),
+ new ColumnEncoding( "price", "label" ),
+ new ColumnEncoding( "x", "label" ),
+ new ColumnEncoding( "y", "label" ),
+ new ColumnEncoding( "z", "label" )
+ };
+
_experimentService.Create(experiment);
+ experiment1 = _experimentService.Get(experiment._id);
+ dataset1 = _datasetService.GetOneDataset(experiment.datasetId);
+ filepath1 = _fileService.GetFilePath(dataset.fileId, "Igrannonica");
+ model1 = _modelService.GetOneModel(model._id);
+ //_mlService.TrainModel(model1, experiment1, filepath1, dataset1, "Igrannonica");
+ /*
predictor = new Predictor();
@@ -224,12 +288,12 @@ namespace api.Services
//izmeni experiment id
_predictorService.Create(predictor);
-
+ */
//--------------------------------------------------------------------
file = new FileModel();
- fullPath = Path.Combine(folderPath, "IMDB-Movie-Data.csv");
+ fullPath = Path.Combine(folderPath, "iris.csv");
file._id = "";
file.type = ".csv";
file.uploaderId = "Igrannonica";
@@ -242,10 +306,9 @@ namespace api.Services
dataset = new Dataset();
dataset._id = "";
- dataset.username = "Igrannonica";
- dataset.name = "Igrannonica dataset 3";
- dataset.description = "Opis dataseta 3";
- dataset.header = new string[] { "PassengerId", "Survived", "Pclass", "Name", "Sex", "Age", "SibSp", "Parch", "Ticket", "Fare", "Cabin", "Embarked" };
+ dataset.uploaderId = "Igrannonica";
+ dataset.name = "Iris dataset";
+ dataset.description = "Iris dataset";
dataset.fileId = _fileService.GetFileId(fullPath);
dataset.extension = ".csv";
dataset.isPublic = true;
@@ -254,56 +317,77 @@ namespace api.Services
dataset.lastUpdated = DateTime.Now;
dataset.delimiter = "";
dataset.hasHeader = true;
- dataset.columnInfo = new ColumnInfo[] { };
- dataset.nullCols = 0;
+ dataset.columnInfo = new[]
+ {
+ new ColumnInfo( "sepal_length", true, 0, 5.8433332443237305f, 4.300000190734863f, 7.900000095367432f, 5.800000190734863f, new string[]{ }),
+ new ColumnInfo( "sepal_width", true, 0, 3.053999900817871f, 2, 4.400000095367432f, 3, new string[]{ }),
+ new ColumnInfo( "petal_length", true, 0, 3.758666753768921f, 1, 6.900000095367432f, 4.349999904632568f, new string[]{ }),
+ new ColumnInfo( "petal_width", true, 0, 1.1986666917800903f, 0.10000000149011612f, 2.5f, 1.2999999523162842f, new string[]{}),
+ new ColumnInfo( "class", false, 0, 0, 0, 0, 0, new string[]{ "Iris-setosa", "Iris-versicolor", "Iris-virginica" }),
+ };
+ dataset.nullCols = 150;
dataset.nullRows = 0;
- dataset.isPreProcess = false;
+ dataset.isPreProcess = true;
_datasetService.Create(dataset);
-
+
model = new Model();
model._id = "";
- model.username = "Igrannonica";
- model.name = "Igrannonica model 3";
- model.description = "Opis modela 3";
+ model.uploaderId = "Igrannonica";
+ model.name = "Model Iris";
+ model.description = "Model Iris";
model.dateCreated = DateTime.Now;
model.lastUpdated = DateTime.Now;
- model.experimentId = "";
- model.type = "";
- model.encoding = "";
- model.optimizer = "";
- model.lossFunction = "";
- model.hiddenLayerNeurons = 0;
- model.hiddenLayers = 0;
- model.batchSize = 0;
+ model.type = "multi-klasifikacioni";
+ model.optimizer = "Adam";
+ model.lossFunction = "sparse_categorical_crossentropy";
+ model.hiddenLayerNeurons = 3;
+ model.hiddenLayers = 3;
+ model.batchSize = 4;
model.outputNeurons = 0;
- model.hiddenLayerActivationFunctions = new string[] { "sigmoid" };
- model.outputLayerActivationFunction = "";
+ model.hiddenLayerActivationFunctions = new string[] { "relu", "relu", "softmax" };
+ model.outputLayerActivationFunction = "softmax";
model.metrics = new string[] { };
- model.epochs = 0;
- model.isTrained = false;
+ model.epochs = 1;
_modelService.Create(model);
-
+
experiment = new Experiment();
experiment._id = "";
+ experiment.name = "Iris eksperiment";
+ experiment.description = "Iris eksperiment";
+ experiment.ModelIds = new string[] { }.ToList();
experiment.datasetId = _datasetService.GetDatasetId(dataset.fileId);
experiment.uploaderId = "Igrannonica";
- experiment.inputColumns = new string[] { };
- experiment.outputColumn = "";
- experiment.randomOrder = false;
- experiment.randomTestSet = false;
- experiment.randomTestSetDistribution = 0;
- experiment.nullValues = "";
- experiment.nullValuesReplacers = new NullValues[] { };
+ experiment.inputColumns = new string[] { "sepal_length", "sepal_width", "petal_length", "petal_width" };
+ experiment.outputColumn = "class";
+ experiment.randomOrder = true;
+ experiment.randomTestSet = true;
+ experiment.randomTestSetDistribution = 0.20000000298023224f;
+ experiment.nullValues = "delete_rows";
+ experiment.nullValuesReplacers = new NullValues[] { };
+ experiment.encodings = new[]
+ {
+ new ColumnEncoding( "sepal_length", "label" ),
+ new ColumnEncoding("sepal_width", "label" ),
+ new ColumnEncoding( "petal_length", "label" ),
+ new ColumnEncoding( "petal_width", "label" ),
+ new ColumnEncoding( "class", "label" )
+ };
_experimentService.Create(experiment);
+ experiment1 = _experimentService.Get(experiment._id);
+ dataset1 = _datasetService.GetOneDataset(experiment.datasetId);
+ filepath1 = _fileService.GetFilePath(dataset.fileId, "Igrannonica");
+ model1 = _modelService.GetOneModel(model._id);
+ //_mlService.TrainModel(model1, experiment1, filepath1, dataset1, "Igrannonica");
+ /*
predictor = new Predictor();
predictor._id = "";
diff --git a/backend/api/api/Services/IDatasetService.cs b/backend/api/api/Services/IDatasetService.cs
index c6138943..f493a2ec 100644
--- a/backend/api/api/Services/IDatasetService.cs
+++ b/backend/api/api/Services/IDatasetService.cs
@@ -5,15 +5,15 @@ namespace api.Services
{
public interface IDatasetService
{
- Dataset GetOneDataset(string username, string name);
+ Dataset GetOneDataset(string userId, string name);
Dataset GetOneDataset(string id);
- List<Dataset> SearchDatasets(string name, string username);
- List<Dataset> GetMyDatasets(string username);
- List<Dataset> SortDatasets(string username, bool ascdsc, int latest);
+ List<Dataset> SearchDatasets(string name);
+ List<Dataset> GetMyDatasets(string userId);
+ List<Dataset> SortDatasets(string userId, bool ascdsc, int latest);
List<Dataset> GetPublicDatasets();
Dataset Create(Dataset dataset);
- void Update(string username, string name, Dataset dataset);
- void Delete(string username, string name);
+ void Update(string userId, string id, Dataset dataset);
+ void Delete(string userId, string id);
public List<Dataset> GetGuestDatasets();
public void Update(Dataset dataset);
string GetDatasetId(string fileId);
diff --git a/backend/api/api/Services/IMlConnectionService.cs b/backend/api/api/Services/IMlConnectionService.cs
index d161bf49..d5dda9f2 100644
--- a/backend/api/api/Services/IMlConnectionService.cs
+++ b/backend/api/api/Services/IMlConnectionService.cs
@@ -8,6 +8,9 @@ namespace api.Services
Task<string> SendModelAsync(object model, object dataset);
Task PreProcess(Dataset dataset, string filePath,string id);
Task TrainModel(Model model, Experiment experiment, string filePath, Dataset dataset, string id);
+
+ Task<string> Predict(Predictor predictor, Experiment experiment, PredictorColumns[] inputs);
+
//Task<Dataset> PreProcess(Dataset dataset, byte[] file, string filename);
}
} \ No newline at end of file
diff --git a/backend/api/api/Services/IModelService.cs b/backend/api/api/Services/IModelService.cs
index e10b8f3b..00299979 100644
--- a/backend/api/api/Services/IModelService.cs
+++ b/backend/api/api/Services/IModelService.cs
@@ -5,16 +5,17 @@ namespace api.Services
{
public interface IModelService
{
- Model GetOneModel(string username, string name);
+ Model GetOneModel(string userId, string name);
Model GetOneModel(string id);
- List<Model> GetMyModels(string username);
- List<Model> GetLatestModels(string username);
+ List<Model> GetMyModels(string userId);
+ List<Model> GetMyModelsByType(string userId, string problemType);
+ List<Model> GetLatestModels(string userId);
//List<Model> GetPublicModels();
Model Create(Model model);
Model Replace(Model model);
- void Update(string username, string name, Model model);
+ void Update(string userId, string name, Model model);
public void Update(string id, Model model);
- void Delete(string username, string name);
+ void Delete(string userId, string name);
bool CheckHyperparameters(int inputNeurons, int hiddenLayerNeurons, int hiddenLayers, int outputNeurons);
bool CheckDb();
}
diff --git a/backend/api/api/Services/IPredictorService.cs b/backend/api/api/Services/IPredictorService.cs
index 729dd0b6..16f0432a 100644
--- a/backend/api/api/Services/IPredictorService.cs
+++ b/backend/api/api/Services/IPredictorService.cs
@@ -1,19 +1,16 @@
-using System;
-using api.Models;
+using api.Models;
namespace api.Services
{
- public interface IPredictorService
- {
- Predictor GetOnePredictor(string username, string name);
- Predictor GetPredictor(string username, string GetPredictor);
- List<Predictor> SearchPredictors(string name, string username);
- List<Predictor> GetMyPredictors(string username);
- List<Predictor> SortPredictors(string username, bool ascdsc, int latest);
- List<Predictor> GetPublicPredictors();
+ public interface IPredictorService
+ {
Predictor Create(Predictor predictor);
- void Update(string username, string name, Predictor predictor);
- void Delete(string username, string name);
+ void Delete(string id, string userId);
+ List<Predictor> GetMyPredictors(string userId);
+ Predictor GetOnePredictor(string id);
+ Predictor GetPredictor(string userId, string id);
+ List<Predictor> GetPublicPredictors();
+ List<Predictor> SortPredictors(string userId, bool ascdsc, int latest);
+ void Update(string id, Predictor predictor);
}
-}
-
+} \ No newline at end of file
diff --git a/backend/api/api/Services/MlApiCheckActionFilter.cs b/backend/api/api/Services/MlApiCheckActionFilter.cs
new file mode 100644
index 00000000..d1c020b0
--- /dev/null
+++ b/backend/api/api/Services/MlApiCheckActionFilter.cs
@@ -0,0 +1,50 @@
+using System.Net;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace api.Services
+{
+ public class MlApiCheckActionFilter:ActionFilterAttribute
+ {
+ private readonly ILogger _logger;
+ private readonly string _safelist;
+
+ public MlApiCheckActionFilter(string safelist,ILogger logger)
+ {
+ _logger = logger;
+ _safelist = safelist;
+ }
+ public override void OnActionExecuting(ActionExecutingContext context)
+ {
+ var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
+ _logger.LogDebug("Remote IpAddress: {RemoteIp}", remoteIp);
+ var ip = _safelist.Split(';');
+ var badIp = true;
+
+ if (remoteIp.IsIPv4MappedToIPv6)
+ {
+ remoteIp = remoteIp.MapToIPv4();
+ }
+
+ foreach (var address in ip)
+ {
+ var testIp = IPAddress.Parse(address);
+
+ if (testIp.Equals(remoteIp))
+ {
+ badIp = false;
+ break;
+ }
+ }
+
+ if (badIp)
+ {
+ _logger.LogWarning("Forbidden Request from IP: {RemoteIp}", remoteIp);
+ context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
+ return;
+ }
+
+ base.OnActionExecuting(context);
+ }
+ }
+}
diff --git a/backend/api/api/Services/MlConnectionService.cs b/backend/api/api/Services/MlConnectionService.cs
index bde9ce4c..17c0f8b8 100644
--- a/backend/api/api/Services/MlConnectionService.cs
+++ b/backend/api/api/Services/MlConnectionService.cs
@@ -14,8 +14,9 @@ namespace api.Services
private readonly IModelService _modelService;
private readonly IHubContext<ChatHub> _ichat;
private readonly IConfiguration _configuration;
+ private readonly IFileService _fileService;
- public MlConnectionService(IConfiguration configuration,IDatasetService datasetService,IHubContext<ChatHub> ichat)
+ public MlConnectionService(IConfiguration configuration,IDatasetService datasetService,IHubContext<ChatHub> ichat, IFileService fileService)
{
_configuration = configuration;
@@ -23,6 +24,7 @@ namespace api.Services
_datasetService=datasetService;
_ichat=ichat;
+ _fileService = fileService;
}
public async Task<string> SendModelAsync(object model, object dataset)//Don't Use
@@ -41,11 +43,7 @@ namespace api.Services
//request.AddFile("file", file,filename);
request.AddFile("file", filePath);
request.AddHeader("Content-Type", "multipart/form-data");
- var result = await this.client.ExecuteAsync(request);
-
- if (ChatHub.CheckUser(id))
- await _ichat.Clients.Client(ChatHub.Users[id]).SendAsync("NotifyModel",model.name,model._id);
-
+ this.client.ExecuteAsync(request);
return;
}
@@ -67,8 +65,20 @@ namespace api.Services
}
-
+ public async Task<string> Predict(Predictor predictor, Experiment experiment, PredictorColumns[] inputs)
+ {
+ string filePath = _fileService.GetFilePath(predictor.h5FileId, predictor.uploaderId);
+ var request = new RestRequest("predict", Method.Post);
+ request.AddParameter("predictor", JsonConvert.SerializeObject(predictor));
+ request.AddParameter("experiment", JsonConvert.SerializeObject(experiment));
+ request.AddFile("file", filePath);
+ request.AddHeader("Content-Type", "multipart/form-data");
+
+ var result = await this.client.ExecuteAsync(request);
+ return result.Content;
+
+ }
}
}
diff --git a/backend/api/api/Services/ModelService.cs b/backend/api/api/Services/ModelService.cs
index c21844f7..c35e5374 100644
--- a/backend/api/api/Services/ModelService.cs
+++ b/backend/api/api/Services/ModelService.cs
@@ -26,18 +26,22 @@ namespace api.Services
return model;
}
- public void Delete(string username, string name)
+ public void Delete(string userId, string name)
{
- _model.DeleteOne(model => (model.username == username && model.name == name));
+ _model.DeleteOne(model => (model.uploaderId == userId && model.name == name));
}
- public List<Model> GetMyModels(string username)
+ public List<Model> GetMyModels(string userId)
{
- return _model.Find(model => model.username == username).ToList();
+ return _model.Find(model => model.uploaderId == userId).ToList();
}
- public List<Model> GetLatestModels(string username)
+ public List<Model> GetMyModelsByType(string userId, string problemType)
{
- List<Model> list = _model.Find(model => model.username == username).ToList();
+ return _model.Find(model => (model.uploaderId == userId && model.type == problemType)).ToList();
+ }
+ public List<Model> GetLatestModels(string userId)
+ {
+ List<Model> list = _model.Find(model => model.uploaderId == userId).ToList();
list = list.OrderByDescending(model => model.lastUpdated).ToList();
@@ -50,9 +54,9 @@ namespace api.Services
return _model.Find(model => model.isPublic == true).ToList();
}
*/
- public Model GetOneModel(string username, string name)
+ public Model GetOneModel(string userId, string name)
{
- return _model.Find(model => model.username == username && model.name == name).FirstOrDefault();
+ return _model.Find(model => model.uploaderId == userId && model.name == name).FirstOrDefault();
}
public Model GetOneModel(string id)
@@ -60,9 +64,9 @@ namespace api.Services
return _model.Find(model => model._id == id).FirstOrDefault();
}
- public void Update(string username, string name, Model model)
+ public void Update(string userId, string name, Model model)
{
- _model.ReplaceOne(model => model.username == username && model.name == name, model);
+ _model.ReplaceOne(model => model.uploaderId == userId && model.name == name, model);
}
public void Update(string id, Model model)
{
@@ -83,7 +87,7 @@ namespace api.Services
public bool CheckDb()
{
Model? model = null;
- model = _model.Find(model => model.username == "igrannonica").FirstOrDefault();
+ model = _model.Find(model => model.uploaderId == "Igrannonica").FirstOrDefault();
if (model != null)
return false;
diff --git a/backend/api/api/Services/PredictorService.cs b/backend/api/api/Services/PredictorService.cs
index b89eaded..756cc943 100644
--- a/backend/api/api/Services/PredictorService.cs
+++ b/backend/api/api/Services/PredictorService.cs
@@ -14,10 +14,6 @@ namespace api.Services
_predictor = database.GetCollection<Predictor>(settings.PredictorCollectionName);
}
- public List<Predictor> SearchPredictors(string name, string username)
- {
- return _predictor.Find(predictor => predictor.name == name && predictor.isPublic == true).ToList();
- }
public Predictor Create(Predictor predictor)
{
@@ -25,30 +21,30 @@ namespace api.Services
return predictor;
}
- public void Delete(string username, string name)
+ public void Delete(string id, string userId)
{
- _predictor.DeleteOne(predictor => (predictor.username == username && predictor.name == name));
+ _predictor.DeleteOne(predictor => (predictor._id == id && predictor.uploaderId == userId));
}
- public List<Predictor> GetMyPredictors(string username)
+ public List<Predictor> GetMyPredictors(string userId)
{
- return _predictor.Find(predictor => predictor.username == username).ToList();
+ return _predictor.Find(predictor => predictor.uploaderId == userId).ToList();
}
- public Predictor GetOnePredictor(string username, string name)
+ public Predictor GetOnePredictor(string id)
{
- return _predictor.Find(predictor => predictor.username == username && predictor.name == name).FirstOrDefault();
+ return _predictor.Find(predictor => predictor._id == id).FirstOrDefault();
}
- public Predictor GetPredictor(string username, string id)
+ public Predictor GetPredictor(string userId, string id)
{
- return _predictor.Find(predictor => predictor._id == id && (predictor.username == username || predictor.isPublic == true)).FirstOrDefault();
+ return _predictor.Find(predictor => predictor._id == id && (predictor.uploaderId == userId || predictor.isPublic == true)).FirstOrDefault();
}
- public List<Predictor> SortPredictors(string username, bool ascdsc, int latest)
+ public List<Predictor> SortPredictors(string userId, bool ascdsc, int latest)
{
- List<Predictor> list = _predictor.Find(predictor => predictor.username == username).ToList();
+ List<Predictor> list = _predictor.Find(predictor => predictor.uploaderId == userId).ToList();
if (ascdsc)
list = list.OrderBy(predictor => predictor.dateCreated).ToList();
@@ -62,9 +58,9 @@ namespace api.Services
return _predictor.Find(predictor => predictor.isPublic == true).ToList();
}
- public void Update(string username, string name, Predictor predictor)
+ public void Update(string id, Predictor predictor)
{
- _predictor.ReplaceOne(predictor => predictor.username == username && predictor.name == name, predictor);
+ _predictor.ReplaceOne(predictor => predictor._id == id, predictor);
}
}
}
diff --git a/backend/api/api/Services/TempRemovalService.cs b/backend/api/api/Services/TempRemovalService.cs
index e7f366a3..302ca974 100644
--- a/backend/api/api/Services/TempRemovalService.cs
+++ b/backend/api/api/Services/TempRemovalService.cs
@@ -27,7 +27,7 @@ namespace api.Services
if ((DateTime.Now.ToUniversalTime() - file.date).TotalDays >= 1)
{
DeleteFile(file._id);
- List<Dataset> datasets = _dataset.Find(dataset => dataset.fileId == file._id && dataset.username=="").ToList();
+ List<Dataset> datasets = _dataset.Find(dataset => dataset.fileId == file._id && dataset.uploaderId=="").ToList();
foreach(var dataset in datasets)
{
DeleteDataset(dataset._id);
@@ -37,7 +37,7 @@ namespace api.Services
DeleteExperiment(experiment._id);
foreach(var modelId in experiment.ModelIds)
{
- var delModel=_model.Find(model=> modelId== model._id && model.username=="").FirstOrDefault();
+ var delModel=_model.Find(model=> modelId== model._id && model.uploaderId=="").FirstOrDefault();
if(delModel!= null)
DeleteModel(delModel._id);
}
@@ -48,7 +48,7 @@ namespace api.Services
}
}
//Brisanje modela ukoliko gost koristi vec postojeci dataset
- List<Model> models1= _model.Find(model =>model.username == "").ToList();
+ List<Model> models1= _model.Find(model =>model.uploaderId == "").ToList();
foreach(var model in models1)
{
if ((DateTime.Now.ToUniversalTime() - model.dateCreated.ToUniversalTime()).TotalDays >= 1)
diff --git a/backend/api/api/Services/UserService.cs b/backend/api/api/Services/UserService.cs
index 7fc4bdb1..b53f5fdc 100644
--- a/backend/api/api/Services/UserService.cs
+++ b/backend/api/api/Services/UserService.cs
@@ -51,17 +51,17 @@ namespace api.Services
using (var session = _client.StartSession())
{
if(username!=user.Username)
- if(_users.Find(u => u.Username == user.Username).FirstOrDefault()!=null)
- {
- return false;
- }
+ if(_users.Find(u => u.Username == user.Username).FirstOrDefault()!=null)
+ {
+ return false;
+ }
//Trenutan MongoDB Server ne podrzava transakcije.Omoguciti Podrsku
//session.StartTransaction();
try
{
_users.ReplaceOne(user => user.Username == username, user);
- if (username != user.Username)
+ /*if (username != user.Username)
{
var builderModel = Builders<Model>.Update;
var builderDataset = Builders<Dataset>.Update;
@@ -70,7 +70,7 @@ namespace api.Services
_datasets.UpdateMany(x => x.username == username, builderDataset.Set(x => x.username, user.Username));
_predictors.UpdateMany(x => x.username == username, builderPredictor.Set(x => x.username, user.Username));
}
-
+ */
//session.AbortTransaction();
diff --git a/backend/api/api/UploadedFiles/Igrannonica/iris.csv b/backend/api/api/UploadedFiles/Igrannonica/iris.csv
new file mode 100644
index 00000000..0713e5cb
--- /dev/null
+++ b/backend/api/api/UploadedFiles/Igrannonica/iris.csv
@@ -0,0 +1,151 @@
+sepal_length,sepal_width,petal_length,petal_width,class
+5.1,3.5,1.4,0.2,Iris-setosa
+4.9,3.0,1.4,0.2,Iris-setosa
+4.7,3.2,1.3,0.2,Iris-setosa
+4.6,3.1,1.5,0.2,Iris-setosa
+5.0,3.6,1.4,0.2,Iris-setosa
+5.4,3.9,1.7,0.4,Iris-setosa
+4.6,3.4,1.4,0.3,Iris-setosa
+5.0,3.4,1.5,0.2,Iris-setosa
+4.4,2.9,1.4,0.2,Iris-setosa
+4.9,3.1,1.5,0.1,Iris-setosa
+5.4,3.7,1.5,0.2,Iris-setosa
+4.8,3.4,1.6,0.2,Iris-setosa
+4.8,3.0,1.4,0.1,Iris-setosa
+4.3,3.0,1.1,0.1,Iris-setosa
+5.8,4.0,1.2,0.2,Iris-setosa
+5.7,4.4,1.5,0.4,Iris-setosa
+5.4,3.9,1.3,0.4,Iris-setosa
+5.1,3.5,1.4,0.3,Iris-setosa
+5.7,3.8,1.7,0.3,Iris-setosa
+5.1,3.8,1.5,0.3,Iris-setosa
+5.4,3.4,1.7,0.2,Iris-setosa
+5.1,3.7,1.5,0.4,Iris-setosa
+4.6,3.6,1.0,0.2,Iris-setosa
+5.1,3.3,1.7,0.5,Iris-setosa
+4.8,3.4,1.9,0.2,Iris-setosa
+5.0,3.0,1.6,0.2,Iris-setosa
+5.0,3.4,1.6,0.4,Iris-setosa
+5.2,3.5,1.5,0.2,Iris-setosa
+5.2,3.4,1.4,0.2,Iris-setosa
+4.7,3.2,1.6,0.2,Iris-setosa
+4.8,3.1,1.6,0.2,Iris-setosa
+5.4,3.4,1.5,0.4,Iris-setosa
+5.2,4.1,1.5,0.1,Iris-setosa
+5.5,4.2,1.4,0.2,Iris-setosa
+4.9,3.1,1.5,0.1,Iris-setosa
+5.0,3.2,1.2,0.2,Iris-setosa
+5.5,3.5,1.3,0.2,Iris-setosa
+4.9,3.1,1.5,0.1,Iris-setosa
+4.4,3.0,1.3,0.2,Iris-setosa
+5.1,3.4,1.5,0.2,Iris-setosa
+5.0,3.5,1.3,0.3,Iris-setosa
+4.5,2.3,1.3,0.3,Iris-setosa
+4.4,3.2,1.3,0.2,Iris-setosa
+5.0,3.5,1.6,0.6,Iris-setosa
+5.1,3.8,1.9,0.4,Iris-setosa
+4.8,3.0,1.4,0.3,Iris-setosa
+5.1,3.8,1.6,0.2,Iris-setosa
+4.6,3.2,1.4,0.2,Iris-setosa
+5.3,3.7,1.5,0.2,Iris-setosa
+5.0,3.3,1.4,0.2,Iris-setosa
+7.0,3.2,4.7,1.4,Iris-versicolor
+6.4,3.2,4.5,1.5,Iris-versicolor
+6.9,3.1,4.9,1.5,Iris-versicolor
+5.5,2.3,4.0,1.3,Iris-versicolor
+6.5,2.8,4.6,1.5,Iris-versicolor
+5.7,2.8,4.5,1.3,Iris-versicolor
+6.3,3.3,4.7,1.6,Iris-versicolor
+4.9,2.4,3.3,1.0,Iris-versicolor
+6.6,2.9,4.6,1.3,Iris-versicolor
+5.2,2.7,3.9,1.4,Iris-versicolor
+5.0,2.0,3.5,1.0,Iris-versicolor
+5.9,3.0,4.2,1.5,Iris-versicolor
+6.0,2.2,4.0,1.0,Iris-versicolor
+6.1,2.9,4.7,1.4,Iris-versicolor
+5.6,2.9,3.6,1.3,Iris-versicolor
+6.7,3.1,4.4,1.4,Iris-versicolor
+5.6,3.0,4.5,1.5,Iris-versicolor
+5.8,2.7,4.1,1.0,Iris-versicolor
+6.2,2.2,4.5,1.5,Iris-versicolor
+5.6,2.5,3.9,1.1,Iris-versicolor
+5.9,3.2,4.8,1.8,Iris-versicolor
+6.1,2.8,4.0,1.3,Iris-versicolor
+6.3,2.5,4.9,1.5,Iris-versicolor
+6.1,2.8,4.7,1.2,Iris-versicolor
+6.4,2.9,4.3,1.3,Iris-versicolor
+6.6,3.0,4.4,1.4,Iris-versicolor
+6.8,2.8,4.8,1.4,Iris-versicolor
+6.7,3.0,5.0,1.7,Iris-versicolor
+6.0,2.9,4.5,1.5,Iris-versicolor
+5.7,2.6,3.5,1.0,Iris-versicolor
+5.5,2.4,3.8,1.1,Iris-versicolor
+5.5,2.4,3.7,1.0,Iris-versicolor
+5.8,2.7,3.9,1.2,Iris-versicolor
+6.0,2.7,5.1,1.6,Iris-versicolor
+5.4,3.0,4.5,1.5,Iris-versicolor
+6.0,3.4,4.5,1.6,Iris-versicolor
+6.7,3.1,4.7,1.5,Iris-versicolor
+6.3,2.3,4.4,1.3,Iris-versicolor
+5.6,3.0,4.1,1.3,Iris-versicolor
+5.5,2.5,4.0,1.3,Iris-versicolor
+5.5,2.6,4.4,1.2,Iris-versicolor
+6.1,3.0,4.6,1.4,Iris-versicolor
+5.8,2.6,4.0,1.2,Iris-versicolor
+5.0,2.3,3.3,1.0,Iris-versicolor
+5.6,2.7,4.2,1.3,Iris-versicolor
+5.7,3.0,4.2,1.2,Iris-versicolor
+5.7,2.9,4.2,1.3,Iris-versicolor
+6.2,2.9,4.3,1.3,Iris-versicolor
+5.1,2.5,3.0,1.1,Iris-versicolor
+5.7,2.8,4.1,1.3,Iris-versicolor
+6.3,3.3,6.0,2.5,Iris-virginica
+5.8,2.7,5.1,1.9,Iris-virginica
+7.1,3.0,5.9,2.1,Iris-virginica
+6.3,2.9,5.6,1.8,Iris-virginica
+6.5,3.0,5.8,2.2,Iris-virginica
+7.6,3.0,6.6,2.1,Iris-virginica
+4.9,2.5,4.5,1.7,Iris-virginica
+7.3,2.9,6.3,1.8,Iris-virginica
+6.7,2.5,5.8,1.8,Iris-virginica
+7.2,3.6,6.1,2.5,Iris-virginica
+6.5,3.2,5.1,2.0,Iris-virginica
+6.4,2.7,5.3,1.9,Iris-virginica
+6.8,3.0,5.5,2.1,Iris-virginica
+5.7,2.5,5.0,2.0,Iris-virginica
+5.8,2.8,5.1,2.4,Iris-virginica
+6.4,3.2,5.3,2.3,Iris-virginica
+6.5,3.0,5.5,1.8,Iris-virginica
+7.7,3.8,6.7,2.2,Iris-virginica
+7.7,2.6,6.9,2.3,Iris-virginica
+6.0,2.2,5.0,1.5,Iris-virginica
+6.9,3.2,5.7,2.3,Iris-virginica
+5.6,2.8,4.9,2.0,Iris-virginica
+7.7,2.8,6.7,2.0,Iris-virginica
+6.3,2.7,4.9,1.8,Iris-virginica
+6.7,3.3,5.7,2.1,Iris-virginica
+7.2,3.2,6.0,1.8,Iris-virginica
+6.2,2.8,4.8,1.8,Iris-virginica
+6.1,3.0,4.9,1.8,Iris-virginica
+6.4,2.8,5.6,2.1,Iris-virginica
+7.2,3.0,5.8,1.6,Iris-virginica
+7.4,2.8,6.1,1.9,Iris-virginica
+7.9,3.8,6.4,2.0,Iris-virginica
+6.4,2.8,5.6,2.2,Iris-virginica
+6.3,2.8,5.1,1.5,Iris-virginica
+6.1,2.6,5.6,1.4,Iris-virginica
+7.7,3.0,6.1,2.3,Iris-virginica
+6.3,3.4,5.6,2.4,Iris-virginica
+6.4,3.1,5.5,1.8,Iris-virginica
+6.0,3.0,4.8,1.8,Iris-virginica
+6.9,3.1,5.4,2.1,Iris-virginica
+6.7,3.1,5.6,2.4,Iris-virginica
+6.9,3.1,5.1,2.3,Iris-virginica
+5.8,2.7,5.1,1.9,Iris-virginica
+6.8,3.2,5.9,2.3,Iris-virginica
+6.7,3.3,5.7,2.5,Iris-virginica
+6.7,3.0,5.2,2.3,Iris-virginica
+6.3,2.5,5.0,1.9,Iris-virginica
+6.5,3.0,5.2,2.0,Iris-virginica
+6.2,3.4,5.4,2.3,Iris-virginica
+5.9,3.0,5.1,1.8,Iris-virginica
diff --git a/backend/api/api/api.csproj b/backend/api/api/api.csproj
index 7275f67c..3d245333 100644
--- a/backend/api/api/api.csproj
+++ b/backend/api/api/api.csproj
@@ -21,6 +21,7 @@
</ItemGroup>
<ItemGroup>
+ <Folder Include="PredictorFiles\" />
<Folder Include="TempFiles\" />
</ItemGroup>
diff --git a/backend/api/api/appsettings.json b/backend/api/api/appsettings.json
index 42f62b75..c2310978 100644
--- a/backend/api/api/appsettings.json
+++ b/backend/api/api/appsettings.json
@@ -1,27 +1,29 @@
{
"AppSettings": {
"JwtToken": "2mnttqPtRb4GIWHFtagm",
- "MlApi": "http://127.0.0.1:5543"
+ "MlApi": "http://127.0.0.1:5543",
+ "FrontApi": "http://localhost:4200",
+ "MlIp":"127.0.0.1;::1"
},
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft.AspNetCore": "Warning"
- }
- },
- "AllowedHosts": "*",
- "UserStoreDatabaseSettings": {
- /* LocalHost*/
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "UserStoreDatabaseSettings": {
+ /* LocalHost*/
- "ConnectionString": "mongodb://127.0.0.1:27017/",
- "DatabaseName": "si_project",
- "CollectionName": "users",
- "DatasetCollectionName": "Dataset",
- "ModelCollectionName": "Model",
- "PredictorCollectionName": "Predictor",
- "FilesCollectionName": "Files",
- "ExperimentCollectionName": "Experiment"
- /*
+ "ConnectionString": "mongodb://127.0.0.1:27017/",
+ "DatabaseName": "si_project",
+ "CollectionName": "users",
+ "DatasetCollectionName": "Dataset",
+ "ModelCollectionName": "Model",
+ "PredictorCollectionName": "Predictor",
+ "FilesCollectionName": "Files",
+ "ExperimentCollectionName": "Experiment"
+ /*
"ConnectionString": "mongodb+srv://si_user:si_user@sidatabase.twtfm.mongodb.net/myFirstDatabase?retryWrites=true&w=majority",
"DatabaseName": "si_db",
"CollectionName": "users",
@@ -30,5 +32,5 @@
"PredictorCollectionName": "Predictor",
"FilesCollectionName": "Files",
"ExperimentCollectionName": "Experiment" */
- }
+ }
} \ No newline at end of file
diff --git a/backend/microservice/api/config.py b/backend/microservice/api/config.py
index 2b6fbe89..8fb088a7 100644
--- a/backend/microservice/api/config.py
+++ b/backend/microservice/api/config.py
@@ -1,2 +1,2 @@
api_url = "http://localhost:5283/api"
-
+hostIP = "127.0.0.1:5543" \ No newline at end of file
diff --git a/backend/microservice/api/controller.py b/backend/microservice/api/controller.py
index 8e12c41d..f870b2b1 100644
--- a/backend/microservice/api/controller.py
+++ b/backend/microservice/api/controller.py
@@ -1,3 +1,7 @@
+from cmath import log
+from dataclasses import dataclass
+from distutils.command.upload import upload
+from gc import callbacks
import flask
from flask import request, jsonify
import newmlservice
@@ -9,47 +13,101 @@ import config
app = flask.Flask(__name__)
app.config["DEBUG"] = True
-app.config["SERVER_NAME"] = "127.0.0.1:5543"
-
+app.config["SERVER_NAME"] = config.hostIP
+
+#@dataclass
+#class Predictor:
+# _id : str
+ # username: str
+ # inputs : list
+ # output : str
+ # isPublic: bool
+ # accessibleByLink: bool
+ # dateCreated: DateTime
+ # experimentId: str
+ # modelId: str
+ # h5FileId: str
+ # metrics: list
+
+
class train_callback(tf.keras.callbacks.Callback):
- def __init__(self, x_test, y_test):
+ def __init__(self, x_test, y_test,modelId):
self.x_test = x_test
self.y_test = y_test
+ self.modelId=modelId
#
def on_epoch_end(self, epoch, logs=None):
- print(epoch)
+ #print('Evaluation: ', self.model.evaluate(self.x_test,self.y_test),"\n")
+
+ #print(epoch)
+
+ #print(logs)
+
#ml_socket.send(epoch)
#file = request.files.get("file")
url = config.api_url + "/Model/epoch"
- requests.post(url, epoch).text
+ r=requests.post(url, json={"Stat":str(logs),"ModelId":str(self.modelId),"EpochNum":epoch}).text
+
+ #print(r)
#print('Evaluation: ', self.model.evaluate(self.x_test,self.y_test),"\n") #broj parametara zavisi od izabranih metrika loss je default
@app.route('/train', methods = ['POST'])
def train():
- print("******************************TRAIN*************************************************")
+ #print("******************************TRAIN*************************************************")
+
f = request.files.get("file")
data = pd.read_csv(f)
paramsModel = json.loads(request.form["model"])
paramsExperiment = json.loads(request.form["experiment"])
paramsDataset = json.loads(request.form["dataset"])
#dataset, paramsModel, paramsExperiment, callback)
- result = newmlservice.train(data, paramsModel, paramsExperiment,paramsDataset, train_callback)
+ filepath,result = newmlservice.train(data, paramsModel, paramsExperiment,paramsDataset, train_callback)
+ """
+ f = request.json['filepath']
+ dataset = pd.read_csv(f)
+ filepath,result=newmlservice.train(dataset,request.json['model'],train_callback)
print(result)
- return jsonify(result)
+ """
+
+ url = config.api_url + "/file/h5"
+ files = {'file': open(filepath, 'rb')}
+ r=requests.post(url, files=files,data={"uploaderId":paramsExperiment['uploaderId']})
+ fileId=r.text
+ m = []
+ for attribute, value in result.items():
+ m.append({"Name" : attribute, "JsonValue" : value})
+ predictor = {
+ "_id" : "",
+ "uploaderId" : paramsModel["uploaderId"],
+ "inputs" : paramsExperiment["inputColumns"],
+ "output" : paramsExperiment["outputColumn"],
+ "isPublic" : False,
+ "accessibleByLink" : False,
+ "experimentId" : paramsExperiment["_id"],
+ "modelId" : paramsModel["_id"],
+ "h5FileId" : fileId,
+ "metrics" : m
+ }
+ #print(predictor)
+ #print('\n')
+ url = config.api_url + "/Predictor/add"
+ r = requests.post(url, json=predictor).text
+ #print(r)
+ return r
@app.route('/predict', methods = ['POST'])
def predict():
- f = request.json['filepath']
- dataset = pd.read_csv(f)
- m = request.json['modelpath']
- model = tf.keras.models.load_model(m)
- print("********************************model loaded*******************************")
- newmlservice.manageH5(dataset,request.json['model'],model)
- return "done"
+ h5 = request.files.get("h5file")
+ model = tf.keras.models.load_model(h5)
+ paramsExperiment = json.loads(request.form["experiment"])
+ paramsPredictor = json.loads(request.form["predictor"])
+ #print("********************************model loaded*******************************")
+ result = newmlservice.predict(paramsExperiment, paramsPredictor, model)
+ return result
@app.route('/preprocess',methods=['POST'])
def returnColumnsInfo():
- print("********************************PREPROCESS*******************************")
+ #print("********************************PREPROCESS*******************************")
dataset = json.loads(request.form["dataset"])
file = request.files.get("file")
data=pd.read_csv(file)
@@ -69,8 +127,8 @@ def returnColumnsInfo():
dataset["colCount"] = preprocess["colCount"]
dataset["rowCount"] = preprocess["rowCount"]
dataset["isPreProcess"] = True
- print(dataset)
+ #print(dataset)
return jsonify(dataset)
-print("App loaded.")
+#print("App loaded.")
app.run() \ No newline at end of file
diff --git a/backend/microservice/api/newmlservice.py b/backend/microservice/api/newmlservice.py
index d19a4e44..6e65c876 100644
--- a/backend/microservice/api/newmlservice.py
+++ b/backend/microservice/api/newmlservice.py
@@ -1,5 +1,6 @@
from enum import unique
from itertools import count
+import os
import pandas as pd
from sklearn import datasets, multiclass
import tensorflow as tf
@@ -21,6 +22,7 @@ from sklearn.model_selection import train_test_split
from dataclasses import dataclass
import statistics as s
from sklearn.metrics import roc_auc_score
+import matplotlib.pyplot as plt
#from ann_visualizer.visualize import ann_viz;
def returnColumnsInfo(dataset):
dict=[]
@@ -112,35 +114,48 @@ class TrainingResult:
metrics: dict
'''
-def train(dataset, params, callback):
- problem_type = params["type"]
+def train(dataset, paramsModel,paramsExperiment,paramsDataset,callback):
+ problem_type = paramsModel["type"]
#print(problem_type)
data = pd.DataFrame()
#print(data)
- for col in params["inputColumns"]:
+ for col in paramsExperiment["inputColumns"]:
#print(col)
data[col]=dataset[col]
- output_column = params["columnToPredict"]
+ output_column = paramsExperiment["outputColumn"]
data[output_column] = dataset[output_column]
#print(data)
###NULL
- null_value_options = params["nullValues"]
- null_values_replacers = params["nullValuesReplacers"]
-
+ null_value_options = paramsExperiment["nullValues"]
+ null_values_replacers = paramsExperiment["nullValuesReplacers"]
+ kategorijskekolone=data.select_dtypes(include=['object']).columns.copy()
+ #print(kategorijskekolone)
if(null_value_options=='replace'):
#print("replace null") #
- dict=params['null_values_replacers']
+ dict=null_values_replacers
while(len(dict)>0):
replace=dict.pop()
col=replace['column']
opt=replace['option']
if(opt=='replace'):
- replacevalue=replace['value']
- data[col]=data[col].fillna(replacevalue)
+ val = replace['value']
+ if(data[col].dtype == 'int64'):
+ val = np.int64(val)
+ elif(data[col].dtype == 'float64'):
+ val = np.float64(val)
+ data[col]=data[col].fillna(val)
elif(null_value_options=='delete_rows'):
data=data.dropna()
elif(null_value_options=='delete_columns'):
+ if(data[output_column].isnull().sum()>0):
+ if(output_column in kategorijskekolone):
+ replace=data[output_column].value_counts().index[0]
+ #print(replace)
+ else:
+ replace=data[output_column].mean()
+ data[output_column]=data[output_column].fillna(replace)
+ #print(data[output_column].isnull().sum())
data=data.dropna(axis=1)
#print(data.shape)
@@ -153,49 +168,74 @@ def train(dataset, params, callback):
data.pop(col)
#
### Enkodiranje
- encoding=params["encoding"]
- if(encoding=='label'):
- encoder=LabelEncoder()
- for col in data.columns:
- if(data[col].dtype==np.object_):
- data[col]=encoder.fit_transform(data[col])
+ '''
+ encodings=paramsExperiment["encodings"]
+
+ from sklearn.preprocessing import LabelEncoder
+ kategorijskekolone=data.select_dtypes(include=['object']).columns
+ encoder=LabelEncoder()
+ for kolona in data.columns:
+ if(kolona in kategorijskekolone):
+ data[kolona]=encoder.fit_transform(data[kolona])
+ '''
- elif(encoding=='onehot'):
- category_columns=[]
- for col in data.columns:
- if(data[col].dtype==np.object_):
- category_columns.append(col)
- data=pd.get_dummies(data, columns=category_columns, prefix=category_columns)
+ encodings=paramsExperiment["encodings"]
+ datafront=dataset.copy()
+ svekolone=datafront.columns
- elif(encoding=='ordinal'):
- encoder = OrdinalEncoder()
- for col in data.columns:
- if(data[col].dtype==np.object_):
- data[col]=encoder.fit_transform(data[col])
-
- elif(encoding=='hashing'):
- category_columns=[]
- for col in data.columns:
- if(data[col].dtype==np.object_):
- category_columns.append(col)
- encoder=ce.HashingEncoder(cols=category_columns, n_components=len(category_columns))
- encoder.fit_transform(data)
- elif(encoding=='binary'):
- category_columns=[]
- for col in data.columns:
- if(data[col].dtype==np.object_):
- category_columns.append(col)
- encoder=ce.BinaryEncoder(cols=category_columns, return_df=True)
- encoder.fit_transform(data)
-
- elif(encoding=='baseN'):
- category_columns=[]
- for col in data.columns:
- if(data[col].dtype==np.object_):
- category_columns.append(col)
- encoder=ce.BaseNEncoder(cols=category_columns, return_df=True, base=5)
- encoder.fit_transform(data)
+
+ for kolonaEncoding in encodings:
+
+ kolona = kolonaEncoding["columnName"]
+ if kolona in data.columns:
+ encoding = kolonaEncoding["encoding"]
+
+ if(kolona in kategorijskekolone):
+ if(encoding=='label'):
+ encoder=LabelEncoder()
+ for col in data.columns:
+ if(data[col].dtype==np.object_):
+ data[col]=encoder.fit_transform(data[col])
+
+
+ elif(encoding=='onehot'):
+ category_columns=[]
+ for col in data.columns:
+ if(data[col].dtype==np.object_):
+ category_columns.append(col)
+ data=pd.get_dummies(data, 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])
+
+ elif(encoding=='hashing'):
+ category_columns=[]
+ for col in data.columns:
+ if(data[col].dtype==np.object_):
+ category_columns.append(col)
+ encoder=ce.HashingEncoder(cols=category_columns, n_components=len(category_columns))
+ encoder.fit_transform(data)
+ elif(encoding=='binary'):
+ category_columns=[]
+ for col in data.columns:
+ if(data[col].dtype==np.object_):
+ category_columns.append(col)
+ encoder=ce.BinaryEncoder(cols=category_columns, return_df=True)
+ encoder.fit_transform(data)
+
+ elif(encoding=='baseN'):
+ category_columns=[]
+ for col in data.columns:
+ if(data[col].dtype==np.object_):
+ category_columns.append(col)
+ encoder=ce.BaseNEncoder(cols=category_columns, return_df=True, base=5)
+ encoder.fit_transform(data)
+
+
#
# Input - output
#
@@ -210,8 +250,8 @@ def train(dataset, params, callback):
#
# Podela na test i trening skupove
#
- test=params["randomTestSetDistribution"]
- randomOrder = params["randomOrder"]
+ test=paramsExperiment["randomTestSetDistribution"]
+ randomOrder = paramsExperiment["randomOrder"]
if(randomOrder):
random=123
else:
@@ -224,7 +264,7 @@ def train(dataset, params, callback):
#
#
###OPTIMIZATORI
-
+ """
if(params['optimizer']=='adam'):
opt=tf.keras.optimizers.Adam(learning_rate=params['learningRate'])
@@ -250,7 +290,7 @@ def train(dataset, params, callback):
opt=tf.keras.optimizers.RMSprop(learning_rate=params['learningRate'])
###REGULARIZACIJA
- #regularisation={'kernelType':'l1 ili l2 ili l1_l2','krenelRate':default=0.01 ili jedna od vrednosti(0.0001,0.001,0.1,1,2,3) ili neka koju je korisnik zadao,'biasType':'','biasRate':'','activityType','activityRate'}
+ #regularisation={'kernelType':'l1 ili l2 ili l1_l2','kernelRate':default=0.01 ili jedna od vrednosti(0.0001,0.001,0.1,1,2,3) ili neka koju je korisnik zadao,'biasType':'','biasRate':'','activityType','activityRate'}
reg=params['regularisation']
###Kernel
@@ -276,49 +316,56 @@ def train(dataset, params, callback):
activityreg=tf.keras.regularizers.l2(reg['activityRate'])
elif(reg['kernelType']=='l1l2'):
activityreg=tf.keras.regularizers.l1_l2(l1=reg['activityRate'][0],l2=reg['activityRate'][1])
-
-
+ """
+ filepath=os.path.join("temp/",paramsExperiment['_id']+"_"+paramsModel['_id']+".h5")
if(problem_type=='multi-klasifikacioni'):
#print('multi')
classifier=tf.keras.Sequential()
- classifier.add(tf.keras.layers.Dense(units=params['hiddenLayerNeurons'], activation=params['hiddenLayerActivationFunctions'][0],input_dim=x_train.shape[1]))#prvi skriveni + definisanje prethodnog-ulaznog
- for i in range(params['hiddenLayers']-1):#ako postoji vise od jednog skrivenog sloja
+ classifier.add(tf.keras.layers.Dense(units=paramsModel['hiddenLayerNeurons'], activation=paramsModel['hiddenLayerActivationFunctions'][0],input_dim=x_train.shape[1]))#prvi skriveni + definisanje prethodnog-ulaznog
+ for i in range(paramsModel['hiddenLayers']-1):#ako postoji vise od jednog skrivenog sloja
#print(i)
- classifier.add(tf.keras.layers.Dense(units=params['hiddenLayerNeurons'], activation=params['hiddenLayerActivationFunctions'][i+1]))#i-ti skriveni sloj
- classifier.add(tf.keras.layers.Dense(units=5, activation=params['outputLayerActivationFunction']))#izlazni sloj
+ classifier.add(tf.keras.layers.Dense(units=paramsModel['hiddenLayerNeurons'], activation=paramsModel['hiddenLayerActivationFunctions'][i+1]))#i-ti skriveni sloj
+ classifier.add(tf.keras.layers.Dense(units=5, activation=paramsModel['outputLayerActivationFunction']))#izlazni sloj
- classifier.compile(loss =params["lossFunction"] , optimizer = params['optimizer'] , metrics =params['metrics'])
-
- history=classifier.fit(x_train, y_train, epochs = params['epochs'],batch_size=params['batchSize'])
+ classifier.compile(loss =paramsModel["lossFunction"] , optimizer = paramsModel['optimizer'] , metrics =['accuracy','mae','mse'])
+ history=classifier.fit(x_train, y_train, epochs = paramsModel['epochs'],batch_size=paramsModel['batchSize'],callbacks=callback(x_test, y_test,paramsModel['_id']))
+
+ hist=history.history
+ #plt.plot(hist['accuracy'])
+ #plt.show()
y_pred=classifier.predict(x_test)
y_pred=np.argmax(y_pred,axis=1)
- #print(y_pred.flatten())
- #print(y_test)
+
scores = classifier.evaluate(x_test, y_test)
#print("\n%s: %.2f%%" % (classifier.metrics_names[1], scores[1]*100))
- classifier.save("temp/"+params['name'], save_format='h5')
+
+
+ classifier.save(filepath, save_format='h5')
+
#vizuelizacija u python-u
#from ann_visualizer.visualize import ann_viz;
#ann_viz(classifier, title="My neural network")
+
+ return filepath,hist
elif(problem_type=='binarni-klasifikacioni'):
#print('*************************************************************************binarni')
classifier=tf.keras.Sequential()
- classifier.add(tf.keras.layers.Dense(units=params['hiddenLayerNeurons'], activation=params['hiddenLayerActivationFunctions'][0],input_dim=x_train.shape[1]))#prvi skriveni + definisanje prethodnog-ulaznog
- for i in range(params['hiddenLayers']-1):#ako postoji vise od jednog skrivenog sloja
+ classifier.add(tf.keras.layers.Dense(units=paramsModel['hiddenLayerNeurons'], activation=paramsModel['hiddenLayerActivationFunctions'][0],input_dim=x_train.shape[1]))#prvi skriveni + definisanje prethodnog-ulaznog
+ for i in range(paramsModel['hiddenLayers']-1):#ako postoji vise od jednog skrivenog sloja
#print(i)
- classifier.add(tf.keras.layers.Dense(units=params['hiddenLayerNeurons'], activation=params['hiddenLayerActivationFunctions'][i+1]))#i-ti skriveni sloj
- classifier.add(tf.keras.layers.Dense(units=1, activation=params['outputLayerActivationFunction']))#izlazni sloj
-
- classifier.compile(loss =params["lossFunction"] , optimizer = params['optimizer'] , metrics =params['metrics'])
+ classifier.add(tf.keras.layers.Dense(units=paramsModel['hiddenLayerNeurons'], activation=paramsModel['hiddenLayerActivationFunctions'][i+1]))#i-ti skriveni sloj
+ classifier.add(tf.keras.layers.Dense(units=1, activation=paramsModel['outputLayerActivationFunction']))#izlazni sloj
- history=classifier.fit(x_train, y_train, epochs = params['epochs'],batch_size=params['batchSize'])
+ classifier.compile(loss =paramsModel["lossFunction"] , optimizer = paramsModel['optimizer'] , metrics =['accuracy'])
+ history=classifier.fit(x_train, y_train, epochs = paramsModel['epochs'],batch_size=paramsModel['batchSize'],callbacks=callback(x_test, y_test,paramsModel['_id']))
+ hist=history.history
y_pred=classifier.predict(x_test)
y_pred=(y_pred>=0.5).astype('int')
@@ -329,23 +376,26 @@ def train(dataset, params, callback):
#print("\n%s: %.2f%%" % (classifier.metrics_names[1], scores[1]*100))
#ann_viz(classifier, title="My neural network")
- classifier.save("temp/"+params['name'], save_format='h5')
+ classifier.save(filepath, save_format='h5')
+ return filepath,hist
elif(problem_type=='regresioni'):
classifier=tf.keras.Sequential()
- classifier.add(tf.keras.layers.Dense(units=params['hiddenLayerNeurons'], activation=params['hiddenLayerActivationFunctions'][0],input_dim=x_train.shape[1]))#prvi skriveni + definisanje prethodnog-ulaznog
- for i in range(params['hiddenLayers']-1):#ako postoji vise od jednog skrivenog sloja
+ classifier.add(tf.keras.layers.Dense(units=paramsModel['hiddenLayerNeurons'], activation=paramsModel['hiddenLayerActivationFunctions'][0],input_dim=x_train.shape[1]))#prvi skriveni + definisanje prethodnog-ulaznog
+ for i in range(paramsModel['hiddenLayers']-1):#ako postoji vise od jednog skrivenog sloja
#print(i)
- classifier.add(tf.keras.layers.Dense(units=params['hiddenLayerNeurons'], activation=params['hiddenLayerActivationFunctions'][i+1]))#i-ti skriveni sloj
+ classifier.add(tf.keras.layers.Dense(units=paramsModel['hiddenLayerNeurons'], activation=paramsModel['hiddenLayerActivationFunctions'][i+1]))#i-ti skriveni sloj
classifier.add(tf.keras.layers.Dense(units=1))
- classifier.compile(loss =params["lossFunction"] , optimizer = params['optimizer'] , metrics =params['metrics'])
+ classifier.compile(loss =paramsModel["lossFunction"] , optimizer = paramsModel['optimizer'] , metrics =['accuracy','mae','mse'])
- history=classifier.fit(x_train, y_train, epochs = params['epochs'],batch_size=params['batchSize'])
+ history=classifier.fit(x_train, y_train, epochs = paramsModel['epochs'],batch_size=paramsModel['batchSize'],callbacks=callback(x_test, y_test,paramsModel['_id']))
+ hist=history.history
y_pred=classifier.predict(x_test)
#print(classifier.evaluate(x_test, y_test))
-
+ classifier.save(filepath, save_format='h5')
+ return filepath,hist
def roc_auc_score_multiclass(actual_class, pred_class, average = "macro"):
#creating a set of all the unique classes using the actual class list
@@ -427,6 +477,11 @@ def train(dataset, params, callback):
micro_averaged_f1=metrics.f1_score(y_test, y_pred, average = 'micro')
roc_auc_dict=roc_auc_score_multiclass(y_test, y_pred)
'''
+def predict(experiment, predictor, model):
+ #model.predict()
+ # ovo je pre bilo manageH5
+ return "TODO"
+
def manageH5(dataset,params,h5model):
problem_type = params["type"]
@@ -503,7 +558,7 @@ def manageH5(dataset,params,h5model):
h5model.summary()
#ann_viz(h5model, title="My neural network")
- h5model.compile(loss=params['lossFunction'], optimizer=params['optimizer'], metrics=params['metrics'])
+ h5model.compile(loss=params['lossFunction'], optimizer=params['optimizer'], metrics=params['accuracy',''])
history=h5model.fit(x2, y2, epochs = params['epochs'],batch_size=params['batchSize'])
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 82cd01e6..d44f86d1 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -25,14 +25,16 @@
"@ng-bootstrap/ng-bootstrap": "^12.0.0",
"@popperjs/core": "^2.10.2",
"bootstrap": "^5.1.3",
+ "canvasjs": "^1.8.3",
"chart.js": "^3.7.1",
+ "chartjs-plugin-datalabels": "^0.7.0",
"csv-parser": "^3.0.0",
"d3-graphviz": "^2.6.1",
"jquery": "^3.6.0",
"mdb-angular-ui-kit": "^2.0.0",
"ng-multiselect-dropdown": "^0.3.8",
"ng-uikit-pro-standard": "^1.0.0",
- "ng2-charts": "^3.0.8",
+ "ng2-charts": "^3.0.9",
"ng2-search-filter": "^0.5.1",
"ngx-cookie-service": "^13.1.2",
"popper.js": "^1.16.1",
@@ -45,6 +47,7 @@
"@angular-devkit/build-angular": "~13.2.5",
"@angular/cli": "~13.2.5",
"@angular/compiler-cli": "~13.2.0",
+ "@types/canvasjs": "^1.9.7",
"@types/crypto-js": "^4.1.1",
"@types/d3-graphviz": "^2.6.7",
"@types/jasmine": "~3.10.0",
@@ -452,7 +455,6 @@
"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,7 +484,6 @@
"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"
},
@@ -494,7 +495,6 @@
"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",
@@ -524,7 +524,6 @@
"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"
}
@@ -533,7 +532,6 @@
"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",
@@ -547,7 +545,6 @@
"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"
}
@@ -787,7 +784,6 @@
"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",
@@ -817,7 +813,6 @@
"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"
}
@@ -826,7 +821,6 @@
"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"
}
@@ -835,7 +829,6 @@
"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",
@@ -849,7 +842,6 @@
"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"
}
@@ -2748,6 +2740,12 @@
"@types/node": "*"
}
},
+ "node_modules/@types/canvasjs": {
+ "version": "1.9.7",
+ "resolved": "https://registry.npmjs.org/@types/canvasjs/-/canvasjs-1.9.7.tgz",
+ "integrity": "sha512-/CfA3VtyTYVXDHvMy8F5hsvRUTiBX7TGe+nAHNONoJlNHbqakeIzxxt1MSNH619s8Oiu4CcFBMmJVg50El/0lw==",
+ "dev": true
+ },
"node_modules/@types/component-emitter": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
@@ -3376,7 +3374,6 @@
"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"
@@ -3432,9 +3429,9 @@
}
},
"node_modules/async": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
- "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true,
"dependencies": {
"lodash": "^4.17.14"
@@ -3650,7 +3647,6 @@
"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"
}
@@ -3747,7 +3743,6 @@
"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"
},
@@ -3897,6 +3892,11 @@
"url": "https://opencollective.com/browserslist"
}
},
+ "node_modules/canvasjs": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/canvasjs/-/canvasjs-1.8.3.tgz",
+ "integrity": "sha512-60eUT0VjqRgYqdIQcOkXg0Zptfbl4HefA/O51YEf1m/P0uXvE3icI/1KPrXpY9aVxn8gG/BB8DzVoTGCcyBnYg=="
+ },
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -3921,11 +3921,18 @@
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz",
"integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA=="
},
+ "node_modules/chartjs-plugin-datalabels": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-0.7.0.tgz",
+ "integrity": "sha512-PKVUX14nYhH0wcdCpgOoC39Gbzvn6cZ7O9n+bwc02yKD9FTnJ7/TSrBcfebmolFZp1Rcicr9xbT0a5HUbigS7g==",
+ "peerDependencies": {
+ "chart.js": ">= 2.7.0 < 3"
+ }
+ },
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
- "dev": true,
"funding": [
{
"type": "individual",
@@ -4944,7 +4951,6 @@
"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"
}
@@ -5109,7 +5115,6 @@
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
- "dev": true,
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.2"
@@ -5119,7 +5124,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "dev": true,
"optional": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
@@ -5923,7 +5927,6 @@
"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"
},
@@ -6092,7 +6095,6 @@
"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": [
@@ -6202,7 +6204,6 @@
"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"
},
@@ -6795,7 +6796,6 @@
"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"
},
@@ -6849,7 +6849,6 @@
"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"
}
@@ -6866,7 +6865,6 @@
"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"
},
@@ -6893,7 +6891,6 @@
"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"
}
@@ -7759,7 +7756,6 @@
"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"
},
@@ -7771,7 +7767,6 @@
"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"
}
@@ -8240,9 +8235,9 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/ng2-charts": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-3.0.8.tgz",
- "integrity": "sha512-ELlpN0b/IJO4ka/P2sFBKeng3bV7XOQuh40f0J5hx9UveWPaSxOYQAOiGxV7BN2VSnKq6GRkjRvqTrcQPyJYww==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-3.0.9.tgz",
+ "integrity": "sha512-VKUd0lMhDb/IC4WGYH/CW29HlMibXGMK+Ik/T+M0uR98Ox+YEQUPsrqbroF/cWMxVf2XuTkFGaUn2fbqlDBSOQ==",
"dependencies": {
"lodash-es": "^4.17.15",
"tslib": "^2.3.0"
@@ -8396,7 +8391,6 @@
"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"
}
@@ -9092,7 +9086,6 @@
"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"
},
@@ -9907,7 +9900,6 @@
"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"
},
@@ -9918,8 +9910,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
},
"node_modules/regenerate": {
"version": "1.4.2",
@@ -10209,7 +10200,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true
+ "devOptional": true
},
"node_modules/sass": {
"version": "1.49.0",
@@ -10339,7 +10330,6 @@
"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"
},
@@ -10724,8 +10714,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"node_modules/spdy": {
"version": "4.0.2",
@@ -11126,7 +11115,6 @@
"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"
},
@@ -11218,7 +11206,6 @@
"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"
@@ -11858,8 +11845,7 @@
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yaml": {
"version": "1.10.2",
@@ -12194,7 +12180,6 @@
"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",
@@ -12212,7 +12197,6 @@
"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"
}
@@ -12221,7 +12205,6 @@
"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",
@@ -12243,8 +12226,7 @@
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
@@ -12252,7 +12234,6 @@
"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",
@@ -12262,8 +12243,7 @@
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
@@ -12408,7 +12388,6 @@
"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",
@@ -12430,14 +12409,12 @@
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
@@ -12445,7 +12422,6 @@
"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",
@@ -12455,8 +12431,7 @@
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
}
}
},
@@ -13617,7 +13592,8 @@
"ws": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz",
- "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A=="
+ "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==",
+ "requires": {}
}
}
},
@@ -13633,7 +13609,8 @@
"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
+ "dev": true,
+ "requires": {}
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
@@ -13792,6 +13769,12 @@
"@types/node": "*"
}
},
+ "@types/canvasjs": {
+ "version": "1.9.7",
+ "resolved": "https://registry.npmjs.org/@types/canvasjs/-/canvasjs-1.9.7.tgz",
+ "integrity": "sha512-/CfA3VtyTYVXDHvMy8F5hsvRUTiBX7TGe+nAHNONoJlNHbqakeIzxxt1MSNH619s8Oiu4CcFBMmJVg50El/0lw==",
+ "dev": true
+ },
"@types/component-emitter": {
"version": "1.2.11",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
@@ -14233,7 +14216,8 @@
"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
+ "dev": true,
+ "requires": {}
},
"adjust-sourcemap-loader": {
"version": "4.0.0",
@@ -14356,7 +14340,6 @@
"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"
@@ -14400,9 +14383,9 @@
"dev": true
},
"async": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
- "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
"dev": true,
"requires": {
"lodash": "^4.17.14"
@@ -14554,8 +14537,7 @@
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
- "dev": true
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
},
"bl": {
"version": "4.1.0",
@@ -14626,7 +14608,8 @@
"bootstrap": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz",
- "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q=="
+ "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==",
+ "requires": {}
},
"brace-expansion": {
"version": "1.1.11",
@@ -14641,7 +14624,6 @@
"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"
}
@@ -14745,6 +14727,11 @@
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz",
"integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ=="
},
+ "canvasjs": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/canvasjs/-/canvasjs-1.8.3.tgz",
+ "integrity": "sha512-60eUT0VjqRgYqdIQcOkXg0Zptfbl4HefA/O51YEf1m/P0uXvE3icI/1KPrXpY9aVxn8gG/BB8DzVoTGCcyBnYg=="
+ },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -14766,11 +14753,16 @@
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.1.tgz",
"integrity": "sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA=="
},
+ "chartjs-plugin-datalabels": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-0.7.0.tgz",
+ "integrity": "sha512-PKVUX14nYhH0wcdCpgOoC39Gbzvn6cZ7O9n+bwc02yKD9FTnJ7/TSrBcfebmolFZp1Rcicr9xbT0a5HUbigS7g==",
+ "requires": {}
+ },
"chokidar": {
"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",
@@ -14798,7 +14790,8 @@
"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
+ "dev": true,
+ "requires": {}
},
"clean-stack": {
"version": "2.2.0",
@@ -15276,7 +15269,8 @@
"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
+ "dev": true,
+ "requires": {}
},
"css-select": {
"version": "4.2.1",
@@ -15542,8 +15536,7 @@
"dependency-graph": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
- "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
- "dev": true
+ "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg=="
},
"destroy": {
"version": "1.0.4",
@@ -15678,7 +15671,6 @@
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
- "dev": true,
"optional": true,
"requires": {
"iconv-lite": "^0.6.2"
@@ -15688,7 +15680,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "dev": true,
"optional": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
@@ -16212,7 +16203,6 @@
"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"
}
@@ -16335,7 +16325,6 @@
"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": {
@@ -16411,7 +16400,6 @@
"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"
}
@@ -16668,7 +16656,8 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"ieee754": {
"version": "1.2.1",
@@ -16865,7 +16854,6 @@
"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"
}
@@ -16897,8 +16885,7 @@
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
@@ -16909,7 +16896,6 @@
"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"
}
@@ -16929,8 +16915,7 @@
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
},
"is-path-cwd": {
"version": "2.2.0",
@@ -17354,7 +17339,8 @@
"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
+ "dev": true,
+ "requires": {}
},
"karma-source-map-support": {
"version": "1.4.0",
@@ -17576,7 +17562,6 @@
"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"
}
@@ -17585,7 +17570,6 @@
"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"
}
@@ -17940,9 +17924,9 @@
}
},
"ng2-charts": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-3.0.8.tgz",
- "integrity": "sha512-ELlpN0b/IJO4ka/P2sFBKeng3bV7XOQuh40f0J5hx9UveWPaSxOYQAOiGxV7BN2VSnKq6GRkjRvqTrcQPyJYww==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-3.0.9.tgz",
+ "integrity": "sha512-VKUd0lMhDb/IC4WGYH/CW29HlMibXGMK+Ik/T+M0uR98Ox+YEQUPsrqbroF/cWMxVf2XuTkFGaUn2fbqlDBSOQ==",
"requires": {
"lodash-es": "^4.17.15",
"tslib": "^2.3.0"
@@ -18046,8 +18030,7 @@
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"normalize-range": {
"version": "0.1.2",
@@ -18580,8 +18563,7 @@
"picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
},
"pify": {
"version": "2.3.0",
@@ -18697,7 +18679,8 @@
"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
+ "dev": true,
+ "requires": {}
},
"postcss-custom-properties": {
"version": "12.1.4",
@@ -18767,13 +18750,15 @@
"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
+ "dev": true,
+ "requires": {}
},
"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
+ "dev": true,
+ "requires": {}
},
"postcss-image-set-function": {
"version": "4.0.6",
@@ -18799,7 +18784,8 @@
"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
+ "dev": true,
+ "requires": {}
},
"postcss-lab-function": {
"version": "4.1.1",
@@ -18826,19 +18812,22 @@
"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
+ "dev": true,
+ "requires": {}
},
"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
+ "dev": true,
+ "requires": {}
},
"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
+ "dev": true,
+ "requires": {}
},
"postcss-modules-local-by-default": {
"version": "4.0.0",
@@ -18882,13 +18871,15 @@
"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
+ "dev": true,
+ "requires": {}
},
"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
+ "dev": true,
+ "requires": {}
},
"postcss-place": {
"version": "7.0.4",
@@ -18953,7 +18944,8 @@
"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
+ "dev": true,
+ "requires": {}
},
"postcss-selector-not": {
"version": "5.0.0",
@@ -19127,7 +19119,6 @@
"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"
}
@@ -19135,8 +19126,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
},
"regenerate": {
"version": "1.4.2",
@@ -19355,7 +19345,7 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true
+ "devOptional": true
},
"sass": {
"version": "1.49.0",
@@ -19411,7 +19401,8 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"json-schema-traverse": {
"version": "0.4.1",
@@ -19440,7 +19431,6 @@
"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"
}
@@ -19756,8 +19746,7 @@
"sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
- "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
- "dev": true
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"spdy": {
"version": "4.0.2",
@@ -19965,7 +19954,8 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"json-schema-traverse": {
"version": "0.4.1",
@@ -20039,7 +20029,6 @@
"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"
}
@@ -20108,8 +20097,7 @@
"typescript": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
- "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
- "dev": true
+ "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA=="
},
"ua-parser-js": {
"version": "0.7.31",
@@ -20318,7 +20306,8 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"json-schema-traverse": {
"version": "0.4.1",
@@ -20554,7 +20543,8 @@
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
- "dev": true
+ "dev": true,
+ "requires": {}
},
"y18n": {
"version": "5.0.8",
@@ -20564,8 +20554,7 @@
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yaml": {
"version": "1.10.2",
diff --git a/frontend/package.json b/frontend/package.json
index c02a1fb0..87b21e05 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -28,14 +28,16 @@
"@ng-bootstrap/ng-bootstrap": "^12.0.0",
"@popperjs/core": "^2.10.2",
"bootstrap": "^5.1.3",
+ "canvasjs": "^1.8.3",
"chart.js": "^3.7.1",
+ "chartjs-plugin-datalabels": "^0.7.0",
"csv-parser": "^3.0.0",
"d3-graphviz": "^2.6.1",
"jquery": "^3.6.0",
"mdb-angular-ui-kit": "^2.0.0",
"ng-multiselect-dropdown": "^0.3.8",
"ng-uikit-pro-standard": "^1.0.0",
- "ng2-charts": "^3.0.8",
+ "ng2-charts": "^3.0.9",
"ng2-search-filter": "^0.5.1",
"ngx-cookie-service": "^13.1.2",
"popper.js": "^1.16.1",
@@ -48,6 +50,7 @@
"@angular-devkit/build-angular": "~13.2.5",
"@angular/cli": "~13.2.5",
"@angular/compiler-cli": "~13.2.0",
+ "@types/canvasjs": "^1.9.7",
"@types/crypto-js": "^4.1.1",
"@types/d3-graphviz": "^2.6.7",
"@types/jasmine": "~3.10.0",
diff --git a/frontend/src/app/_data/Dataset.ts b/frontend/src/app/_data/Dataset.ts
index 732d1c56..766040a3 100644
--- a/frontend/src/app/_data/Dataset.ts
+++ b/frontend/src/app/_data/Dataset.ts
@@ -10,7 +10,7 @@ export default class Dataset {
public accessibleByLink: boolean = false,
public dateCreated: Date = new Date(),
public lastUpdated: Date = new Date(),
- public username: string = '',
+ public uploaderId: string = '',
public delimiter: string = '',
public hasHeader: boolean = true,
diff --git a/frontend/src/app/_data/Experiment.ts b/frontend/src/app/_data/Experiment.ts
index 453f6ca0..95ef6e1e 100644
--- a/frontend/src/app/_data/Experiment.ts
+++ b/frontend/src/app/_data/Experiment.ts
@@ -1,3 +1,4 @@
+import { ProblemType } from "./Model";
export default class Experiment {
_id: string = '';
uploaderId: string = '';
@@ -18,8 +19,8 @@ export default class Experiment {
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
+ public encodings: ColumnEncoding[] = [],
+ public type: ProblemType = ProblemType.Regression
) { }
}
@@ -65,4 +66,12 @@ export enum Encoding {
WOE = 'woe',
Quantile = 'quantile'
*/
+}
+
+export class ColumnEncoding {
+ constructor (
+ public columnName: string,
+ public encoding: Encoding
+ )
+ {}
} \ No newline at end of file
diff --git a/frontend/src/app/_data/Model.ts b/frontend/src/app/_data/Model.ts
index 8a85e296..7d383584 100644
--- a/frontend/src/app/_data/Model.ts
+++ b/frontend/src/app/_data/Model.ts
@@ -16,10 +16,10 @@ export default class Model {
public inputNeurons: number = 1,
public hiddenLayerNeurons: number = 1,
public hiddenLayers: number = 1,
- public batchSize: number = 5,
+ public batchSize: number = 4,
public hiddenLayerActivationFunctions: string[] = ['sigmoid'],
public outputLayerActivationFunction: ActivationFunction = ActivationFunction.Sigmoid,
- public username: string = '',
+ public uploaderId: string = '',
public metrics: string[] = [], // TODO add to add-model form
public epochs: number = 5 // TODO add to add-model form
) { }
@@ -73,7 +73,7 @@ export enum LossFunction {
HingeLoss = 'hinge_loss',
// multi-class classification loss functions
CategoricalCrossEntropy = 'categorical_crossentropy',
- SparseCategoricalCrossEntropy = 'sparse_categorical_crosentropy',
+ SparseCategoricalCrossEntropy = 'sparse_categorical_crossentropy',
KLDivergence = 'kullback_leibler_divergence',
// regression loss functions
@@ -94,8 +94,8 @@ export enum LossFunctionBinaryClassification {
HingeLoss = 'hinge_loss',
}
export enum LossFunctionMultiClassification {
- CategoricalCrossEntropy = 'categorical_crossentropy',
- SparseCategoricalCrossEntropy = 'sparse_categorical_crosentropy',
+ //CategoricalCrossEntropy = 'categorical_crossentropy',
+ SparseCategoricalCrossEntropy = 'sparse_categorical_crossentropy',
KLDivergence = 'kullback_leibler_divergence',
}
diff --git a/frontend/src/app/_data/Notification.ts b/frontend/src/app/_data/Notification.ts
index c505d399..94a3be1d 100644
--- a/frontend/src/app/_data/Notification.ts
+++ b/frontend/src/app/_data/Notification.ts
@@ -1,5 +1,4 @@
export default class Notification {
- _id: string = '';
constructor(
public title: string = 'Treniranje u toku...',
public id: string = '042',
diff --git a/frontend/src/app/_data/Predictor.ts b/frontend/src/app/_data/Predictor.ts
index 7e902eae..8aa2b6cb 100644
--- a/frontend/src/app/_data/Predictor.ts
+++ b/frontend/src/app/_data/Predictor.ts
@@ -7,6 +7,7 @@ export default class Predictor {
public output: string = '',
public isPublic: boolean = false,
public accessibleByLink: boolean = false,
- public dateCreated: Date = new Date()
+ public dateCreated: Date = new Date(),
+ public uploaderId: string = ''
) { }
} \ No newline at end of file
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 bff8b022..e5d4cd23 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
@@ -2,48 +2,54 @@
<div class="col-2">
</div>
<div class="col-3">
- <label for="name" class="col-form-label">Naziv dataseta:</label>
- <input type="text" class="form-control mb-1" name="name" placeholder="Naziv..." [(ngModel)]="dataset.name">
+ <label for="name" class="col-form-label">Naziv dataseta:</label>
+ <input type="text" class="form-control mb-1" name="name" placeholder="Naziv..." [(ngModel)]="dataset.name">
- <label for="desc" class="col-sm-2 col-form-label">Opis:</label>
- <div>
- <textarea class="form-control" name="desc" rows="3" [(ngModel)]="dataset.description"></textarea>
- </div>
+ <label for="desc" class="col-sm-2 col-form-label">Opis:</label>
+ <div>
+ <textarea class="form-control" name="desc" rows="3" [(ngModel)]="dataset.description"></textarea>
+ </div>
- <label for="checkboxIsPublic" class="form-check-label mt-3 mb-1">Želite li da dataset bude javan?
+ <label for="checkboxIsPublic" class="form-check-label mt-3 mb-1">Želite li da dataset bude javan?
<input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="dataset.isPublic" (change)="checkAccessible()" type="checkbox"
value="" id="checkboxIsPublic">
- </label>
-
- <label for="checkboxAccessibleByLink" class="form-check-label">Želite li da bude deljiv linkom? &nbsp;
+ </label>
+
+ <label for="checkboxAccessibleByLink" class="form-check-label">Želite li da bude deljiv linkom? &nbsp;
<input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="dataset.accessibleByLink" type="checkbox"
value="" id="checkboxAccessibleByLink">
</label>
</div>
<div class="col-1">
</div>
- <div class="col-4 mt-4">
+ <div class="col-3 mt-4">
- <input list="delimiterOptions" placeholder="Izaberite ili ukucajte delimiter za .csv fajl" class="form-control mt-2"
- [(ngModel)]="dataset.delimiter" (input)="update()">
- <datalist id="delimiterOptions">
+ <label for="type" class="col-form-label">Izaberite delimiter za .csv fajl</label>
+ <select id="delimiterOptions" class="form-select" name="type" [(ngModel)]="dataset.delimiter" (change)="update()">
+ <option
+ *ngFor="let option of delimiterOptions">
+ {{ option }}
+ </option>
+ </select>
+ <!--
+ <input list="delimiterOptions" placeholder="Izaberite ili ukucajte delimiter za .csv fajl" class="form-control mt-2" [(ngModel)]="dataset.delimiter" (input)="update()">
+ <datalist id="delimiterOptions">
<option *ngFor="let option of delimiterOptions">{{option}}</option>
</datalist>
-
- <label for="type" class="form-check-label my-5">Da li .csv ima header?
+-->
+ <label for="type" class="form-check-label my-5">Da li .csv ima header?
<input class="mx-3 form-check-input" type="checkbox" (input)="update()" [(ngModel)]="dataset.hasHeader" type="checkbox"
value="" id="checkboxHeader" checked>
</label>
- <br>
- <input id="fileInput" class="form-control" type="file" class="upload" (change)="changeListener($event)"
- accept=".csv">
+ <br>
+ <input id="fileInput" class="form-control btn-lg" type="file" class="upload" (change)="changeListener($event)" accept=".csv">
</div>
</div>
<div class="px-5 mt-5">
- <app-datatable [tableData]="tableData"></app-datatable>
+ <app-datatable [tableData]="tableData"></app-datatable>
</div>
<div class="d-flex flex-row align-items-center justify-content-center w-100 my-2">
- <button (click)="uploadDataset()" class="btn btn-lg col-4" style="background-color:#003459; color:white;">Dodaj izvor podataka</button>
-</div>
+ <button (click)="uploadDataset()" class="btn btn-lg col-4" style="background-color:#003459; color:white;">Dodaj izvor podataka</button>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts
index 6ff108ce..1f395105 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
@@ -13,10 +13,9 @@ import { CsvParseService } from 'src/app/_services/csv-parse.service';
})
export class AddNewDatasetComponent {
- @Output() newDatasetAdded = new EventEmitter<string>();
@ViewChild(DatatableComponent) datatable!: DatatableComponent;
- delimiterOptions: Array<string> = [",", ";", "\t", "razmak", "|"]; //podrazumevano ","
+ delimiterOptions: Array<string> = [",", ";", "|", "razmak", "novi red"]; //podrazumevano ","
csvRecords: any[] = [];
files: File[] = [];
@@ -29,6 +28,7 @@ export class AddNewDatasetComponent {
constructor(private modelsService: ModelsService, private datasetsService: DatasetsService, private csv: CsvParseService) {
this.dataset = new Dataset();
+ this.dataset.delimiter = ',';
}
//@ViewChild('fileImportInput', { static: false }) fileImportInput: any; cemu je ovo sluzilo?
@@ -36,8 +36,6 @@ export class AddNewDatasetComponent {
changeListener($event: any): void {
this.files = $event.srcElement.files;
if (this.files.length == 0 || this.files[0] == null) {
- //console.log("NEMA FAJLA");
- //this.loaded.emit("not loaded");
this.tableData.hasInput = false;
return;
}
@@ -56,7 +54,7 @@ export class AddNewDatasetComponent {
const fileReader = new FileReader();
fileReader.onload = (e) => {
if (typeof fileReader.result === 'string') {
- const result = this.csv.csvToArray(fileReader.result, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter)
+ const result = this.csv.csvToArray(fileReader.result, (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "novi red") ? "\t" : this.dataset.delimiter)
if (this.dataset.hasHeader)
this.csvRecords = result.splice(0, 11);
@@ -90,10 +88,9 @@ 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.dataset.uploaderId = shared.userId;
this.datasetsService.addDataset(this.dataset).subscribe((dataset) => {
- this.newDatasetAdded.emit("added");
shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se procesira.");
}, (error) => {
shared.openDialog("Neuspeo pokušaj!", "Izvor podataka sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeći dataset.");
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 56a3b3c9..f244a882 100644
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.html
+++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.html
@@ -1,40 +1,34 @@
<div>
- <!--Sklonjeno ucitavanje novog dataseta i sve opcije u vezi sa tim, premesteno u add-new-dataset-->
+ <!--Sklonjeno ucitavanje novog dataseta i sve opcije u vezi sa tim, premesteno u add-new-dataset-->
<div class="d-flex flex-row justify-content-center align-items-center mt-3 mb-5">
- <button type="button" id="btnMyDataset" class="btn" (click)="viewMyDatasetsForm()"
- [ngClass]="{'btnType1': showMyDatasets, 'btnType2': !showMyDatasets}">
+ <button type="button" id="btnMyDataset" class="btn" (click)="viewMyDatasetsForm()" [ngClass]="{'btnType1': showMyDatasets, 'btnType2': !showMyDatasets}">
Izaberite dataset iz kolekcije
</button>
<h3 class="mt-3 mx-3">ili</h3>
- <button type="button" id="btnNewDataset" class="btn" (click)="viewNewDatasetForm()"
- [ngClass]="{'btnType1': !showMyDatasets, 'btnType2': showMyDatasets}">
+ <button type="button" id="btnNewDataset" class="btn" (click)="viewNewDatasetForm()" [ngClass]="{'btnType1': !showMyDatasets, 'btnType2': showMyDatasets}">
Dodajte novi dataset
</button>
</div>
- <div class="px-5 my-2">
- <input *ngIf="showMyDatasets" type="text" class="form-control" placeholder="Pretraga"
- [(ngModel)]="term">
- </div>
- <div class="px-5" *ngIf="showMyDatasets">
+ <div class="px-5 my-2">
+ <input *ngIf="showMyDatasets" type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term">
+ </div>
+ <div class="px-5" *ngIf="showMyDatasets">
<div class="overflow-auto" style="max-height: 500px;">
<ul class="list-group">
- <li class="list-group-item p-3" *ngFor="let dataset of myDatasets|filter:term"
- [ngClass]="{'selectedDatasetClass': this.selectedDataset == dataset}">
- <app-item-dataset name="usersDataset" [dataset]="dataset"
- (click)="selectThisDataset(dataset);"></app-item-dataset>
+ <li class="list-group-item p-3" *ngFor="let dataset of myDatasets|filter:term" [ngClass]="{'selectedDatasetClass': this.selectedDataset == dataset}">
+ <app-item-dataset name="usersDataset" [dataset]="dataset" (click)="selectThisDataset(dataset);"></app-item-dataset>
</li>
</ul>
</div>
<div class="px-5 mt-5">
<app-datatable [tableData]="tableData"></app-datatable>
</div>
- </div>
+ </div>
- <app-add-new-dataset [style]="(showMyDatasets)?'display:none;visibility:hidden;':''" id="dataset"
- (newDatasetAdded)="refreshMyDatasets()">
- </app-add-new-dataset>
+ <app-add-new-dataset [style]="(showMyDatasets)?'display:none;visibility:hidden;':''" id="dataset">
+ </app-add-new-dataset>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
index 62cca456..be1dc097 100644
--- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
+++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts
@@ -8,6 +8,7 @@ import { DatasetsService } from 'src/app/_services/datasets.service';
import { CsvParseService } from 'src/app/_services/csv-parse.service';
import { Output, EventEmitter } from '@angular/core';
import { SignalRService } from 'src/app/_services/signal-r.service';
+import { AuthService } from 'src/app/_services/auth.service';
@Component({
selector: 'app-dataset-load',
@@ -33,7 +34,15 @@ export class DatasetLoadComponent implements OnInit {
term: string = "";
- constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService) {
+ constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService, private signalRService: SignalRService, private authService: AuthService) {
+ this.fetchDatasets();
+
+ authService.loggedInEvent.subscribe(_ => {
+ this.fetchDatasets();
+ })
+ }
+
+ fetchDatasets() {
this.datasets.getMyDatasets().subscribe((datasets) => {
this.myDatasets = datasets;
});
@@ -41,22 +50,17 @@ export class DatasetLoadComponent implements OnInit {
viewMyDatasetsForm() {
this.showMyDatasets = true;
- this.resetSelectedDataset();
+ if (this.selectedDataset != undefined)
+ this.resetSelectedDataset();
//this.resetCbsAndRbs(); //TREBA DA SE DESI
}
viewNewDatasetForm() {
this.showMyDatasets = false;
- this.resetSelectedDataset();
+ if (this.selectedDataset != undefined)
+ this.resetSelectedDataset();
//this.resetCbsAndRbs(); //TREBA DA SE DESI
}
- refreshMyDatasets() {
- this.datasets.getMyDatasets().subscribe((datasets) => {
- this.myDatasets = datasets;
- this.showMyDatasets = true;
- });
- }
-
selectThisDataset(dataset: Dataset) {
this.selectedDataset = dataset;
this.selectedDatasetLoaded = false;
@@ -86,10 +90,19 @@ export class DatasetLoadComponent implements OnInit {
return true;
}
+ refreshMyDatasets(selectedDatasetId: string | null) {
+ this.datasets.getMyDatasets().subscribe((datasets) => {
+ this.myDatasets = datasets.reverse();
+ this.showMyDatasets = true;
+ this.selectedDataset = this.myDatasets.filter(x => x._id == selectedDatasetId)[0];
+ this.resetSelectedDataset();
+ });
+ }
+
ngOnInit(): void {
if (this.signalRService.hubConnection) {
- this.signalRService.hubConnection.on("NotifyDataset", _ => {
- this.refreshMyDatasets();
+ this.signalRService.hubConnection.on("NotifyDataset", (dName: string, dId: string) => {
+ this.refreshMyDatasets(dId);
});
} else {
console.warn("Dataset-Load: No connection!");
diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.html b/frontend/src/app/_elements/item-predictor/item-predictor.component.html
index 7ae26fd3..3199dcc8 100644
--- a/frontend/src/app/_elements/item-predictor/item-predictor.component.html
+++ b/frontend/src/app/_elements/item-predictor/item-predictor.component.html
@@ -1,26 +1,35 @@
<div class="card" style="min-width: 12rem;">
- <div class="card-header">
- {{predictor.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;">Prediktor</b></div>
+
</div>
- <div class="card-body">
+ <div class="card-body overflow-hidden">
+ <b style="color: gray;">Opis</b><hr style="width: 20%;">
<p class="card-text">
- {{predictor.description}}
+ {{predictor.description}}
</p>
- <div class="d-flex flex-column align-items-center">
- <table class="table table-bordered table-sm">
+
+ <b style="color: gray;">Ulazne kolone</b>
+ <div style="overflow: scroll; overflow-y: hidden;">
+
+ <table class="table table-bordered table-md" >
<thead>
- <th class="text-center" *ngFor="let column of predictor.inputs">{{column}}</th>
+ <th scope="col" *ngFor="let column of predictor.inputs" >{{column}}</th>
</thead>
</table>
- <mat-icon>arrow_downward</mat-icon>
- <p>
- {{predictor.output}}
- </p>
+ </div>
+ <b style="color: gray;">Izlazna kolona: </b><b>{{predictor.output}}</b>
+ <hr>
+ <div>
+ <table>
+ <tr><td><span class="material-icons">calendar_today</span></td><td><span style="color: grey;"> <b> Kreirano</b></span></td><td>{{predictor.dateCreated |date}}</td></tr>
+ </table>
</div>
</div>
<div class="card-footer text-center">
- <button class="btn btn-lg col-4" style="background-color:#003459; color:white;"
+ <button class="btn btn-md col-4" style="background-color:#003459; color:white;"
(click)="openPredictor();">Iskoristi</button>
- <!--<a routerLink="/predict" mat-raised-button color="primary">Iskoristi</a>-->
+
</div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.css b/frontend/src/app/_elements/line-chart/line-chart.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_elements/line-chart/line-chart.component.css
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.html b/frontend/src/app/_elements/line-chart/line-chart.component.html
new file mode 100644
index 00000000..c8f406f4
--- /dev/null
+++ b/frontend/src/app/_elements/line-chart/line-chart.component.html
@@ -0,0 +1,5 @@
+<div class="chart-wrapper">
+ <canvas id="myChart">
+
+ </canvas>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.spec.ts b/frontend/src/app/_elements/line-chart/line-chart.component.spec.ts
new file mode 100644
index 00000000..0c5e7ef5
--- /dev/null
+++ b/frontend/src/app/_elements/line-chart/line-chart.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LineChartComponent } from './line-chart.component';
+
+describe('LineChartComponent', () => {
+ let component: LineChartComponent;
+ let fixture: ComponentFixture<LineChartComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ LineChartComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LineChartComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/line-chart/line-chart.component.ts b/frontend/src/app/_elements/line-chart/line-chart.component.ts
new file mode 100644
index 00000000..1a8579a0
--- /dev/null
+++ b/frontend/src/app/_elements/line-chart/line-chart.component.ts
@@ -0,0 +1,88 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { Chart } from 'chart.js';
+
+@Component({
+ selector: 'app-line-chart',
+ templateUrl: './line-chart.component.html',
+ styleUrls: ['./line-chart.component.css']
+})
+
+export class LineChartComponent implements OnInit {
+
+ dataAcc: number[] = [];
+ dataMAE: number[] = [];
+ dataMSE: number[] = [];
+ dataLOSS: number[] = [];
+
+ dataEpoch: number[] = [];
+
+ constructor() {
+ /*let i = 0;
+ setInterval(() => {
+ this.dataAcc.push(0.5);
+ this.dataEpoch.push(i);
+ i++;
+ this.update();
+ }, 200);*/
+ }
+
+ myChart!: Chart;
+
+ update(myEpochs: number[], myAcc: number[], myLoss: number[], myMae: number[], myMse: number[]) {
+ this.dataAcc.length = 0;
+ this.dataAcc.push(...myAcc);
+
+ this.dataEpoch.length = 0;
+ this.dataEpoch.push(...myEpochs);
+
+ this.dataMAE.length = 0;
+ this.dataMAE.push(...myMae);
+
+ this.dataLOSS.length = 0;
+ this.dataLOSS.push(...myLoss);
+
+ this.dataMSE.length = 0;
+ this.dataMSE.push(...myMse);
+
+ this.myChart.update();
+ }
+
+ ngOnInit(): void {
+ this.myChart = new Chart("myChart",
+ {
+ type: 'line',
+ data: {
+ labels: this.dataEpoch,
+ datasets: [{
+ label: 'Accuracy',
+ data: this.dataAcc,
+ borderWidth: 1
+ },
+ {
+ label: 'Loss',
+ data: this.dataLOSS,
+ borderWidth: 1
+ },
+ {
+ label: 'MAE',
+ data: this.dataMAE,
+ borderWidth: 1
+ },
+ {
+ label: 'MSE',
+ data: this.dataMSE,
+ borderWidth: 1
+ }
+ ]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ }
+ );
+ }
+}
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.css b/frontend/src/app/_elements/metric-view/metric-view.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.css
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.html b/frontend/src/app/_elements/metric-view/metric-view.component.html
new file mode 100644
index 00000000..e7a4c547
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.html
@@ -0,0 +1,5 @@
+<div>
+ <app-line-chart>
+
+ </app-line-chart>
+</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.spec.ts b/frontend/src/app/_elements/metric-view/metric-view.component.spec.ts
new file mode 100644
index 00000000..c3ecc67f
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MetricViewComponent } from './metric-view.component';
+
+describe('MetricViewComponent', () => {
+ let component: MetricViewComponent;
+ let fixture: ComponentFixture<MetricViewComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ MetricViewComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MetricViewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/_elements/metric-view/metric-view.component.ts b/frontend/src/app/_elements/metric-view/metric-view.component.ts
new file mode 100644
index 00000000..9193a0e5
--- /dev/null
+++ b/frontend/src/app/_elements/metric-view/metric-view.component.ts
@@ -0,0 +1,49 @@
+import { Component, Input, OnInit, ViewChild } from '@angular/core';
+import { SignalRService } from 'src/app/_services/signal-r.service';
+import { LineChartComponent } from '../line-chart/line-chart.component';
+@Component({
+ selector: 'app-metric-view',
+ templateUrl: './metric-view.component.html',
+ styleUrls: ['./metric-view.component.css']
+})
+export class MetricViewComponent implements OnInit {
+ @ViewChild(LineChartComponent) linechartComponent!: LineChartComponent;
+
+ @Input() history!: any[];
+
+ constructor(private signalRService: SignalRService) { }
+
+ ngOnInit(): void {
+ }
+
+ update(history: any[]) {
+ const myAcc: number[] = [];
+ const myMae: number[] = [];
+ const myMse: number[] = [];
+ const myLoss: number[] = [];
+
+ const myEpochs: number[] = [];
+ this.history = history;
+ this.history.forEach((metrics, epoch) => {
+ myEpochs.push(epoch + 1);
+ for (let key in metrics) {
+ let value = metrics[key];
+ console.log(key, ':::', value, epoch);
+ if (key === 'accuracy') {
+ myAcc.push(parseFloat(value));
+ }
+ else if (key === 'loss') {
+ myLoss.push(parseFloat(value));
+ }
+ else if (key === 'mae') {
+ myMae.push(parseFloat(value));
+ }
+ else if (key === 'mse') {
+ myMse.push(parseFloat(value));
+ }
+ }
+ });
+
+ this.linechartComponent.update(myEpochs, myAcc, myLoss, myMae, myMse);
+ }
+} \ No newline at end of file
diff --git a/frontend/src/app/_elements/model-load/model-load.component.html b/frontend/src/app/_elements/model-load/model-load.component.html
index f40ea476..1f9852d1 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.html
+++ b/frontend/src/app/_elements/model-load/model-load.component.html
@@ -1,12 +1,10 @@
<div>
<div class="d-flex flex-row justify-content-center align-items-center mt-3 mb-5">
- <button type="button" id="btnMyModel" class="btn" (click)="viewMyModelsForm()"
- [ngClass]="{'btnType1': showMyModels, 'btnType2': !showMyModels}">
+ <button type="button" id="btnMyModel" class="btn" (click)="viewMyModelsForm()" [ngClass]="{'btnType1': showMyModels, 'btnType2': !showMyModels}">
Izaberite model iz kolekcije
</button>
<h3 class="mt-3 mx-3">ili</h3>
- <button type="button" id="btnNewModel" class="btn" (click)="viewNewModelForm()"
- [ngClass]="{'btnType1': !showMyModels, 'btnType2': showMyModels}">
+ <button type="button" id="btnNewModel" class="btn" (click)="viewNewModelForm()" [ngClass]="{'btnType1': !showMyModels, 'btnType2': showMyModels}">
Dodajte novi model
</button>
</div>
@@ -17,8 +15,7 @@
<div *ngIf="showMyModels" class="px-5">
<div class="overflow-auto" style="max-height: 500px;">
<ul class="list-group">
- <li class="list-group-item p-3" *ngFor="let model of myModels|filter:term"
- [ngClass]="{'selectedModelClass': this.selectedModel == model}">
+ <li class="list-group-item p-3" *ngFor="let model of myModels|filter:((forExperiment != undefined) ? forExperiment.type : '')" [ngClass]="{'selectedModelClass': this.selectedModel == model}">
<app-item-model name="usersModel" [model]="model" (click)="selectThisModel(model);">
</app-item-model>
</li>
@@ -43,11 +40,7 @@
<textarea class="form-control" name="desc" rows="3" [(ngModel)]="newModel.description"></textarea>
</div>
</div>
- <div class="col-2">
- <label for="dateCreated" class="col-form-label">Datum:</label> &nbsp;&nbsp;
- <input type="text" class="form-control-plaintext" id="dateCreated" placeholder="--/--/--"
- value="{{newModel.dateCreated | date: 'dd/MM/yyyy'}}" readonly>
- </div>
+
</div>
<h2 class="mt-5 mb-4 mx-5">Parametri treniranja modela:</h2>
<div>
@@ -58,8 +51,7 @@
<label for="type" class="col-form-label">Tip problema: </label>
</div>
<div class="col-2">
- <select id=typeOptions class="form-control" name="type" [(ngModel)]="newModel.type"
- (change)="filterOptions()">
+ <select id="typeOptions" class="form-select" name="type" [(ngModel)]="newModel.type" (change)="filterOptions()">
<option
*ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)"
[value]="option">
@@ -72,10 +64,7 @@
<label for="hiddenLayers" class="col-form-label">Broj skrivenih slojeva: </label>
</div>
<div class="col-1">
- <input type="number" min="1" class="form-control" name="hiddenLayers"
- [(ngModel)]="newModel.hiddenLayers"
- (change)="newModel.hiddenLayerActivationFunctions = [].constructor(newModel.hiddenLayers).fill(newModel.hiddenLayerActivationFunctions[0])"
- (ngModelChange)="updateGraph()">
+ <input type="number" min="1" class="form-control" name="hiddenLayers" [(ngModel)]="newModel.hiddenLayers" (change)="newModel.hiddenLayerActivationFunctions = [].constructor(newModel.hiddenLayers).fill(newModel.hiddenLayerActivationFunctions[0])" (ngModelChange)="updateGraph()">
</div>
</div>
@@ -86,7 +75,7 @@
<label for="optimizer" class="col-form-label">Optimizacija: </label>
</div>
<div class="col-2">
- <select id=optimizerOptions class="form-control" name="optimizer" [(ngModel)]="newModel.optimizer">
+ <select id="optimizerOptions" class="form-select" name="optimizer" [(ngModel)]="newModel.optimizer">
<option
*ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)"
[value]="option">
@@ -100,19 +89,17 @@
<label for="hiddenLayerNeurons" class="col-form-label">Broj neurona skrivenih slojeva: </label>
</div>
<div class="col-1">
- <input type="number" min="1" class="form-control" name="hiddenLayerNeurons"
- [(ngModel)]="newModel.hiddenLayerNeurons" (ngModelChange)="updateGraph()">
+ <input type="number" min="1" class="form-control" name="hiddenLayerNeurons" [(ngModel)]="newModel.hiddenLayerNeurons" (ngModelChange)="updateGraph()">
</div>
</div>
<div class="row p-2">
<div class="col-1"></div>
<div class="col-3">
- <label for="lossFunction" class="col-form-label">Funkcija obrade gubitka: </label>
+ <label for="lossFunction" class="col-form-label">Funkcija troška: </label>
</div>
<div class="col-2">
- <select id=lossFunctionOptions class="form-control" name="lossFunction"
- [(ngModel)]="newModel.lossFunction" aria-checked="true">
+ <select id="lossFunctionOptions" class="form-select" name="lossFunction" [(ngModel)]="newModel.lossFunction" aria-checked="true">
<option
*ngFor="let option of Object.keys(lossFunction); let optionName of Object.values(lossFunction)"
[value]="option">
@@ -122,10 +109,22 @@
</div>
<div class="col-1"></div>
<div class="col-3">
- <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label>
+ <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji:&nbsp;<b>{{newModel.batchSize}}</b><br>(izaberite stepen dvojke)</label>
</div>
<div class="col-1">
- <input type="number" min="1" class="form-control" name="batchSize" [(ngModel)]="newModel.batchSize">
+
+ <input type="number" min="0" step="1" max="7" class="form-control" name="batchSizePower" [(ngModel)]="batchSizePower" (click)="updateBatchSize()">
+
+ </div>
+
+ <div class="row p-2">
+ <div class="col-1"></div>
+ <div class="col-3 m-1">
+ <label for="epochs" class="col-form-label">Broj epoha: </label>
+ </div>
+ <div class="col-1">
+ <input type="number" min="1" max="1000" class="form-control" name="epochs" [(ngModel)]="newModel.epochs">
+ </div>
</div>
</div>
@@ -138,8 +137,7 @@
<div class="row p-2" style="align-self: center;">
<div class="col-1"></div>
<div class="col-3">
- <label for="hiddenLayerActivationFunction" class="col-form-label"
- style="text-align: center;">Funkcija aktivacije<br>skrivenih slojeva:</label>
+ <label for="hiddenLayerActivationFunction" class="col-form-label" style="text-align: center;">Funkcija aktivacije<br>skrivenih slojeva:</label>
</div>
<div class="col-2 mt-2">
<div *ngFor="let item of [].constructor(newModel.hiddenLayers); let i = index">
@@ -147,8 +145,7 @@
<div class="input-group-prepend">
<span class="input-group-text">#{{i+1}}</span>
</div>
- <select [id]="'hiddenLayerActivationFunctionOption_'+i" class="form-control"
- [(ngModel)]="newModel.hiddenLayerActivationFunctions[i]">
+ <select [id]="'hiddenLayerActivationFunctionOption_'+i" class="form-select" [(ngModel)]="newModel.hiddenLayerActivationFunctions[i]">
<option
*ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)"
[value]="option">
@@ -160,12 +157,10 @@
</div>
<div class="col-1"></div>
<div class="col-2">
- <label for="outputLayerActivationFunction" class="col-form-label"
- style="text-align: center;">Funkcija aktivacije<br>izlaznog sloja:</label>
+ <label for="outputLayerActivationFunction" class="col-form-label" style="text-align: center;">Funkcija aktivacije<br>izlaznog sloja:</label>
</div>
<div class="col-2 mt-2">
- <select id=outputLayerActivationFunctionOptions class="form-control"
- name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction">
+ <select id="outputLayerActivationFunctionOptions" class="form-select" name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction">
<option
*ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)"
[value]="option">
@@ -178,26 +173,23 @@
</div>
</div>
- <div class="form-check form-check-inline overflow-auto m-4" style="width: max-content;">
+ <!--<div class="form-check form-check-inline overflow-auto m-4" style="width: max-content;">
<h3>Izaberite metrike:</h3>
<div id="divMetricsinput" class="mt-2 mx-5">
- <div *ngFor="let option of Object.keys(metrics); let optionName of Object.values(metrics) "
- class="form-check form-check-inline">
+ <div *ngFor="let option of Object.keys(metrics); let optionName of Object.values(metrics) " class="form-check form-check-inline">
- <input name="cbmetrics" class="form-check-input" type="checkbox" value="{{option}}"
- id="metrics_{{option}}" style="float: left;" checked>
+ <input name="cbmetrics" class="form-check-input" type="checkbox" value="{{option}}" id="metrics_{{option}}" style="float: left;" checked>
<label class="form-check-label" for="metrics_{{option}}" for="inlineCheckbox2">
{{optionName}}
</label>
</div>
</div>
- </div>
-
+ </div>-->
+
<div class="form-group row mt-3 mb-3">
<div class="col"></div>
- <button class="btn btn-lg col-4" style="background-color:#003459; color:white;"
- (click)="uploadModel();">Sačuvaj
+ <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="uploadModel();">Sačuvaj
model</button>
<div class="col"></div>
</div>
diff --git a/frontend/src/app/_elements/model-load/model-load.component.ts b/frontend/src/app/_elements/model-load/model-load.component.ts
index 745dc12e..fb4b3fd0 100644
--- a/frontend/src/app/_elements/model-load/model-load.component.ts
+++ b/frontend/src/app/_elements/model-load/model-load.component.ts
@@ -1,7 +1,10 @@
-import { Component, OnInit, ViewChild, Output, EventEmitter } from '@angular/core';
+import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from '@angular/core';
import Shared from 'src/app/Shared';
+import Experiment from 'src/app/_data/Experiment';
import Model, { ActivationFunction, LossFunction, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType } from 'src/app/_data/Model';
+import { AuthService } from 'src/app/_services/auth.service';
import { ModelsService } from 'src/app/_services/models.service';
+import { SignalRService } from 'src/app/_services/signal-r.service';
import { GraphComponent } from '../graph/graph.component';
@@ -13,6 +16,7 @@ import { GraphComponent } from '../graph/graph.component';
export class ModelLoadComponent implements OnInit {
@ViewChild(GraphComponent) graph!: GraphComponent;
+ @Input() forExperiment?: Experiment;
@Output() selectedModelChangeEvent = new EventEmitter<Model>();
newModel: Model = new Model();
@@ -29,21 +33,44 @@ export class ModelLoadComponent implements OnInit {
shared = Shared;
term: string = "";
- selectedProblemType: string = '';
selectedMetrics = [];
lossFunction: any = LossFunction;
showMyModels: boolean = true;
- constructor(private modelsService: ModelsService) {
+ batchSizePower: number = 2;
+
+ constructor(private modelsService: ModelsService, private authService: AuthService) {
+ //console.log("forExperiment = ", this.forExperiment);
+ this.fetchModels();
+
+ this.authService.loggedInEvent.subscribe(_ => {
+ this.fetchModels();
+ })
+ }
+
+ fetchModels(andSelectWithId: string | null = '') {
+ //if (this.forExperiment == undefined) {
this.modelsService.getMyModels().subscribe((models) => {
- this.myModels = models;
+ this.myModels = models.reverse();
+ this.selectThisModel(this.myModels.filter(x => x._id == andSelectWithId)[0]);
});
+ /*}
+ else {
+ this.modelsService.getMyModelsByType(ProblemType.Regression).subscribe((models) => {
+ this.myModels = models;
+ //console.log("modeli po tipu: ", this.myModels);
+ });
+ }*/
}
ngOnInit(): void {
}
+ updateBatchSize() {
+ this.newModel.batchSize = 2 ** this.batchSizePower;
+ }
+
updateGraph() {
this.graph.update();
}
@@ -62,12 +89,17 @@ export class ModelLoadComponent implements OnInit {
uploadModel() {
this.getMetrics();
- this.newModel.username = Shared.username;
+ this.newModel.uploaderId = Shared.userId;
this.modelsService.addModel(this.newModel).subscribe((response) => {
- Shared.openDialog('Model dodat', 'Model je uspešno dodat u bazu.');
- // treba da se selektuje nov model u listi modela
- //this.selectedModel =
+ console.log(this.newModel);
+ //Shared.openDialog('Model dodat', 'Model je uspešno dodat u bazu.');
+
+ Shared.openYesNoDialog("Model dodat", "Model je uspešno dodat u bazu. Da li želite da nastavite treniranje sa dodatim modelom?", () => {
+ this.fetchModels(response._id);
+ this.showMyModels = true;
+ });
+ this.fetchModels();
}, (error) => {
Shared.openDialog('Greška', 'Model sa unetim nazivom već postoji u Vašoj kolekciji. Promenite naziv modela i nastavite sa kreiranim datasetom.');
});
diff --git a/frontend/src/app/_elements/navbar/navbar.component.html b/frontend/src/app/_elements/navbar/navbar.component.html
index 7d0c4cd8..1988b834 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.html
+++ b/frontend/src/app/_elements/navbar/navbar.component.html
@@ -6,31 +6,25 @@
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
- <li><a routerLink="" class="nav-link px-2"
- [class]="(currentUrl === '') ? 'text-secondary' : 'text-white'">Početna</a></li>
- <li><a routerLink="experiment" class="nav-link px-2"
- [class]="(currentUrl === '/experiment') ? 'text-secondary' : 'text-white'">Napravi eksperiment</a>
+ <li><a routerLink="" class="nav-link px-2" [class]="(currentUrl === '') ? 'text-secondary' : 'text-white'">Početna</a></li>
+ <li><a routerLink="experiment" class="nav-link px-2" [class]="(currentUrl === '/experiment') ? 'text-secondary' : 'text-white'">Napravi eksperiment</a>
</li>
- <li><a routerLink="training" class="nav-link px-2"
- [class]="(currentUrl === '/training') ? 'text-secondary' : 'text-white'">Treniraj model</a>
+ <li><a routerLink="training" class="nav-link px-2" [class]="(currentUrl === '/training') ? 'text-secondary' : 'text-white'">Treniraj model</a>
</li>
- <li><a routerLink="my-predictors" class="nav-link px-2"
- [class]="(currentUrl === '/my-predictors') ? 'text-secondary' : 'text-white' + (shared.loggedIn) ? '' : 'disabled'">Predvidi</a>
+ <li><a routerLink="my-predictors" class="nav-link px-2" [class]="(currentUrl === '/my-predictors') ? 'text-secondary' : 'text-white' + (shared.loggedIn) ? '' : 'disabled'">Predvidi</a>
</li>
</ul>
<div *ngIf="shared.loggedIn" class="dropdown text-end">
- <a href="#" class="d-block link-light text-decoration-none dropdown-toggle" id="dropdownUser1"
- data-bs-toggle="dropdown" aria-expanded="false">
- <img [src]="'/assets/profilePictures/'+ shared.photoId +'.png'" alt="mdo" width="32" height="32"
- class="rounded-circle">
+ <a href="#" class="d-block link-light text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
+ <img [src]="'/assets/profilePictures/'+ shared.photoId +'.png'" alt="mdo" width="32" height="32" class="rounded-circle">
</a>
- <ul class="dropdown-menu text-small" aria-labelledby="dropdownUser1"
- style="position: absolute; inset: 0px 0px auto auto; margin: 0px; transform: translate(0px, 34px);"
- data-popper-placement="bottom-end">
- <li><a class="dropdown-item" routerLink="add-model">Nov model...</a></li>
- <li><a class="dropdown-item" routerLink="settings">Podešavanja</a></li>
+ <ul class="dropdown-menu text-small" aria-labelledby="dropdownUser1" style="position: absolute; inset: 0px 0px auto auto; margin: 0px; transform: translate(0px, 34px);" data-popper-placement="bottom-end">
+ <li><a class="dropdown-item" routerLink="my-datasets">Moji izvori podataka</a></li>
+ <li><a class="dropdown-item" routerLink="my-models">Moji modeli</a></li>
+ <li><a class="dropdown-item" routerLink="my-predictors">Moji prediktori</a></li>
<li><a class="dropdown-item" routerLink="profile">Moj profil</a></li>
+ <li><a class="dropdown-item" routerLink="settings" disabled>Podešavanja</a></li>
<li>
<hr class="dropdown-divider">
</li>
@@ -38,12 +32,10 @@
</ul>
</div>
<div *ngIf="!shared.loggedIn" class="dropdown text-end">
- <button type="button" mat-raised-button color="primary" class="mx-2" data-bs-toggle="modal"
- data-bs-target="#modalForLogin">
+ <button type="button" mat-raised-button color="primary" class="mx-2" data-bs-toggle="modal" data-bs-target="#modalForLogin">
Prijavi se
</button>
- <button type="button" mat-raised-button color="primary" data-bs-toggle="modal"
- data-bs-target="#modalForRegister">
+ <button type="button" mat-raised-button color="primary" data-bs-toggle="modal" data-bs-target="#modalForRegister">
Registruj se
</button>
</div>
diff --git a/frontend/src/app/_elements/navbar/navbar.component.ts b/frontend/src/app/_elements/navbar/navbar.component.ts
index 368508ed..d5d1744f 100644
--- a/frontend/src/app/_elements/navbar/navbar.component.ts
+++ b/frontend/src/app/_elements/navbar/navbar.component.ts
@@ -4,6 +4,7 @@ import { AuthService } from '../../_services/auth.service';
import shared from 'src/app/Shared';
import { UserInfoService } from 'src/app/_services/user-info.service';
import { MatDialog } from '@angular/material/dialog';
+import { SignalRService } from 'src/app/_services/signal-r.service';
@Component({
selector: 'app-navbar',
@@ -15,11 +16,15 @@ export class NavbarComponent implements OnInit {
currentUrl: string;
shared = shared;
- constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService, private matDialog: MatDialog) {
+ constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService, private matDialog: MatDialog, private signalRService: SignalRService) {
shared.dialog = matDialog;
this.currentUrl = this.location.path();
this.location.onUrlChange(() => {
this.currentUrl = this.location.path();
+ });
+
+ this.auth.loggedInEvent.subscribe(_ => {
+ this.signalRService.startConnection();
})
}
@@ -34,5 +39,6 @@ export class NavbarComponent implements OnInit {
logOut() {
this.auth.logOut();
+ this.signalRService.stopConnection();
}
}
diff --git a/frontend/src/app/_elements/notifications/notifications.component.html b/frontend/src/app/_elements/notifications/notifications.component.html
index ef897cfc..3b2f4eaa 100644
--- a/frontend/src/app/_elements/notifications/notifications.component.html
+++ b/frontend/src/app/_elements/notifications/notifications.component.html
@@ -11,14 +11,18 @@
</button>
</h2>
- <div id="collapseNotifs" class="collapse show">
+ <div id="collapseNotifs" class="collapse show overflow-auto" style="max-height: 32rem;">
<div *ngFor="let notification of notifications" class="p-2 m-1 border rounded">
- <div class="d-flex flex-row">
- <p>{{notification.title}}</p>
- </div>
- <div *ngIf="notification.hasProgress" class="border-3 border-primary bg-dark m-1" style="height: 5px; margin-top: -10px !important;">
- <div class="bg-primary" style="height: 5px;" [style]="'width: '+(notification.progress*100)+'%;'">
+ <div class="d-flex flex-row ">
+ <div>
+ <p>{{notification.title}}</p>
+ <div *ngIf="notification.hasProgress" class="border-3 border-primary bg-dark m-1" style="height: 5px; margin-top: -10px !important; min-width: 12rem;">
+ <div class="bg-primary" style="height: 5px;" [style]="'width: '+(notification.progress*100)+'%;'">
+ </div>
+ </div>
</div>
+ <button type="button" class="btn-close ms-auto align-self-center" aria-label="Close" (click)="removeNotification(notification);"></button>
</div>
+
</div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/notifications/notifications.component.ts b/frontend/src/app/_elements/notifications/notifications.component.ts
index e199f70a..f324662a 100644
--- a/frontend/src/app/_elements/notifications/notifications.component.ts
+++ b/frontend/src/app/_elements/notifications/notifications.component.ts
@@ -21,13 +21,33 @@ export class NotificationsComponent implements OnInit {
this.notifications.push(new Notification(`Obrađen izvor podataka: ${dName}`, dId, 1.0, false));
});
- this.signalRService.hubConnection.on("NotifyEpoch", (epoch: string, mName: string, mId: string, numEpochs) => {
- //todo epoch
- this.notifications.push(new Notification(`Treniranje modela: ${mName}`, mId, 0.5));
+ this.signalRService.hubConnection.on("NotifyEpoch", (mName: string, mId: string, stat: string, totalEpochs: number, currentEpoch: number) => {
+ const existingNotification = this.notifications.find(x => x.id === mId)
+ const progress = ((currentEpoch + 1) / totalEpochs);
+ //console.log("Ukupno epoha", totalEpochs, "Trenutna epoha:", currentEpoch);
+ if (!existingNotification)
+ this.notifications.push(new Notification(`Treniranje modela: ${mName}`, mId, progress, true));
+ else {
+ existingNotification.progress = progress;
+ }
+ });
+
+ this.signalRService.hubConnection.on("NotifyModel", (mName: string, mId: string, stat: string, totalEpochs: number, currentEpoch: number) => {
+ const existingNotification = this.notifications.find(x => x.id === mId)
+ const progress = ((currentEpoch + 1) / totalEpochs);
+ if (!existingNotification)
+ this.notifications.push(new Notification(`Treniranje modela: ${mName}`, mId, progress, true));
+ else {
+ existingNotification.progress = progress;
+ }
});
} else {
console.warn("Notifications: No connection!");
}
}
+ removeNotification(notification: Notification) {
+ this.notifications.splice(this.notifications.indexOf(notification), 1);
+ }
+
}
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
index 06e74093..572e8c4c 100644
--- 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
@@ -1,8 +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}}
+ {{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>
+ <button mat-button cdkFocusInitial (click)="onYesClick()" class="btn-lg" style="background-color: #003459; color:white;">Da</button>
+ <button mat-button cdkFocusInitial (click)="onNoClick()" class="btn-lg" style="background-color: lightgray;">Ne</button>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts
index c83bf208..66b3755e 100644
--- a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts
+++ b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts
@@ -33,7 +33,7 @@ export class FilterDatasetsComponent implements OnInit {
newDataset._id = "";
newDataset.isPublic = false;
newDataset.lastUpdated = new Date();
- newDataset.username = decodedToken.name;
+ newDataset.uploaderId = decodedToken.uploaderId;
let name=prompt("Unesite naziv dataset-a",newDataset.name);
newDataset.name=name as string;
if(name!=null && name!="")
diff --git a/frontend/src/app/_pages/home/home.component.html b/frontend/src/app/_pages/home/home.component.html
index 08f95a69..f5e94d27 100644
--- a/frontend/src/app/_pages/home/home.component.html
+++ b/frontend/src/app/_pages/home/home.component.html
@@ -53,4 +53,5 @@
<app-carousel [items]="publicPredictors" [type]="'Predictor'">
</app-carousel>
<h3><a routerLink="browse-predictors">Pogledaj sve javne trenirane modele...</a></h3>
+
</div> \ 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 b0e9c4ef..9b281239 100644
--- a/frontend/src/app/_pages/my-models/my-models.component.html
+++ b/frontend/src/app/_pages/my-models/my-models.component.html
@@ -15,7 +15,7 @@
<div class="panel-footer row"><!-- panel-footer -->
<div class="col-xs-6 text-center">
<div>
- <button type="button" class="btn btn-default btn-lg"style="min-width: 7rem;float: left;" (click)="deleteThisModel(model)" mat-raised-button color="primary">Koristi
+ <button type="button" class="btn btn-default btn-lg"style="min-width: 7rem;float: left;" (click)="useThisModel(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>
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 92d3fbaa..d379fa69 100644
--- a/frontend/src/app/_pages/my-models/my-models.component.ts
+++ b/frontend/src/app/_pages/my-models/my-models.component.ts
@@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
import shared from 'src/app/Shared';
import Model from 'src/app/_data/Model';
import { ModelsService } from 'src/app/_services/models.service';
@@ -12,7 +13,7 @@ export class MyModelsComponent implements OnInit {
myModels: Model[] = [];
//myModel: Model;
- constructor(private modelsS : ModelsService) {
+ constructor(private modelsS : ModelsService, private router : Router) {
@@ -44,6 +45,11 @@ deleteThisModel(model: Model): void{
}
+useThisModel(model: Model): void{
+
+ this.router.navigate(['/training'])
+
+}
getAllMyModels(): void{
this.modelsS.getMyModels().subscribe(m => {
this.myModels = 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 d38f93e4..31fa786c 100644
--- a/frontend/src/app/_pages/my-predictors/my-predictors.component.html
+++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.html
@@ -1,16 +1,23 @@
<div id="header">
<h1>Trenirani modeli</h1>
</div>
-<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>
- </div>
- <div>
- <button (click)="delete(predictor)" mat-raised-button color="warn" style="min-width: 15rem;float: right" ><mat-icon>delete</mat-icon></button>
- </div>
-
+<div id="wrapper">
+<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;">
+ </div>
+ <div class="row">
+ <div class="col-sm-4" style="margin-bottom: 10px;" *ngFor="let predictor of predictors">
+ <app-item-predictor [predictor]="predictor"></app-item-predictor>
+ <div>
+ <button (click)="deleteThisPredictor(predictor)" mat-raised-button color="warn" style="min-width: 10rem;float: right" ><mat-icon>delete</mat-icon></button>
+ </div>
+ </div>
+ </div>
+</div>
</div>
-
</div>
+
+
diff --git a/frontend/src/app/_pages/my-predictors/my-predictors.component.ts b/frontend/src/app/_pages/my-predictors/my-predictors.component.ts
index 17c496fd..4dc5300d 100644
--- a/frontend/src/app/_pages/my-predictors/my-predictors.component.ts
+++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.ts
@@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import Predictor from 'src/app/_data/Predictor';
import { PredictorsService } from 'src/app/_services/predictors.service';
-
+import shared from 'src/app/Shared';
@Component({
selector: 'app-my-predictors',
templateUrl: './my-predictors.component.html',
@@ -12,28 +12,30 @@ export class MyPredictorsComponent implements OnInit {
constructor(private predictorsS : PredictorsService) {
}
ngOnInit(): void {
- this.getAllMyPredictors();
-
+ this.predictorsS.getMyPredictors().subscribe((response) => {
+ this.predictors = response;
+ }, (error) => {
+ if (error.error == "Predictor with...") {
+ shared.openDialog("Greska", "Greska");
+ }
+ });
}
- delete(predictor: Predictor){
- if(window.confirm("IZABRANI MODEL ĆE BITI IZBRISAN"))
- {
- this.predictorsS.deletePredictor(predictor).subscribe((response) => {
- this.getAllMyPredictors();
- }, (error) =>{
- if (error.error == "Predictor with name = {name} deleted") {
- alert("Greška pri brisanju modela!");
- }
- });
- }
-
-
+ deleteThisPredictor(predictor: Predictor): void{
+ shared.openYesNoDialog('Brisanje prediktora','Da li ste sigurni da želite da obrišete prediktor?',() => {
+ this.predictorsS.deletePredictor(predictor).subscribe((response) => {
+ this.getAllMyPredictors();
+ }, (error) =>{
+ if (error.error == "Predictor with name = {name} deleted") {
+ shared.openDialog("Obaveštenje", "Greška prilikom brisanja prediktora.");
+ }
+ });
+ });
}
getAllMyPredictors(): void{
- this.predictorsS.getMyPredictors().subscribe(m => {
- this.predictors = m;
+ this.predictorsS.getMyPredictors().subscribe(p => {
+ this.predictors = p;
});
}
diff --git a/frontend/src/app/_services/auth.service.ts b/frontend/src/app/_services/auth.service.ts
index 92cebe7f..9e3f9f2f 100644
--- a/frontend/src/app/_services/auth.service.ts
+++ b/frontend/src/app/_services/auth.service.ts
@@ -1,4 +1,4 @@
-import { Injectable } from '@angular/core';
+import { EventEmitter, Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { JwtHelperService } from '@auth0/angular-jwt';
import { CookieService } from 'ngx-cookie-service';
@@ -12,6 +12,8 @@ const jwtHelper = new JwtHelperService();
})
export class AuthService {
+ public loggedInEvent: EventEmitter<boolean> = new EventEmitter();
+
shared = shared;
constructor(private http: HttpClient, private cookie: CookieService) { }
@@ -44,6 +46,7 @@ export class AuthService {
enableAutoRefresh() {
this.lastToken = this.cookie.get('token');
+ clearTimeout(this.refresher);
let exp = jwtHelper.getTokenExpirationDate(this.lastToken);
if (!exp) {
exp = new Date();
@@ -51,6 +54,7 @@ export class AuthService {
var property = jwtHelper.decodeToken(this.cookie.get('token'));
var username = property['name'];
if (username != "") {
+
this.refresher = setTimeout(() => {
this.http.post(`${Configuration.settings.apiURL}/auth/renewJwt`, {}, { headers: this.authHeader(), responseType: 'text' }).subscribe((response) => {
this.authenticate(response);
@@ -79,6 +83,7 @@ export class AuthService {
}
this.cookie.set('token', token, exp);
this.updateUser();
+ this.loggedInEvent.emit(true);
}
updateUser() {
diff --git a/frontend/src/app/_services/datasets.service.ts b/frontend/src/app/_services/datasets.service.ts
index c3281be6..b2272f0a 100644
--- a/frontend/src/app/_services/datasets.service.ts
+++ b/frontend/src/app/_services/datasets.service.ts
@@ -29,10 +29,10 @@ export class DatasetsService {
}
editDataset(dataset: Dataset): Observable<Dataset> {
- return this.http.put<Dataset>(`${Configuration.settings.apiURL}/dataset/`, dataset, { headers: this.authService.authHeader() });
+ return this.http.put<Dataset>(`${Configuration.settings.apiURL}/dataset/` + dataset._id, dataset, { headers: this.authService.authHeader() });
}
deleteDataset(dataset: Dataset) {
- return this.http.delete(`${Configuration.settings.apiURL}/dataset/` + dataset.name, { headers: this.authService.authHeader(), responseType: "text" });
+ return this.http.delete(`${Configuration.settings.apiURL}/dataset/` + dataset._id, { headers: this.authService.authHeader(), responseType: "text" });
}
}
diff --git a/frontend/src/app/_services/models.service.ts b/frontend/src/app/_services/models.service.ts
index 44383828..1130b12c 100644
--- a/frontend/src/app/_services/models.service.ts
+++ b/frontend/src/app/_services/models.service.ts
@@ -1,6 +1,6 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
-import Model from '../_data/Model';
+import Model, { ProblemType } from '../_data/Model';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs';
import Dataset from '../_data/Dataset';
@@ -31,20 +31,16 @@ export class ModelsService {
addModel(model: Model): Observable<any> {
return this.http.post(`${Configuration.settings.apiURL}/model/add`, model, { headers: this.authService.authHeader() });
}
- addDataset(dataset: Dataset): Observable<any> {
- return this.http.post(`${Configuration.settings.apiURL}/dataset/add`, dataset, { headers: this.authService.authHeader() });
- }
trainModel(modelId: string, experimentId: string): Observable<any> {
return this.http.post(`${Configuration.settings.apiURL}/model/trainmodel`, { ModelId: modelId, ExperimentId: experimentId }, { headers: this.authService.authHeader(), responseType: 'text' });
}
- getMyDatasets(): Observable<Dataset[]> {
- return this.http.get<Dataset[]>(`${Configuration.settings.apiURL}/dataset/mydatasets`, { headers: this.authService.authHeader() });
- }
-
getMyModels(): Observable<Model[]> {
return this.http.get<Model[]>(`${Configuration.settings.apiURL}/model/mymodels`, { headers: this.authService.authHeader() });
}
+ getMyModelsByType(problemType: ProblemType): Observable<Model[]> {
+ return this.http.get<Model[]>(`${Configuration.settings.apiURL}/model/mymodelsbytype/` + problemType, { headers: this.authService.authHeader() });
+ }
editModel(model: Model): Observable<Model> {
return this.http.put<Model>(`${Configuration.settings.apiURL}/model/`, model, { headers: this.authService.authHeader() });
@@ -53,4 +49,5 @@ export class ModelsService {
deleteModel(model: Model) {
return this.http.delete(`${Configuration.settings.apiURL}/model/` + model.name, { headers: this.authService.authHeader(), responseType: "text" });
}
+
}
diff --git a/frontend/src/app/_services/predictors.service.ts b/frontend/src/app/_services/predictors.service.ts
index 9e8383aa..a24ee708 100644
--- a/frontend/src/app/_services/predictors.service.ts
+++ b/frontend/src/app/_services/predictors.service.ts
@@ -24,7 +24,7 @@ export class PredictorsService {
}
deletePredictor(predictor: Predictor) {
- return this.http.delete(`${Configuration.settings.apiURL}/predictor/` + predictor.name, { headers: this.authService.authHeader(), responseType: "text" });
+ return this.http.delete(`${Configuration.settings.apiURL}/predictor/` + predictor._id, { headers: this.authService.authHeader(), responseType: "text" });
}
getMyPredictors(): Observable<Predictor[]> {
diff --git a/frontend/src/app/_services/signal-r.service.ts b/frontend/src/app/_services/signal-r.service.ts
index b279b5ca..6a2e61e9 100644
--- a/frontend/src/app/_services/signal-r.service.ts
+++ b/frontend/src/app/_services/signal-r.service.ts
@@ -16,13 +16,18 @@ export class SignalRService {
}).build();
this.hubConnection.on("Notify", (message: string) => {
- console.log(" " + message);
+ //console.log(" " + message);
});
this.hubConnection
.start()
- .then(() => console.log("con Started"))
- .catch(err => console.log("Error" + err))
+ .then(() => {})
+ .catch(err => {})
}
+
+ public stopConnection = () => {
+ this.hubConnection?.stop();
+ }
+
constructor(private cookie: CookieService) { }
}
diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts
index e22f7a88..a0952d21 100644
--- a/frontend/src/app/app-routing.module.ts
+++ b/frontend/src/app/app-routing.module.ts
@@ -19,6 +19,7 @@ const routes: Routes = [
{ path: '', component: HomeComponent, data: { title: 'Početna strana' } },
/*{ path: 'add-model', component: AddModelComponent, data: { title: 'Dodaj model' } },*/
{ path: 'experiment', component: ExperimentComponent, data: { title: 'Dodaj eksperiment' } },
+ { path: 'training/:id', component: TrainingComponent, data: { title: 'Treniraj model' } },
{ path: 'training', component: TrainingComponent, data: { title: 'Treniraj model' } },
{ path: 'my-datasets', component: MyDatasetsComponent, canActivate: [AuthGuardService], data: { title: 'Moji izvori podataka' } },
{ path: 'my-models', component: MyModelsComponent, canActivate: [AuthGuardService], data: { title: 'Moji modeli' } },
@@ -28,6 +29,7 @@ const routes: Routes = [
{ path: 'browse-datasets', component: FilterDatasetsComponent, data: { title: 'Javni izvori podataka' } },
{ path: 'browse-predictors', component: BrowsePredictorsComponent, data: { title: 'Javni trenirani modeli' } },
{ path: 'predict/:id', component: PredictComponent, data: { title: 'Predvidi vrednosti' } },
+
];
@NgModule({
diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts
index 59f247ed..54c18bec 100644
--- a/frontend/src/app/app.component.ts
+++ b/frontend/src/app/app.component.ts
@@ -50,7 +50,6 @@ export class AppComponent implements OnInit {
private startHttpRequest = () => {
this.http.get('http://localhost:5283/chatHub')
.subscribe(res => {
- console.log(res);
})
}
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index c4f89ad8..51374bd4 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -48,7 +48,11 @@ 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';
import { Configuration } from './configuration.service';
+import { PointLinechartComponent } from './point-linechart/point-linechart.component';
+import { MixedChartComponent } from './mixed-chart/mixed-chart.component';
+import { LineChartComponent } from './_elements/line-chart/line-chart.component';
+import { MetricViewComponent } from './_elements/metric-view/metric-view.component';
export function initializeApp(appConfig: Configuration) {
return () => appConfig.load();
}
@@ -87,7 +91,14 @@ export function initializeApp(appConfig: Configuration) {
GraphComponent,
TrainingComponent,
ItemExperimentComponent,
- YesNoDialogComponent
+ YesNoDialogComponent,
+ LineChartComponent,
+ PointLinechartComponent,
+ MixedChartComponent,
+ LineChartComponent,
+ MetricViewComponent,
+
+
],
imports: [
BrowserModule,
diff --git a/frontend/src/app/experiment/experiment.component.css b/frontend/src/app/experiment/experiment.component.css
index 4a3d7741..d84a897e 100644
--- a/frontend/src/app/experiment/experiment.component.css
+++ b/frontend/src/app/experiment/experiment.component.css
@@ -40,4 +40,9 @@ ul li:hover {
h2 {
color: #003459;
+}
+
+.boldClass {
+ font-weight: bold;
+ font-size: 125%;
} \ No newline at end of file
diff --git a/frontend/src/app/experiment/experiment.component.html b/frontend/src/app/experiment/experiment.component.html
index e46f5bd9..4bdd79b2 100644
--- a/frontend/src/app/experiment/experiment.component.html
+++ b/frontend/src/app/experiment/experiment.component.html
@@ -4,159 +4,147 @@
<div id="wrapper">
<div id="container" class="container p-5" style="background-color: white; min-height: 100%;">
- <div class="d-flex flex-row justify-content-center align-items-center my-3">
- <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="0">Izvor podataka</a>
+ <div class="d-flex flex-row justify-content-center align-items-center my-3 links">
+ <a id="firstStep" href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="0" (click)="updateCarouselIndex(0);" [ngClass]="{'boldClass' : carouselIndex == 0}" style="text-decoration: none">Izvor podataka</a>
<mat-icon>arrow_forward</mat-icon>
- <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="1">Preprocesiranje</a>
+ <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="1" (click)="updateCarouselIndex(1)" [ngClass]="{'boldClass' : carouselIndex == 1}" style="text-decoration: none">Preprocesiranje</a>
<mat-icon>arrow_forward</mat-icon>
- <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="2">Dodaj eksperiment</a>
+ <a href="#" data-bs-target="#carouselExampleControls" data-bs-slide-to="2" (click)="updateCarouselIndex(2)" [ngClass]="{'boldClass' : carouselIndex == 2}" style="text-decoration: none">Dodaj eksperiment</a>
</div>
- <div id="carouselExampleControls" class="carousel slide px-5 mt-5" data-bs-wrap="false" data-bs-ride="carousel" data-bs-interval="false">
- <div class="carousel-inner">
- <div class="carousel-item active mt-2">
- <h2 class="mb-5">1. Izvor podataka</h2>
- <app-dataset-load (selectedDatasetChangeEvent)="updateDataset($event)"></app-dataset-load>
- </div>
+ <div id="carouselExampleControls" class="carousel px-5 mt-5" data-bs-wrap="false" data-bs-ride="carousel" data-bs-interval="false">
+ <div class="carousel-inner">
+ <div class="carousel-item active mt-2">
+ <h2 class="mb-5">1. Izvor podataka</h2>
+ <app-dataset-load (selectedDatasetChangeEvent)="updateDataset($event)"></app-dataset-load>
+ </div>
- <div class="carousel-item mt-2">
- <h2 class="mb-4">2. Preprocesiranje</h2>
+ <div class="carousel-item mt-2">
+ <h2 class="mb-4">2. Preprocesiranje</h2>
- <div class="px-5">
- <h3>Biranje ulaznih i izlaznih kolona:</h3>
- <div *ngIf="selectedDataset">
- <div class="row">
- <div class="col d-flex justify-content-center">
- <h3>Izaberite ulazne kolone:</h3>
- <div id="divInputs" class="form-check mt-2">
- <br>
- <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]="experiment.outputColumn != item.columnName"
- [disabled]="experiment.outputColumn == item.columnName"
- (click)="checkedColumnsChanged(item, 0)">&nbsp;
- <label class="form-check-label" for="cb_{{item.columnName}}">
+ <div class="px-5">
+ <h3>Biranje ulaznih i izlaznih kolona:</h3>
+ <div *ngIf="selectedDataset">
+ <div class="row">
+ <div class="col d-flex justify-content-center">
+ <h3>Izaberite ulazne kolone:</h3>
+ <div id="divInputs" class="form-check mt-2">
+ <br>
+ <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]="experiment.outputColumn != item.columnName" [disabled]="experiment.outputColumn == item.columnName" (change)="checkedColumnsChanged(item, 0); resetColumnEncodings();">&nbsp;
+ <label class="form-check-label" for="cb_{{item.columnName}}">
{{item.columnName}}
</label>
+ </div>
</div>
</div>
- </div>
- <div class="col d-flex justify-content-left">
- <h3>Izaberite izlaznu kolonu:</h3>
- <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"
- [(ngModel)]="this.experiment.outputColumn"
- (change)="experiment.outputColumn = item.columnName"
- (click)="checkedColumnsChanged(item, 1);">&nbsp;
- <label class="form-check-label" for="rb_{{item.columnName}}">
+ <div class="col d-flex justify-content-left">
+ <h3>Izaberite izlaznu kolonu:</h3>
+ <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" [(ngModel)]="this.experiment.outputColumn" (change)="experiment.outputColumn = item.columnName; checkedColumnsChanged(item, 1); resetColumnEncodings();"
+ checked>&nbsp;
+ <label class="form-check-label" for="rb_{{item.columnName}}">
{{item.columnName}}
</label>
+ </div>
</div>
</div>
</div>
</div>
- </div>
-
- <h3 class="mt-5">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">
- <label for="delRows" class="form-check-label">Obriši sve
- redove sa nedostajućim vrednostima ({{selectedDataset.nullRows}} od {{selectedDataset.rowCount}})</label><br>
- <input type="radio" [(ngModel)]="experiment.nullValues" [value]="NullValueOptions.DeleteColumns"
- class="form-check-input" value="deleteCols" name="fillMissing" id="delCols" data-bs-toggle="collapse"
- data-bs-target="#fillMissingCustom.show">
- <label for="delCols" class="form-check-label">Obriši sve
+
+
+ <div class="mt-5 mb-3 mx-3" *ngIf="countSelectedNullCols() == 0">
+ <h3 class="border p-2 text-center"><i>Izabrane kolone nemaju nedostajuće vrednosti koje možete popuniti.</i></h3>
+ </div>
+
+ <div *ngIf="countSelectedNullCols() != 0">
+ <h3 class="mt-5">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">
+ <label *ngIf="selectedColumnsInfoArray.length == selectedDataset.columnInfo.length" for="delRows" class="form-check-label ">Obriši sve
+ redove sa nedostajućim vrednostima ({{selectedDataset.nullRows}} od {{selectedDataset.rowCount}})</label>
+ <label *ngIf="selectedColumnsInfoArray.length != selectedDataset.columnInfo.length" for="delRows" class="form-check-label ">Obriši sve
+ redove sa nedostajućim vrednostima</label><br>
+ <input type="radio" [(ngModel)]="experiment.nullValues " [value]="NullValueOptions.DeleteColumns " class="form-check-input" value="deleteCols" name="fillMissing " id="delCols" data-bs-toggle="collapse" data-bs-target="#fillMissingCustom.show">
+ <label for="delCols" class="form-check-label ">Obriši sve
kolone sa nedostajućim vrednostima ({{countSelectedNullCols()}} od {{selectedDataset.columnInfo.length}})</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)">
- <label for="replace" class="form-check-label">Izabraću
+ <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="getSelectedColumnsArrayWithoutNullVals().length > 0" >
- <label class="text-center form-control mx-3 text-secondary">
+ <div class="collapse" id="fillMissingCustom">
+ <div>
+ <label for="columnReplacers" class="form-label">Unesite zamenu za svaku kolonu:</label>
+ <div class="my-3 " *ngIf="getSelectedColumnsArrayWithoutNullVals().length> 0">
+ <label class="text-center form-control mx-3 text-secondary">
Kolone <span style="font-style: italic;" *ngFor="let colname of getSelectedColumnsArrayWithoutNullVals(); let i = index">
<span *ngIf="i != getSelectedColumnsArrayWithoutNullVals().length - 1">{{colname}}, </span>
<span *ngIf="i == getSelectedColumnsArrayWithoutNullVals().length - 1">{{colname}} </span>
</span>
nemaju nedostajućih vrednosti za popunjavanje.
</label>
- </div>
- <div id="columnReplacers">
- <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}}&nbsp;<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">
- 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">
- <div class="input-group-prepend">
- <label [for]="'fillCol_'+column.columnName" class="form-control">
+ </div>
+ <div id="columnReplacers">
+ <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}}&nbsp;
+ <span class="small" style="color:gray;">({{column.numNulls}} null)</span>
+ </span>
+
+ <div class="d-flex flex-row justify-content-end">
+ <div class="flex-grow-3 mx-3 me-auto">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <label [for]="'fillCol_'+column.columnName" class="form-control">
Zameni
<input type="radio" [id]="'fillCol_'+column.columnName"
[name]="'delOp_'+column.columnName">
</label>
- </div>
- <input type="text" class="form-control" [id]="'fillText_'+column.columnName"
- (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); checkFillColRadio(column.columnName);">
+ </div>
+ <input type="text" class="form-control" [id]="'fillText_'+column.columnName" (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); 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"
- *ngIf="!column.isNumber && column.numNulls > 0"
- (change)="replace($event, column); checkFillColRadio(column.columnName);">
+ <select [id]="'replaceOptions'+i" class="form-control btn-outline-primary" *ngIf="!column.isNumber && column.numNulls > 0" (change)="replace($event, column); checkFillColRadio(column.columnName);">
<option *ngFor="let option of column.uniqueValues" [value]="option">
{{ option }}
</option>
</select>
+ </div>
+ </div>
</div>
- </div>
- </div>
-
- <div class="flex-shrink-1 mx-3">
- <div class="input-group">
- <label class="form-control" [for]="'delCol_'+column.columnName">Izbriši
- kolonu
- <input type="radio" [id]="'delCol_'+column.columnName"
+
+ <div class="flex-shrink-1 mx-3">
+ <div class="input-group" *ngIf="column.columnName != this.experiment.outputColumn">
+ <label class="form-control" [for]="'delCol_'+column.columnName">Izbriši
+ kolonu
+ <input type="radio" [id]="'delCol_'+column.columnName"
[name]="'delOp_'+column.columnName"
- (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
+ (change)="emptyFillTextInput(column.columnName)">
+ </label>
+ </div>
+ <label class="form-control text-secondary" role="alert" *ngIf="column.columnName == this.experiment.outputColumn">
+ (Izlazna kolona)
+ </label>
+ </div>
+
+ <div class="flex-shrink-1 mx-3">
+ <div class="input-group">
+ <label class="form-control" [for]="'delRows_'+column.columnName">Izbriši
redove
<input type="radio" [id]="'delRows_'+column.columnName"
[name]="'delOp_'+column.columnName" checked
(change)="emptyFillTextInput(column.columnName)"></label>
+ </div>
+ </div>
</div>
</div>
</div>
@@ -165,96 +153,109 @@
</div>
</div>
</div>
- </div>
-
- <div id="randomOptions" class="mt-5">
- <div class="p-2 m-2">
- <label for="type" class="form-check-label">Želite li da redosled podataka bude nasumičan?</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">
+
+ <div id="randomOptions" class="mt-5">
+ <div class="p-2 m-2">
+ <label for="type" class="form-check-label">Želite li da redosled podataka bude nasumičan?</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:&nbsp;&nbsp;
<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="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 m-2">
- <label for="percentage" class="form-label">Procenat podataka koji se uzima za trening
+
+ <div class="row p-2 m-2">
+ <label for="percentage" class="form-label">Procenat podataka koji se uzima za trening
skup:</label>
- <input id="percentage" type="number" class="form-control mx-3" style=" max-width: 15%" min="10" max="90" step="10" value="90"
- [(ngModel)]="tempTestSetDistribution" [disabled]="!experiment.randomTestSet">
+ <input id="percentage" type="number" class="form-control mx-3" style=" max-width: 15%" min="10" max="90" step="10" value="90" [(ngModel)]="tempTestSetDistribution" [disabled]="!experiment.randomTestSet">
+ </div>
</div>
</div>
- </div>
-
- <div id="encodingForColumns" class="row px-3 my-5">
- <div class="col-1">
- <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 id="encodingsForColumns" class="px-3 my-5">
+ <label for="encoding" class="col-form-label mb-2">Enkoding za izabrane kolone:</label>
+
+ <div class="row d-flex flex-row justify-content-between align-items-center">
+ <div class="col-5" *ngFor="let item of [].constructor(selectedColumnsInfoArray.length); let i = index">
+ <div class="input-group mb-2">
+ <div class="input-group-prepend">
+ <span class="input-group-text">{{selectedColumnsInfoArray[i].columnName}}</span>
+ </div>
+ <select [id]="'encodingOption_'+i" class="form-select" [(ngModel)]="experiment.encodings[i].encoding">
+ <option
+ *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)"
+ [value]="option">
+ {{ optionName }}
+ </option>
+ </select>
+ </div>
+ </div>
+ </div>
</div>
+
</div>
-
- </div>
- </div>
+ </div>
- <div class="carousel-item mt-2">
- <h2 class="mb-4">3. Dodaj eskperiment</h2>
+ <div class="carousel-item mt-2">
+ <h2 class="mb-4">3. Dodaj eksperiment</h2>
- <div class="row">
- <div class="col"></div>
- <div class="col-8">
- <label for="name" class="col-form-label">Naziv eksperimenta:</label>
- <input type="text" class="form-control mb-3" 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>
- <div class="form-group row mt-5 mb-3">
- <div class="col"></div>
- <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="saveExperiment();">Sačuvaj
+ <div class="row">
+ <div class="col"></div>
+ <div class="col-8">
+ <label for="name" class="col-form-label">Naziv eksperimenta:</label>
+ <input type="text" class="form-control mb-3" 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>
+ <label for="desc" class="col-sm-2 col-form-label mt-3">Tip problema:</label>
+ <div class="col-4">
+ <select id="typeOptions" class="form-select" name="type" [(ngModel)]="experiment.type">
+ <option
+ *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)"
+ [value]="option">
+ {{ optionName }}
+ </option>
+ </select>
+ </div>
+ <div class="form-group row mt-5 mb-3">
+ <div class="col"></div>
+ <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="saveExperiment();">Sačuvaj
eksperiment</button>
- <div class="col"></div>
+ <div class="col"></div>
+ </div>
</div>
+ <div class="col"></div>
</div>
- <div class="col"></div>
</div>
+
</div>
- </div>
- <div class="m-3 d-flex flex-row justify-content-between align-items-center" style=" margin-left: auto;">
- <button mat-fab color="primary" data-bs-target="#carouselExampleControls" data-bs-slide="prev">
- <mat-icon>arrow_backward</mat-icon>
- </button>
- <button mat-fab color="primary" data-bs-target="#carouselExampleControls" data-bs-slide="next">
- <mat-icon>arrow_forward</mat-icon>
- </button>
- </div>
+ <div class="m-3 d-flex flex-row align-items-center" style=" margin-left: auto;">
+ <button class="me-auto" *ngIf="carouselIndex != 0" mat-fab color="primary" data-bs-target="#carouselExampleControls" data-bs-slide="prev" (click)="updateCarouselIndex(carouselIndex - 1)">
+ <mat-icon>arrow_backward</mat-icon>
+ </button>
+ <button class="ms-auto" *ngIf="carouselIndex != 2" mat-fab color="primary" data-bs-target="#carouselExampleControls" data-bs-slide="next" (click)="updateCarouselIndex(carouselIndex + 1)">
+ <mat-icon>arrow_forward</mat-icon>
+ </button>
+ </div>
</div>
</div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/experiment/experiment.component.ts b/frontend/src/app/experiment/experiment.component.ts
index b5e1d1f4..64adce3f 100644
--- a/frontend/src/app/experiment/experiment.component.ts
+++ b/frontend/src/app/experiment/experiment.component.ts
@@ -1,10 +1,14 @@
import { Component, OnInit } from '@angular/core';
import Experiment, { NullValReplacer, NullValueOptions, ReplaceWith, Encoding } from '../_data/Experiment';
-import Model from '../_data/Model';
+import Model,{ProblemType} from '../_data/Model';
import Dataset, { ColumnInfo } from '../_data/Dataset';
import { ModelsService } from '../_services/models.service';
import Shared from '../Shared';
import { ExperimentsService } from '../_services/experiments.service';
+import { ColumnEncoding } from '../_data/Experiment';
+import { Router } from '@angular/router';
+import { TrainingComponent } from '../training/training.component';
+import { NEVER, retryWhen } from 'rxjs';
@Component({
selector: 'app-experiment',
@@ -21,14 +25,16 @@ export class ExperimentComponent implements OnInit {
NullValueOptions = NullValueOptions;
ReplaceWith = ReplaceWith;
Encoding = Encoding;
+ ColumnEncoding = ColumnEncoding;
Object = Object;
-
+ ProblemType=ProblemType;
selectedColumnsInfoArray: ColumnInfo[] = [];
selectedNotNullColumnsArray: string[] = [];
tempTestSetDistribution = 90;
+ carouselIndex: number = 0;
- constructor(private modelsService: ModelsService, private experimentsService: ExperimentsService) {
+ constructor(private experimentsService: ExperimentsService, private router: Router) {
}
ngOnInit(): void {
@@ -38,6 +44,16 @@ export class ExperimentComponent implements OnInit {
this.selectedDataset = dataset;
this.selectedColumnsInfoArray = this.selectedDataset.columnInfo;
this.selectedNotNullColumnsArray = [];
+ this.experiment.outputColumn = this.selectedDataset.columnInfo[this.selectedDataset.columnInfo.length - 1].columnName;
+
+ this.resetColumnEncodings();
+ }
+
+ resetColumnEncodings() {
+ this.experiment.encodings = [];
+ for (let i = 0; i < this.selectedColumnsInfoArray.length; i++) {
+ this.experiment.encodings.push(new ColumnEncoding(this.selectedColumnsInfoArray[i].columnName, Encoding.Label));
+ }
}
getInputById(id: string): HTMLInputElement {
@@ -153,7 +169,7 @@ export class ExperimentComponent implements OnInit {
}
saveExperiment() {
- if (this.selectedDataset == undefined) {
+ if (this.selectedDataset == undefined) {
Shared.openDialog("Greška", "Izvor podataka nije izabran!");
return;
}
@@ -184,10 +200,9 @@ export class ExperimentComponent implements OnInit {
this.experimentsService.addExperiment(this.experiment).subscribe((response) => {
this.experiment = response;
- this.selectedColumnsInfoArray = [];
- this.selectedNotNullColumnsArray = [];
-
- Shared.openDialog("Obaveštenje", "Eksperiment je uspešno kreiran.");
+ Shared.openYesNoDialog("Obaveštenje", "Eksperiment je uspešno kreiran. Da li želite da pređete na treniranje modela?", () => {
+ this.router.navigate(['/training', this.experiment._id]);
+ });
}, (error) => {
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.");
@@ -205,4 +220,23 @@ export class ExperimentComponent implements OnInit {
}
return counter;
}
+
+ updateCarouselIndex(newIndex: number) {
+ if (newIndex > 2)
+ newIndex = 2;
+ else if (newIndex < 0)
+ newIndex = 0;
+
+ if (this.carouselIndex == 0 && (newIndex == 1 || newIndex == 2))
+ this.checkRequiredData();
+ this.carouselIndex = newIndex;
+ }
+
+ checkRequiredData() {
+ if (this.selectedDataset == undefined) {
+ (<HTMLAnchorElement>document.getElementById("firstStep")).click();
+ Shared.openDialog("Pažnja", "Potrebno je da dodate ili izabere izvor podataka kako biste prešli na naredni korak (preprocesiranje).");
+ return;
+ }
+ }
}
diff --git a/frontend/src/app/mixed-chart/mixed-chart.component.css b/frontend/src/app/mixed-chart/mixed-chart.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/mixed-chart/mixed-chart.component.css
diff --git a/frontend/src/app/mixed-chart/mixed-chart.component.html b/frontend/src/app/mixed-chart/mixed-chart.component.html
new file mode 100644
index 00000000..806ea9e8
--- /dev/null
+++ b/frontend/src/app/mixed-chart/mixed-chart.component.html
@@ -0,0 +1,2 @@
+<canvas #mixedchart width="800" height="450"></canvas>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
diff --git a/frontend/src/app/mixed-chart/mixed-chart.component.spec.ts b/frontend/src/app/mixed-chart/mixed-chart.component.spec.ts
new file mode 100644
index 00000000..361cd047
--- /dev/null
+++ b/frontend/src/app/mixed-chart/mixed-chart.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MixedChartComponent } from './mixed-chart.component';
+
+describe('MixedChartComponent', () => {
+ let component: MixedChartComponent;
+ let fixture: ComponentFixture<MixedChartComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ MixedChartComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MixedChartComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/mixed-chart/mixed-chart.component.ts b/frontend/src/app/mixed-chart/mixed-chart.component.ts
new file mode 100644
index 00000000..2524ee36
--- /dev/null
+++ b/frontend/src/app/mixed-chart/mixed-chart.component.ts
@@ -0,0 +1,56 @@
+import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'node_modules/chart.js';
+
+@Component({
+ selector: 'app-mixed-chart',
+ templateUrl: './mixed-chart.component.html',
+ styleUrls: ['./mixed-chart.component.css']
+})
+export class MixedChartComponent implements AfterViewInit {
+
+ @ViewChild('mixedchart') chartRef!: ElementRef;
+ constructor() { }
+
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'bar',
+ data: {
+ labels: ["1900", "1950", "1999", "2050"],
+ datasets: [{
+ label: "Europe",
+ type: "line",
+ borderColor: "#8e5ea2",
+ data: [408,547,675,734],
+ fill: false
+ }, {
+ label: "Africa",
+ type: "line",
+ borderColor: "#3e95cd",
+ data: [133,221,783,2478],
+ fill: false
+ }, {
+ label: "Europe",
+ type: "bar",
+ backgroundColor: "rgba(0,0,0,0.2)",
+ data: [408,547,675,734],
+ }, {
+ label: "Africa",
+ type: "bar",
+ backgroundColor: "rgba(0,0,0,0.2)",
+ //backgroundColorHover: "#3e95cd",
+ data: [133,221,783,2478]
+ }
+ ]
+ },
+ /*options: {
+ title: {
+ display: true,
+ text: 'Population growth (millions): Europe & Africa'
+ },
+ legend: { display: false }
+ }*/
+
+ });
+ }
+
+}
diff --git a/frontend/src/app/point-linechart/point-linechart.component.css b/frontend/src/app/point-linechart/point-linechart.component.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/frontend/src/app/point-linechart/point-linechart.component.css
diff --git a/frontend/src/app/point-linechart/point-linechart.component.html b/frontend/src/app/point-linechart/point-linechart.component.html
new file mode 100644
index 00000000..f9f9a24a
--- /dev/null
+++ b/frontend/src/app/point-linechart/point-linechart.component.html
@@ -0,0 +1,2 @@
+<canvas #linechart width="800" height="450">Point line chart:</canvas>
+<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
diff --git a/frontend/src/app/point-linechart/point-linechart.component.spec.ts b/frontend/src/app/point-linechart/point-linechart.component.spec.ts
new file mode 100644
index 00000000..fe08fe7c
--- /dev/null
+++ b/frontend/src/app/point-linechart/point-linechart.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PointLinechartComponent } from './point-linechart.component';
+
+describe('PointLinechartComponent', () => {
+ let component: PointLinechartComponent;
+ let fixture: ComponentFixture<PointLinechartComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PointLinechartComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PointLinechartComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/point-linechart/point-linechart.component.ts b/frontend/src/app/point-linechart/point-linechart.component.ts
new file mode 100644
index 00000000..3497a20c
--- /dev/null
+++ b/frontend/src/app/point-linechart/point-linechart.component.ts
@@ -0,0 +1,57 @@
+import { AfterViewInit, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
+import {Chart} from 'node_modules/chart.js';
+
+
+@Component({
+ selector: 'app-point-linechart',
+ templateUrl: './point-linechart.component.html',
+ styleUrls: ['./point-linechart.component.css']
+})
+export class PointLinechartComponent implements AfterViewInit {
+
+ @ViewChild('linechart') chartRef!: ElementRef;
+ constructor() { }
+ ngAfterViewInit(): void {
+ const myChart = new Chart(this.chartRef.nativeElement, {
+ type: 'line',
+ data: {
+ labels: [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050],
+ datasets: [{
+ data: [86,114,106,106,107,111,133,221,783,2478],
+ label: "Africa",
+ borderColor: "#3e95cd",
+ fill: false
+ }, {
+ data: [282,350,411,502,635,809,947,1402,3700,5267],
+ label: "Asia",
+ borderColor: "#8e5ea2",
+ fill: false
+ }, {
+ data: [168,170,178,190,203,276,408,547,675,734],
+ label: "Europe",
+ borderColor: "#3cba9f",
+ fill: false
+ }, {
+ data: [40,20,10,16,24,38,74,167,508,784],
+ label: "Latin America",
+ borderColor: "#e8c3b9",
+ fill: false
+ }, {
+ data: [6,3,2,2,7,26,82,172,312,433],
+ label: "North America",
+ borderColor: "#c45850",
+ fill: false
+ }
+ ]
+ },
+ /*options: {
+ title: {
+ display: true,
+ text: 'World population per region (in millions)'
+ }
+ }*/
+
+ });
+
+ }
+} \ No newline at end of file
diff --git a/frontend/src/app/training/training.component.html b/frontend/src/app/training/training.component.html
index 0ce4cc89..deac88cc 100644
--- a/frontend/src/app/training/training.component.html
+++ b/frontend/src/app/training/training.component.html
@@ -2,45 +2,40 @@
<h1>Trenirajte veštačku neuronsku mrežu</h1>
</div>
<div id="wrapper" class="mb-4">
- <div id="container" class="container p-5 row" style="background-color: white; min-height: 100%;">
- <div class="col"></div>
+ <div id="container" class="container p-5" style="background-color: white; min-height: 100%;">
+ <h2>1. Izaberite eksperiment iz kolekcije</h2>
+ <div class="px-5 mt-5 mb-3">
+ <input type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term">
+ </div>
+ <div class="overflow-auto px-5" 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>
- <div class="col-10">
+ <h2 class="mt-5 mb-2">2. Izaberite model</h2>
+ <app-model-load *ngIf="selectedExperiment" (selectedModelChangeEvent)="selectModel($event)" [forExperiment]="selectedExperiment"></app-model-load>
+ <h3 *ngIf="!selectedExperiment">Morate prvo izabrati eksperiment.</h3>
- <h2>1. Izaberite eksperiment iz kolekcije</h2>
- <div class="px-5 mt-5 mb-3">
- <input type="text" class="form-control" placeholder="Pretraga"
- [(ngModel)]="term">
- </div>
- <div class="overflow-auto px-5" 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 class="mt-5 mb-2">2. Izaberite model</h2>
- <app-model-load (selectedModelChangeEvent)="selectModel($event)"></app-model-load>
-
- <h2 class="my-5">3. Treniranje modela</h2>
+ <h2 class="my-5">3. Treniranje modela</h2>
- <div class="d-flex flex-row justify-content-center align-items-center my-3">
- <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="trainModel();">Treniraj
+ <div class="d-flex flex-row justify-content-center align-items-center my-3">
+ <button #trainButton class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="trainModel();">Treniraj
model</button>
- </div>
-
- <h2 class="mt-5">Rezultati treniranja</h2>
- <div class="m-3" *ngIf="trainingResult">
- <h2 class="my-2">Rezultati treniranja:</h2>
- <p>
- {{trainingResult}}
- </p>
- </div>
</div>
- <div class="col"></div>
+ <h2 class="mt-5">Rezultati treniranja</h2>
+ <div class="m-3" *ngIf="trainingResult">
+ <h2 class="my-2">Rezultati treniranja:</h2>
+ <app-metric-view *ngIf="trainingResult" [metrics]="trainingResult"></app-metric-view>
+ </div>
+ </div>
+
+ <h2 class="mt-5">Rezultati treniranja</h2>
+ <div class="m-3" *ngIf="trainingResult">
+ <h2 class="my-2">Rezultati treniranja:</h2>
+ <app-metric-view *ngIf="trainingResult" [metrics]="trainingResult"></app-metric-view>
</div>
</div> \ No newline at end of file
diff --git a/frontend/src/app/training/training.component.ts b/frontend/src/app/training/training.component.ts
index 027d2c22..a0bec8e2 100644
--- a/frontend/src/app/training/training.component.ts
+++ b/frontend/src/app/training/training.component.ts
@@ -1,17 +1,26 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
import Shared from '../Shared';
import Experiment from '../_data/Experiment';
-import Model from '../_data/Model';
-import { DatasetsService } from '../_services/datasets.service';
+import Model, { ProblemType } from '../_data/Model';
+import { MetricViewComponent } from '../_elements/metric-view/metric-view.component';
+import { ModelLoadComponent } from '../_elements/model-load/model-load.component';
+import { AuthService } from '../_services/auth.service';
import { ExperimentsService } from '../_services/experiments.service';
import { ModelsService } from '../_services/models.service';
+import { SignalRService } from '../_services/signal-r.service';
@Component({
selector: 'app-training',
templateUrl: './training.component.html',
styleUrls: ['./training.component.css']
})
-export class TrainingComponent{
+export class TrainingComponent implements OnInit {
+
+ @ViewChild(ModelLoadComponent) modelLoadComponent?: ModelLoadComponent;
+ @ViewChild("trainButton") trainButtonRef!: ElementRef;
+
+ @ViewChild(MetricViewComponent) metricViewComponent!: MetricViewComponent;
myExperiments?: Experiment[];
selectedExperiment?: Experiment;
@@ -19,16 +28,55 @@ export class TrainingComponent{
trainingResult: any;
+ history: any[] = [];
+
term: string = "";
- constructor(private modelsService: ModelsService, private datasetsService: DatasetsService, private experimentsService: ExperimentsService) {
+ constructor(private modelsService: ModelsService, private route: ActivatedRoute, private experimentsService: ExperimentsService, private authService: AuthService, private signalRService: SignalRService) {
+ }
+
+ ngOnInit(): void {
+ this.route.queryParams.subscribe(params => {
+ let experimentId = this.route.snapshot.paramMap.get("id");
+
+ this.fetchExperiments(experimentId);
+
+ this.authService.loggedInEvent.subscribe(_ => {
+ this.fetchExperiments(experimentId);
+
+ this.signalRService.startConnection();
+ });
+
+ console.log(this.signalRService.hubConnection);
+ if (this.signalRService.hubConnection) {
+ this.signalRService.hubConnection.on("NotifyEpoch", (mName: string, mId: string, stat: string, totalEpochs: number, currentEpoch: number) => {
+ console.log(this.selectedModel?._id, mId);
+ if (this.selectedModel?._id == mId) {
+ stat = stat.replace(/'/g, '"');
+ this.trainingResult = JSON.parse(stat);
+ //console.log('JSON', this.trainingResult);
+ this.history.push(this.trainingResult);
+ this.metricViewComponent.update(this.history);
+ }
+ });
+ }
+ });
+ }
+
+ fetchExperiments(andSelectWithId: string | null = '') {
this.experimentsService.getMyExperiments().subscribe((experiments) => {
- this.myExperiments = experiments;
+ this.myExperiments = experiments.reverse();
+
+ this.selectedExperiment = this.myExperiments.filter(x => x._id == andSelectWithId)[0];
+ if (this.modelLoadComponent)
+ this.modelLoadComponent.newModel.type = this.selectedExperiment.type;
});
}
selectThisExperiment(experiment: Experiment) {
this.selectedExperiment = experiment;
+ if (this.modelLoadComponent)
+ this.modelLoadComponent.newModel.type = this.selectedExperiment.type;
}
selectModel(model: Model) {
@@ -46,9 +94,11 @@ export class TrainingComponent{
Shared.openDialog("Greška", "Molimo Vas da izaberete model.");
return;
}
+ this.trainButtonRef.nativeElement.disabled = true;
this.modelsService.trainModel(this.selectedModel._id, this.selectedExperiment._id).subscribe((response: any) => {
//console.log('Train model complete!', response);
- Shared.openDialog("Obaveštenje", "Treniranje modela je uspešno završeno!");
+ this.trainButtonRef.nativeElement.disabled = false;
+ Shared.openDialog("Obaveštenje", "Treniranje modela je počelo!");
this.trainingResult = response;
});
}