diff options
48 files changed, 1073 insertions, 141 deletions
@@ -4,4 +4,6 @@ sandbox/test-projekat-danijel/backend/.vs/ sandbox/testAppSonja/MiniApkSonja/MiniApkSonja/bin/ sandbox/testAppSonja/MiniApkSonja/MiniApkSonja/obj/ sandbox/TestTamara/TestTamara/TestTamara/obj/ -sandbox/TestTamara/TestTamara/TestTamara/bin/
\ No newline at end of file +sandbox/TestTamara/TestTamara/TestTamara/bin/ +backend/microservice/temp/ +backend/microservice/api/__pycache__/ diff --git a/backend/api/api/Controllers/ModelController.cs b/backend/api/api/Controllers/ModelController.cs index b997efa3..4bc094cd 100644 --- a/backend/api/api/Controllers/ModelController.cs +++ b/backend/api/api/Controllers/ModelController.cs @@ -14,22 +14,28 @@ namespace api.Controllers { private IMlConnectionService _mlService; + private readonly IDatasetService _datasetService; + private readonly IFileService _fileService; private readonly IModelService _modelService; private JwtToken jwtToken; - public ModelController(IMlConnectionService mlService, IModelService modelService, IConfiguration configuration) + public ModelController(IMlConnectionService mlService, IModelService modelService, IDatasetService datasetService, IFileService fileService, IConfiguration configuration) { _mlService = mlService; _modelService = modelService; + _datasetService = datasetService; + _fileService = fileService; jwtToken = new JwtToken(configuration); } [HttpPost("sendModel")] [Authorize(Roles = "User")] - public async Task<ActionResult<string>> Test([FromBody] object model) + public async Task<ActionResult<string>> Test([FromBody] Model model) { - var result = await _mlService.SendModelAsync(model); + var dataset = _datasetService.GetOneDataset(model.datasetId); + var filepath = _fileService.GetFilePath(dataset.fileId, dataset.username); + var result = await _mlService.SendModelAsync(model, filepath); return Ok(result); } diff --git a/backend/api/api/Controllers/PredictorController.cs b/backend/api/api/Controllers/PredictorController.cs index 7f8f1692..63c5d2bf 100644 --- a/backend/api/api/Controllers/PredictorController.cs +++ b/backend/api/api/Controllers/PredictorController.cs @@ -74,6 +74,36 @@ namespace api.Controllers return _predictorService.SearchPredictors(name, username); } + //SEARCH za predictore (public ili private sa ovim imenom ) + // GET api/<PredictorController>/search/{name} + [HttpGet("{id}")] + [Authorize(Roles = "User")] + public ActionResult<Predictor> GetPredictor(string id) + { + string username; + var header = Request.Headers[HeaderNames.Authorization]; + if (AuthenticationHeaderValue.TryParse(header, out var headerValue)) + { + var scheme = headerValue.Scheme; + var parameter = headerValue.Parameter; + username = jwtToken.TokenToUsername(parameter); + if (username == null) + return null; + } + else + return BadRequest(); + + //ako bude trebao ID, samo iz baze uzeti + + Predictor predictor = _predictorService.GetPredictor(username, id); + + if (predictor == null) + return NotFound($"Predictor with id = {id} not found"); + + return predictor; + } + + //da li da se odvoji search za public i posebno za private? // GET api/<PredictorController>/{name} [HttpGet("{name}")] diff --git a/backend/api/api/Controllers/UserController.cs b/backend/api/api/Controllers/UserController.cs index 0287f3cb..741382b8 100644 --- a/backend/api/api/Controllers/UserController.cs +++ b/backend/api/api/Controllers/UserController.cs @@ -135,8 +135,7 @@ namespace api.Controllers else return BadRequest(); - userService.Update(username, user); - return NoContent(); + return Ok(userService.Update(username, user)); } // DELETE api/<UserController>/5 diff --git a/backend/api/api/Models/Model.cs b/backend/api/api/Models/Model.cs index 5678daaf..2baab1c0 100644 --- a/backend/api/api/Models/Model.cs +++ b/backend/api/api/Models/Model.cs @@ -38,10 +38,13 @@ namespace api.Models public int batchSize { get; set; } // na izlazu je moguce da bude vise neurona (klasifikacioni problem sa vise od 2 klase) public int outputNeurons { get; set; } - public string inputLayerActivationFunction { get; set; } - public string hiddenLayerActivationFunction { get; set; } + public string[] hiddenLayerActivationFunctions { get; set; } public string outputLayerActivationFunction { get; set; } + public string[] metrics { get; set; } + public int epochs { get; set; } + public string nullValues { get; set; } + public string[] nullValuesReplacers { get; set; } } } diff --git a/backend/api/api/Services/DatasetService.cs b/backend/api/api/Services/DatasetService.cs index ab19935d..2ff271f3 100644 --- a/backend/api/api/Services/DatasetService.cs +++ b/backend/api/api/Services/DatasetService.cs @@ -68,6 +68,11 @@ namespace api.Services } //odraditi za pretragu getOne + public Dataset GetOneDataset(string id) + { + return _dataset.Find(dataset => dataset._id == id).FirstOrDefault(); + } + //ako je potrebno da se zameni name ili ekstenzija public void Update(string username, string name, Dataset dataset) { diff --git a/backend/api/api/Services/IDatasetService.cs b/backend/api/api/Services/IDatasetService.cs index 9beec400..8e62ba43 100644 --- a/backend/api/api/Services/IDatasetService.cs +++ b/backend/api/api/Services/IDatasetService.cs @@ -6,6 +6,7 @@ namespace api.Services public interface IDatasetService { Dataset GetOneDataset(string username, 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); diff --git a/backend/api/api/Services/IMlConnectionService.cs b/backend/api/api/Services/IMlConnectionService.cs index f38fb50a..ee839d28 100644 --- a/backend/api/api/Services/IMlConnectionService.cs +++ b/backend/api/api/Services/IMlConnectionService.cs @@ -3,6 +3,6 @@ namespace api.Services { public interface IMlConnectionService { - Task<string> SendModelAsync(object model); + Task<string> SendModelAsync(object model, object dataset); } }
\ No newline at end of file diff --git a/backend/api/api/Services/IModelService.cs b/backend/api/api/Services/IModelService.cs index ee5c279f..637d09a3 100644 --- a/backend/api/api/Services/IModelService.cs +++ b/backend/api/api/Services/IModelService.cs @@ -6,6 +6,7 @@ namespace api.Services public interface IModelService { Model GetOneModel(string username, string name); + Model GetOneModel(string id); List<Model> GetMyModels(string username); List<Model> GetLatestModels(string username); //List<Model> GetPublicModels(); diff --git a/backend/api/api/Services/IPredictorService.cs b/backend/api/api/Services/IPredictorService.cs index 2017add2..729dd0b6 100644 --- a/backend/api/api/Services/IPredictorService.cs +++ b/backend/api/api/Services/IPredictorService.cs @@ -6,6 +6,7 @@ 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); diff --git a/backend/api/api/Services/IUserService.cs b/backend/api/api/Services/IUserService.cs index 1cb6a609..e4a23213 100644 --- a/backend/api/api/Services/IUserService.cs +++ b/backend/api/api/Services/IUserService.cs @@ -8,7 +8,7 @@ namespace api.Services List<User> Get();// daje sve korisnike User GetUserUsername(string username); //daje korisnika po korisnickom imenu User Create(User user); // kreira korisnika - void Update(string username, User user); //apdejtuje korisnika po idu + bool Update(string username, User user); //apdejtuje korisnika po idu void Delete(string username);//brise korisnika } } diff --git a/backend/api/api/Services/MlConnectionService.cs b/backend/api/api/Services/MlConnectionService.cs index 9b167537..9c3b3fd8 100644 --- a/backend/api/api/Services/MlConnectionService.cs +++ b/backend/api/api/Services/MlConnectionService.cs @@ -6,13 +6,19 @@ namespace api.Services { public class MlConnectionService : IMlConnectionService { - public async Task<string> SendModelAsync(object model) + private RestClient client; + + public MlConnectionService() + { + this.client = new RestClient("http://127.0.0.1:5543"); + } + + public async Task<string> SendModelAsync(object model, object dataset) { - RestClient client = new RestClient("http://localhost:5000"); - var request = new RestRequest("data", Method.Post); - request.AddJsonBody(model); - var result = await client.ExecuteAsync(request); - return result.Content;//Response od ML microservisa + var request = new RestRequest("train", Method.Post); + request.AddJsonBody(new { model, dataset}); + var result = await this.client.ExecuteAsync(request); + return result.Content; //Response od ML microservisa } } } diff --git a/backend/api/api/Services/ModelService.cs b/backend/api/api/Services/ModelService.cs index f42219f5..eae8c78b 100644 --- a/backend/api/api/Services/ModelService.cs +++ b/backend/api/api/Services/ModelService.cs @@ -50,6 +50,11 @@ namespace api.Services return _model.Find(model => model.username == username && model.name == name).FirstOrDefault(); } + public Model GetOneModel(string id) + { + return _model.Find(model => model._id == id).FirstOrDefault(); + } + public void Update(string username, string name, Model model) { _model.ReplaceOne(model => model.username == username && model.name == name, model); diff --git a/backend/api/api/Services/PredictorService.cs b/backend/api/api/Services/PredictorService.cs index 05860126..01bc8359 100644 --- a/backend/api/api/Services/PredictorService.cs +++ b/backend/api/api/Services/PredictorService.cs @@ -40,6 +40,11 @@ namespace api.Services return _predictor.Find(predictor => predictor.username == username && predictor.name == name).FirstOrDefault(); } + public Predictor GetPredictor(string username, string id) + { + return _predictor.Find(predictor => predictor.username == username && predictor._id == id).FirstOrDefault(); + + } //last private models public List<Predictor> SortPredictors(string username, bool ascdsc, int latest) { diff --git a/backend/api/api/Services/UserService.cs b/backend/api/api/Services/UserService.cs index f613f923..607bb04b 100644 --- a/backend/api/api/Services/UserService.cs +++ b/backend/api/api/Services/UserService.cs @@ -7,11 +7,22 @@ namespace api.Services public class UserService : IUserService { private readonly IMongoCollection<User> _users; + private readonly IMongoClient _client; + private readonly IMongoCollection<Model> _models; + private readonly IMongoCollection<Dataset> _datasets; + private readonly IMongoCollection<FileModel> _fileModels; + private readonly IMongoCollection<Predictor> _predictors; + public UserService(IUserStoreDatabaseSettings settings, IMongoClient mongoClient) { var database = mongoClient.GetDatabase(settings.DatabaseName); _users = database.GetCollection<User>(settings.CollectionName); + _models = database.GetCollection<Model>(settings.ModelCollectionName); + _datasets= database.GetCollection<Dataset>(settings.DatasetCollectionName); + _fileModels = database.GetCollection<FileModel>(settings.FilesCollectionName); + _predictors= database.GetCollection<Predictor>(settings.PredictorCollectionName); + _client = mongoClient; } public User Create(User user) { @@ -26,10 +37,46 @@ namespace api.Services { return _users.Find(user => user.Username == username).FirstOrDefault(); } - public void Update(string username, User user) + public bool Update(string username, User user) { //username koji postoji u bazi - _users.ReplaceOne(user => user.Username == username, user); + using (var session = _client.StartSession()) + { + + 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) + { + var builderModel = Builders<Model>.Update; + var builderDataset = Builders<Dataset>.Update; + var builderFileModel = Builders<FileModel>.Update; + var builderPredictor = Builders<Predictor>.Update; + _models.UpdateMany(x => x.username == username, builderModel.Set(x => x.username, user.Username)); + _datasets.UpdateMany(x => x.username == username, builderDataset.Set(x => x.username, user.Username)); + _fileModels.UpdateMany(x => x.username == username, builderFileModel.Set(x => x.username, user.Username)); + _predictors.UpdateMany(x => x.username == username, builderPredictor.Set(x => x.username, user.Username)); + } + + //session.AbortTransaction(); + + + //session.CommitTransaction(); + } + catch (Exception e) + { + //session.AbortTransaction(); + return false; + } + return true; + } } public void Delete(string username) { diff --git a/backend/microservice/PythonServer/project/api/socket/client.py b/backend/microservice/PythonServer/project/socket_example/socket/client.py index d5740e25..d5740e25 100644 --- a/backend/microservice/PythonServer/project/api/socket/client.py +++ b/backend/microservice/PythonServer/project/socket_example/socket/client.py diff --git a/backend/microservice/PythonServer/project/api/socket/server.py b/backend/microservice/PythonServer/project/socket_example/socket/server.py index d6ff3f7c..d6ff3f7c 100644 --- a/backend/microservice/PythonServer/project/api/socket/server.py +++ b/backend/microservice/PythonServer/project/socket_example/socket/server.py diff --git a/backend/microservice/PythonServer/project/api/socket2/client.py b/backend/microservice/PythonServer/project/socket_example/socket2/client.py index 65e76b55..65e76b55 100644 --- a/backend/microservice/PythonServer/project/api/socket2/client.py +++ b/backend/microservice/PythonServer/project/socket_example/socket2/client.py diff --git a/backend/microservice/PythonServer/project/api/socket2/server.py b/backend/microservice/PythonServer/project/socket_example/socket2/server.py index c65dae78..c65dae78 100644 --- a/backend/microservice/PythonServer/project/api/socket2/server.py +++ b/backend/microservice/PythonServer/project/socket_example/socket2/server.py diff --git a/backend/microservice/api/controller.py b/backend/microservice/api/controller.py new file mode 100644 index 00000000..ceed02ad --- /dev/null +++ b/backend/microservice/api/controller.py @@ -0,0 +1,42 @@ +import flask +from flask import request, jsonify +import ml_socket +import ml_service +import tensorflow as tf +import pandas as pd + +app = flask.Flask(__name__) +app.config["DEBUG"] = True +app.config["SERVER_NAME"] = "127.0.0.1:5543" + +class train_callback(tf.keras.callbacks.Callback): + def __init__(self, x_test, y_test): + self.x_test = x_test + self.y_test = y_test + # + def on_epoch_end(self, epoch, logs=None): + print(epoch) + #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*************************************************") + f = request.json["dataset"] + dataset = pd.read_csv(f) + # + result = ml_service.train(dataset, request.json["model"], train_callback) + print(result) + return jsonify(result) + +@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) + # + #model.predict? + +print("App loaded.") +ml_socket.start() +app.run()
\ No newline at end of file diff --git a/backend/microservice/api/ml_service.py b/backend/microservice/api/ml_service.py new file mode 100644 index 00000000..efd24fdc --- /dev/null +++ b/backend/microservice/api/ml_service.py @@ -0,0 +1,155 @@ +import pandas as pd +import tensorflow as tf +import keras +import numpy as np +import csv +import json +import h5py +import sklearn.metrics as sm +from statistics import mode +from typing_extensions import Self +from copyreg import constructor +from flask import request, jsonify, render_template +from sklearn.preprocessing import LabelEncoder +from sklearn.preprocessing import StandardScaler +from sklearn.model_selection import train_test_split +from dataclasses import dataclass + +@dataclass +class TrainingResult: + accuracy: float + precision: float + recall: float + tn: float + fp: float + fn: float + tp: float + specificity: float + f1: float + mse: float + mae: float + mape: float + rmse: float + fpr: float + tpr: float + +def train(dataset, params, callback): + data = pd.DataFrame() + for col in params["inputColumns"]: + data[col]=dataset[col] + output_column = params["columnToPredict"] + data[output_column] = dataset[output_column] + # + # Brisanje null kolona / redova / zamena + #nullreplace=[ + # {"column":"Embarked","value":"C","deleteRow":false,"deleteCol":true}, + # {"column": "Cabin","value":"C123","deleteRow":"0","deleteCol":"0"}] + + null_value_options = params["nullValues"] + null_values_replacers = params["nullValuesReplacers"] + + if(null_value_options=='replace'): + print("replace null") # TODO + elif(null_value_options=='delete_rows'): + data=data.dropna() + elif(null_value_options=='delete_columns'): + data=data.dropna() + # + #print(data.isnull().any()) + # + # Brisanje kolona koje ne uticu na rezultat + # + num_rows=data.shape[0] + for col in data.columns: + if((data[col].nunique()==(num_rows)) and (data[col].dtype==np.object_)): + data.pop(col) + # + # 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]) + 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) + # + # Input - output + # + x_columns = [] + for col in data.columns: + if(col!=output_column): + x_columns.append(col) + x = data[x_columns].values + y = data[output_column].values + # + # Podela na test i trening skupove + # + test=params["randomTestSetDistribution"] + randomOrder = params["randomOrder"] + if(randomOrder): + random=50 + else: + random=0 + x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=test, random_state=random) + # + # Skaliranje vrednosti + # + scaler=StandardScaler() + scaler.fit(x_train) + x_test=scaler.transform(x_test) + x_train=scaler.transform(x_train) + # + # Treniranje modela + # + classifier=tf.keras.Sequential() + hidden_layer_neurons = params["hiddenLayerNeurons"] + for func in params["hiddenLayerActivationFunctions"]: + classifier.add(tf.keras.layers.Dense(units=hidden_layer_neurons,activation=func)) + output_func = params["outputLayerActivationFunction"] + classifier.add(tf.keras.layers.Dense(units=1,activation=output_func)) + optimizer = params["optimizer"] + metrics=params['metrics'] + loss_func=params["lossFunction"] + classifier.compile(optimizer=optimizer, loss=loss_func,metrics=metrics) + batch_size = params["batchSize"] + epochs = params["epochs"] + history=classifier.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, callbacks=callback(x_test, y_test), validation_split=0.2) # TODO params["validationSplit"] + # + # Test + # + y_pred=classifier.predict(x_test) + y_pred=(y_pred>=0.5).astype('int') + #y_pred=(y_pred * 100).astype('int') + y_pred=y_pred.flatten() + result=pd.DataFrame({"Actual":y_test,"Predicted":y_pred}) + model_name = params['_id'] + classifier.save("temp/"+model_name, save_format='h5') + # + # Metrike + # + print("HELLO???") + print(result) + print("HELLO???") + accuracy = float(sm.accuracy_score(y_test,y_pred)) + precision = float(sm.precision_score(y_test,y_pred)) + recall = float(sm.recall_score(y_test,y_pred)) + tn, fp, fn, tp = sm.confusion_matrix(y_test,y_pred).ravel() + specificity = float(tn / (tn+fp)) + f1 = float(sm.f1_score(y_test,y_pred)) + mse = float(sm.mean_squared_error(y_test,y_pred)) + mae = float(sm.mean_absolute_error(y_test,y_pred)) + mape = float(sm.mean_absolute_percentage_error(y_test,y_pred)) + rmse = float(np.sqrt(sm.mean_squared_error(y_test,y_pred))) + fpr, tpr, _ = sm.roc_curve(y_test,y_pred) + # TODO upload trenirani model nazad na backend + return TrainingResult(accuracy, precision, recall, float(tn), float(fp), float(fn), float(tp), specificity, f1, mse, mae, mape, rmse, fpr.tolist(), tpr.tolist()) + + + + diff --git a/backend/microservice/ml_socket.py b/backend/microservice/api/ml_socket.py index 5489b787..65dd7321 100644 --- a/backend/microservice/ml_socket.py +++ b/backend/microservice/api/ml_socket.py @@ -14,12 +14,15 @@ def get_or_create_eventloop(): # create handler for each connection async def handler(websocket, path): #data = json.loads(await websocket.recv()) - #reply = f"Data recieved as: {data}!" #print(data['test']) msg = await websocket.recv() - await websocket.send("[" + msg + "]") + print(msg) -start_server = websockets.serve(handler, "localhost", 5027) +async def start(): + start_server = websockets.serve(handler, "localhost", 5027) + print('Websocket starting...') + get_or_create_eventloop().run_until_complete(start_server) + get_or_create_eventloop().run_forever() -get_or_create_eventloop().run_until_complete(start_server) -get_or_create_eventloop().run_forever()
\ No newline at end of file +async def send(msg): + await websocket.send(msg)
\ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8c025c8b..3d909d7a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,6 +26,7 @@ "bootstrap": "^5.1.3", "chart.js": "^3.7.1", "csv-parser": "^3.0.0", + "d3-graphviz": "^2.6.1", "mdb-angular-ui-kit": "^2.0.0", "ng-uikit-pro-standard": "^1.0.0", "ng2-charts": "^3.0.8", @@ -33,7 +34,7 @@ "ngx-cookie-service": "^13.1.2", "ngx-csv-parser": "^0.0.7", "rxjs": "~7.5.0", - "tslib": "^2.3.0", + "tslib": "^2.3.1", "websocket-ts": "^1.1.1", "zone.js": "~0.11.4" }, @@ -42,6 +43,7 @@ "@angular/cli": "~13.2.5", "@angular/compiler-cli": "~13.2.0", "@types/crypto-js": "^4.1.1", + "@types/d3-graphviz": "^2.6.7", "@types/jasmine": "~3.10.0", "@types/node": "^12.11.1", "jasmine-core": "~4.0.0", @@ -2743,6 +2745,57 @@ "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==", "dev": true }, + "node_modules/@types/d3-color": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz", + "integrity": "sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA==", + "dev": true + }, + "node_modules/@types/d3-graphviz": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@types/d3-graphviz/-/d3-graphviz-2.6.7.tgz", + "integrity": "sha512-dKJjD5HiFvAmC0FL/c70VB1diie8FCpyiCZfxMlf6TwYBqUyFvS4XJt6MoxjIuQTJhKDBGzrIvDOgM8gYMLSVA==", + "dev": true, + "dependencies": { + "@types/d3-selection": "^1", + "@types/d3-transition": "^1", + "@types/d3-zoom": "^1" + } + }, + "node_modules/@types/d3-interpolate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz", + "integrity": "sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg==", + "dev": true, + "dependencies": { + "@types/d3-color": "^1" + } + }, + "node_modules/@types/d3-selection": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.3.tgz", + "integrity": "sha512-GjKQWVZO6Sa96HiKO6R93VBE8DUW+DDkFpIMf9vpY5S78qZTlRRSNUsHr/afDpF7TvLDV7VxrUFOWW7vdIlYkA==", + "dev": true + }, + "node_modules/@types/d3-transition": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.3.2.tgz", + "integrity": "sha512-J+a3SuF/E7wXbOSN19p8ZieQSFIm5hU2Egqtndbc54LXaAEOpLfDx4sBu/PKAKzHOdgKK1wkMhINKqNh4aoZAg==", + "dev": true, + "dependencies": { + "@types/d3-selection": "^1" + } + }, + "node_modules/@types/d3-zoom": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.8.3.tgz", + "integrity": "sha512-3kHkL6sPiDdbfGhzlp5gIHyu3kULhtnHTTAl3UBZVtWB1PzcLL8vdmz5mTx7plLiUqOA2Y+yT2GKjt/TdA2p7Q==", + "dev": true, + "dependencies": { + "@types/d3-interpolate": "^1", + "@types/d3-selection": "^1" + } + }, "node_modules/@types/eslint": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", @@ -4565,6 +4618,99 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "node_modules/d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "node_modules/d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, + "node_modules/d3-drag": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", + "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "dependencies": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "node_modules/d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + }, + "node_modules/d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + }, + "node_modules/d3-graphviz": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/d3-graphviz/-/d3-graphviz-2.6.1.tgz", + "integrity": "sha512-878AFSagQyr5tTOrM7YiVYeUC2/NoFcOB3/oew+LAML0xekyJSw9j3WOCUMBsc95KYe9XBYZ+SKKuObVya1tJQ==", + "dependencies": { + "d3-dispatch": "^1.0.3", + "d3-format": "^1.2.0", + "d3-interpolate": "^1.1.5", + "d3-path": "^1.0.5", + "d3-selection": "^1.1.0", + "d3-timer": "^1.0.6", + "d3-transition": "^1.1.1", + "d3-zoom": "^1.5.0", + "viz.js": "^1.8.2" + } + }, + "node_modules/d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "dependencies": { + "d3-color": "1" + } + }, + "node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-selection": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", + "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + }, + "node_modules/d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "node_modules/d3-transition": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", + "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", + "dependencies": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "node_modules/d3-zoom": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", + "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "dependencies": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, "node_modules/date-format": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.4.tgz", @@ -11042,6 +11188,12 @@ "node": ">= 0.8" } }, + "node_modules/viz.js": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/viz.js/-/viz.js-1.8.2.tgz", + "integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ==", + "deprecated": "no longer supported" + }, "node_modules/void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", @@ -13445,6 +13597,57 @@ "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==", "dev": true }, + "@types/d3-color": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz", + "integrity": "sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA==", + "dev": true + }, + "@types/d3-graphviz": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@types/d3-graphviz/-/d3-graphviz-2.6.7.tgz", + "integrity": "sha512-dKJjD5HiFvAmC0FL/c70VB1diie8FCpyiCZfxMlf6TwYBqUyFvS4XJt6MoxjIuQTJhKDBGzrIvDOgM8gYMLSVA==", + "dev": true, + "requires": { + "@types/d3-selection": "^1", + "@types/d3-transition": "^1", + "@types/d3-zoom": "^1" + } + }, + "@types/d3-interpolate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz", + "integrity": "sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg==", + "dev": true, + "requires": { + "@types/d3-color": "^1" + } + }, + "@types/d3-selection": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.3.tgz", + "integrity": "sha512-GjKQWVZO6Sa96HiKO6R93VBE8DUW+DDkFpIMf9vpY5S78qZTlRRSNUsHr/afDpF7TvLDV7VxrUFOWW7vdIlYkA==", + "dev": true + }, + "@types/d3-transition": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.3.2.tgz", + "integrity": "sha512-J+a3SuF/E7wXbOSN19p8ZieQSFIm5hU2Egqtndbc54LXaAEOpLfDx4sBu/PKAKzHOdgKK1wkMhINKqNh4aoZAg==", + "dev": true, + "requires": { + "@types/d3-selection": "^1" + } + }, + "@types/d3-zoom": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.8.3.tgz", + "integrity": "sha512-3kHkL6sPiDdbfGhzlp5gIHyu3kULhtnHTTAl3UBZVtWB1PzcLL8vdmz5mTx7plLiUqOA2Y+yT2GKjt/TdA2p7Q==", + "dev": true, + "requires": { + "@types/d3-interpolate": "^1", + "@types/d3-selection": "^1" + } + }, "@types/eslint": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", @@ -14874,6 +15077,99 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, + "d3-drag": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", + "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "requires": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + }, + "d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + }, + "d3-graphviz": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/d3-graphviz/-/d3-graphviz-2.6.1.tgz", + "integrity": "sha512-878AFSagQyr5tTOrM7YiVYeUC2/NoFcOB3/oew+LAML0xekyJSw9j3WOCUMBsc95KYe9XBYZ+SKKuObVya1tJQ==", + "requires": { + "d3-dispatch": "^1.0.3", + "d3-format": "^1.2.0", + "d3-interpolate": "^1.1.5", + "d3-path": "^1.0.5", + "d3-selection": "^1.1.0", + "d3-timer": "^1.0.6", + "d3-transition": "^1.1.1", + "d3-zoom": "^1.5.0", + "viz.js": "^1.8.2" + } + }, + "d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "d3-selection": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", + "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + }, + "d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "d3-transition": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", + "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", + "requires": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "d3-zoom": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", + "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, "date-format": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.4.tgz", @@ -19594,6 +19890,11 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "dev": true }, + "viz.js": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/viz.js/-/viz.js-1.8.2.tgz", + "integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ==" + }, "void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8cd6db58..975c740f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,6 +28,7 @@ "bootstrap": "^5.1.3", "chart.js": "^3.7.1", "csv-parser": "^3.0.0", + "d3-graphviz": "^2.6.1", "mdb-angular-ui-kit": "^2.0.0", "ng-uikit-pro-standard": "^1.0.0", "ng2-charts": "^3.0.8", @@ -35,7 +36,7 @@ "ngx-cookie-service": "^13.1.2", "ngx-csv-parser": "^0.0.7", "rxjs": "~7.5.0", - "tslib": "^2.3.0", + "tslib": "^2.3.1", "websocket-ts": "^1.1.1", "zone.js": "~0.11.4" }, @@ -44,6 +45,7 @@ "@angular/cli": "~13.2.5", "@angular/compiler-cli": "~13.2.0", "@types/crypto-js": "^4.1.1", + "@types/d3-graphviz": "^2.6.7", "@types/jasmine": "~3.10.0", "@types/node": "^12.11.1", "jasmine-core": "~4.0.0", diff --git a/frontend/src/app/_data/Model.ts b/frontend/src/app/_data/Model.ts index f6e01d08..32247bbd 100644 --- a/frontend/src/app/_data/Model.ts +++ b/frontend/src/app/_data/Model.ts @@ -15,7 +15,7 @@ export default class Model { public randomTestSetDistribution: number = 0.1, //0.1-0.9 (10% - 90%) JESTE OVDE ZAKUCANO 10, AL POSLATO JE KAO 0.1 BACK-U // Neural net training settings - public type: ANNType = ANNType.FullyConnected, + public type: ProblemType = ProblemType.Regression, public encoding: Encoding = Encoding.Label, public optimizer: Optimizer = Optimizer.Adam, public lossFunction: LossFunction = LossFunction.MeanSquaredError, @@ -23,18 +23,21 @@ export default class Model { public hiddenLayerNeurons: number = 1, public hiddenLayers: number = 1, public batchSize: number = 5, - public inputLayerActivationFunction: ActivationFunction = ActivationFunction.Sigmoid, - public hiddenLayerActivationFunction: ActivationFunction = ActivationFunction.Sigmoid, + public hiddenLayerActivationFunctions: string[] = ['sigmoid'], + //public inputLayerActivationFunction: ActivationFunction = ActivationFunction.Sigmoid, public outputLayerActivationFunction: ActivationFunction = ActivationFunction.Sigmoid, public username: string = '', public nullValues: NullValueOptions = NullValueOptions.DeleteRows, - public nullValuesReplacers = [] + public nullValuesReplacers = [], + public metrics: Metric[] = [], // TODO add to add-model form + public epochs: number = 5 // TODO add to add-model form ) { } } -export enum ANNType { - FullyConnected = 'potpuno povezana', - Convolutional = 'konvoluciona' +export enum ProblemType { + Regression = 'regresioni', + BinaryClassification = 'binarni-klasifikacioni', + MultiClassification = 'multi-klasifikacioni' } // replaceMissing srednja vrednost mean, median, najcesca vrednost (mode) @@ -110,4 +113,11 @@ export enum ReplaceWith { None = 'Popuni...', Mean = 'Srednja vrednost', Median = 'Medijana' +} + +export enum Metric { + MSE = 'mse', + MAE = 'mae', + RMSE = 'rmse' + //... }
\ No newline at end of file diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.css b/frontend/src/app/_elements/annvisual/annvisual.component.css new file mode 100644 index 00000000..857a3390 --- /dev/null +++ b/frontend/src/app/_elements/annvisual/annvisual.component.css @@ -0,0 +1,4 @@ +#graph{ + width: 100%; + text-align: center; +}
\ No newline at end of file diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.html b/frontend/src/app/_elements/annvisual/annvisual.component.html new file mode 100644 index 00000000..f23022de --- /dev/null +++ b/frontend/src/app/_elements/annvisual/annvisual.component.html @@ -0,0 +1,5 @@ +<div style="text-align: center; width: 100%;" > + <button (click)="d3()" mat-raised-button color="primary">Prikaz veštačke neuronske mreže</button> + <div id="graph" align-items-center ></div> + </div> + diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.spec.ts b/frontend/src/app/_elements/annvisual/annvisual.component.spec.ts new file mode 100644 index 00000000..cb07ef1d --- /dev/null +++ b/frontend/src/app/_elements/annvisual/annvisual.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AnnvisualComponent } from './annvisual.component'; + +describe('AnnvisualComponent', () => { + let component: AnnvisualComponent; + let fixture: ComponentFixture<AnnvisualComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AnnvisualComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AnnvisualComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.ts b/frontend/src/app/_elements/annvisual/annvisual.component.ts new file mode 100644 index 00000000..ff5b45d6 --- /dev/null +++ b/frontend/src/app/_elements/annvisual/annvisual.component.ts @@ -0,0 +1,47 @@ +import { Component, OnInit,Input } from '@angular/core'; +import Model from 'src/app/_data/Model'; +import { graphviz } from 'd3-graphviz'; + +@Component({ + selector: 'app-annvisual', + templateUrl: './annvisual.component.html', + styleUrls: ['./annvisual.component.css'] +}) +export class AnnvisualComponent implements OnInit { + ngOnInit(): void { + throw new Error('Method not implemented.'); + } + + @Input() model: Model = new Model(); + + d3(){ + let inputlayerstring:string=''; + let hiddenlayerstring:string=''; + let digraphstring:string='digraph {'; + + for(let i=0;i<this.model.inputNeurons;i++) + { + inputlayerstring=inputlayerstring+'i'+i+','; + } + inputlayerstring=inputlayerstring.slice(0,-1); + + digraphstring=digraphstring+'->'; + + for(let j=0;j<this.model.hiddenLayers;j++) + { + for(let i=0;i<this.model.hiddenLayerNeurons;i++) + { + hiddenlayerstring=hiddenlayerstring+'h'+j+'_'+i+','; + } + hiddenlayerstring=hiddenlayerstring.slice(0,1); + digraphstring=digraphstring+hiddenlayerstring+'->'; + hiddenlayerstring=''; + } + digraphstring=digraphstring+'o}'; + alert(digraphstring); + + graphviz('#graph').renderDot(digraphstring); + } + + +} 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 92d747e2..b4690154 100644 --- a/frontend/src/app/_elements/item-predictor/item-predictor.component.html +++ b/frontend/src/app/_elements/item-predictor/item-predictor.component.html @@ -19,6 +19,6 @@ </div> </div> <div class="card-footer text-center"> - <a routerLink="predict" mat-raised-button color="primary">Iskoristi</a> + <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/reactive-background/reactive-background.component.css b/frontend/src/app/_elements/reactive-background/reactive-background.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.css diff --git a/frontend/src/app/_elements/reactive-background/reactive-background.component.html b/frontend/src/app/_elements/reactive-background/reactive-background.component.html new file mode 100644 index 00000000..756952fb --- /dev/null +++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.html @@ -0,0 +1 @@ +<canvas id="bgCanvas" width="200" height="200" style="position: fixed; z-index: -1;"></canvas>
\ No newline at end of file diff --git a/frontend/src/app/_elements/reactive-background/reactive-background.component.spec.ts b/frontend/src/app/_elements/reactive-background/reactive-background.component.spec.ts new file mode 100644 index 00000000..441f4b11 --- /dev/null +++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReactiveBackgroundComponent } from './reactive-background.component'; + +describe('ReactiveBackgroundComponent', () => { + let component: ReactiveBackgroundComponent; + let fixture: ComponentFixture<ReactiveBackgroundComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ReactiveBackgroundComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ReactiveBackgroundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/reactive-background/reactive-background.component.ts b/frontend/src/app/_elements/reactive-background/reactive-background.component.ts new file mode 100644 index 00000000..8294a8a5 --- /dev/null +++ b/frontend/src/app/_elements/reactive-background/reactive-background.component.ts @@ -0,0 +1,167 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-reactive-background', + templateUrl: './reactive-background.component.html', + styleUrls: ['./reactive-background.component.css'] +}) +export class ReactiveBackgroundComponent implements OnInit { + + numPoints: number = 450; + speed: number = 0.001; // 0-1 + rotateInterval: number = 1000; + maxSize: number = 6; + + minDistance: number = 0.07; //0-1 + cursorDistance: number = 0.07; + + private points: Point[] = []; + + private width = 200; + private height = 200; + private ratio = 1; + + private canvas?: HTMLCanvasElement; + private ctx?: CanvasRenderingContext2D; + + private time: number = 0; + + constructor() { } + + private mouseX = 0; + private mouseY = 0; + + ngOnInit(): void { + + document.addEventListener('mousemove', (e) => { + this.mouseX = e.clientX / this.width; + this.mouseY = e.clientY / this.height; + }) + + this.canvas = (<HTMLCanvasElement>document.getElementById('bgCanvas')); + const ctx = this.canvas.getContext('2d'); + if (ctx) { + this.ctx = ctx; + } else { + console.warn('Could not get canvas context!'); + } + + let i = 0; + while (i < this.numPoints) { + const x = Math.random(); + const y = Math.random(); + const size = (Math.random() * 0.8 + 0.2) * this.maxSize; + const direction = Math.random() * 360; + this.points.push(new Point(x, y, size, direction)); + i++; + } + + window.addEventListener('resize', () => { this.resize() }); + this.resize(); + + setInterval(() => { + this.drawBackground(); + }, 1000 / 60); + } + + drawBackground() { + if (!this.ctx || !this.canvas) return; + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + + this.ctx.fillStyle = "#222277"; + this.ctx.fillRect(0, 0, this.width, this.height); + + this.points.forEach((point, index) => { + this.drawLines(point, index); + this.drawPoint(point); + this.updatePoint(point); + }); + + //this.drawPoint(new Point(this.mouseX, this.mouseY, 12, 0)); + + this.time += 1; + } + + drawLines(p: Point, index: number) { + let i = index + 1; + while (i < this.points.length) { + const otherPoint = this.points[i]; + + const dist = this.distance(p.x, p.y, otherPoint.x, otherPoint.y); + if (dist < this.minDistance) { + const h = HEX[Math.round((1 - dist / this.minDistance) * 16)] + this.ctx!.strokeStyle = '#ffffff' + h + h; + this.ctx!.beginPath(); + this.ctx!.moveTo(p.x * this.width, p.y * this.height); + this.ctx!.lineTo(otherPoint.x * this.width, otherPoint.y * this.height); + this.ctx!.stroke(); + } + + i++; + } + } + + drawPoint(p: Point) { + this.ctx!.fillStyle = '#ffffff'; + this.ctx!.beginPath(); + this.ctx!.arc(p.x * this.width, p.y * this.height, p.size, 0, 2 * Math.PI); + this.ctx!.fill(); + } + + resize() { + this.width = window.innerWidth; + this.height = window.innerHeight; + this.ratio = this.width / this.height; + + if (this.canvas) { + this.canvas.width = this.width; + this.canvas.height = this.height; + } + + this.drawBackground(); + } + + updatePoint(p: Point) { + const vx = Math.sin(p.direction); + const vy = Math.cos(p.direction); + + p.x = p.x + vx * this.speed; + p.y = p.y + vy * this.speed; + + const mx = this.mouseX; + const my = this.mouseY; + const distToCursor = this.distance(p.x, p.y, mx, my); + if (distToCursor < this.cursorDistance) { + + p.x -= ((mx - p.x) / distToCursor) / 500; + p.y -= ((my - p.y) / distToCursor) / 500; + + const grd = this.ctx!.createLinearGradient(p.x * this.width, p.y * this.height, mx * this.width, my * this.height); + grd.addColorStop(0, '#ff0000ff'); + grd.addColorStop(1, '#ff000000'); + this.ctx!.strokeStyle = grd; + this.ctx!.beginPath(); + this.ctx!.moveTo(p.x * this.width, p.y * this.height); + this.ctx!.lineTo(mx * this.width, my * this.height); + this.ctx!.stroke(); + } + + p.x %= 1; + p.y %= 1; + } + + distance(x1: number, y1: number, x2: number, y2: number): number { + return Math.sqrt(((x2 - x1) ** 2) + ((y2 / this.ratio - y1 / this.ratio) ** 2)); + } +} + +class Point { + constructor( + public x: number, + public y: number, + public size: number, + public direction: number + ) { } +} + +const HEX = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
\ No newline at end of file diff --git a/frontend/src/app/_pages/add-model/add-model.component.html b/frontend/src/app/_pages/add-model/add-model.component.html index 09a11e37..662d34de 100644 --- a/frontend/src/app/_pages/add-model/add-model.component.html +++ b/frontend/src/app/_pages/add-model/add-model.component.html @@ -40,21 +40,23 @@ 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"> <div *ngIf="showMyDatasets" class="overflow-auto" style="max-height: 500px;"> <ul class="list-group"> - <li class="list-group-item p-3" *ngFor="let dataset of myDatasets" + <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> + (click)="scrollToNextForm(); selectThisDataset(dataset);"></app-item-dataset> </li> </ul> </div> </div> <app-dataset-load *ngIf="!showMyDatasets" id="dataset" - (loaded)="datasetLoaded = true; selectedDataset = datasetLoadComponent?.dataset; datasetFile = datasetLoadComponent?.csvRecords; datasetHasHeader = datasetLoadComponent?.dataset!.hasHeader"> + (loaded)="scrollToNextForm(); datasetLoaded = true; selectedDataset = datasetLoadComponent?.dataset; datasetFile = datasetLoadComponent?.csvRecords; datasetHasHeader = datasetLoadComponent?.dataset!.hasHeader"> </app-dataset-load> <div class="px-5 mt-5"> <app-datatable [data]="datasetFile" [hasHeader]="datasetHasHeader"></app-datatable> @@ -62,6 +64,7 @@ </div> <!-- ULAZNE/IZLAZNE KOLONE --> + <span id="selectInAndOuts"></span> <div *ngIf="selectedDataset"> <div class="row"> <div class="col d-flex justify-content-center"> @@ -70,7 +73,8 @@ <br> <div *ngFor="let item of selectedDataset.header; let i = index"> <input class="form-check-input" type="checkbox" value="{{item}}" id="cb_{{item}}" - name="cbsNew" checked [disabled]="this.selectedOutputColumnVal == item"> + name="cbsNew" [checked]="this.selectedOutputColumnVal != item" + [disabled]="this.selectedOutputColumnVal == item"> <label class="form-check-label" for="cb_{{item}}"> {{item}} </label> @@ -92,7 +96,7 @@ </div> - <div class="my-2" *ngIf="datasetFile"> + <div class="mt-5" *ngIf="datasetFile"> <h2>Popunjavanje nedostajućih vrednosti:</h2> <div class="form-check"> <input type="radio" [(ngModel)]="newModel.nullValues" [value]="NullValueOptions.DeleteRows" @@ -151,8 +155,6 @@ </div> </div> - - <h2 class="mt-5 mb-4">Parametri treniranja:</h2> <div> @@ -160,11 +162,12 @@ <div class="col-1"> </div> <div class="col-3"> - <label for="type" class="col-form-label">Tip mreže: </label> + <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"> - <option *ngFor="let option of Object.keys(ANNType); let optionName of Object.values(ANNType)" + <option + *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" [value]="option"> {{ optionName }} </option> @@ -177,7 +180,8 @@ </div> <div class="col-1"> <input type="number" min="1" class="form-control" name="hiddenLayers" - [(ngModel)]="newModel.hiddenLayers"> + [(ngModel)]="newModel.hiddenLayers" + (change)="newModel.hiddenLayerActivationFunctions = [].constructor(newModel.hiddenLayers).fill(newModel.hiddenLayerActivationFunctions[0])"> </div> </div> @@ -259,52 +263,36 @@ </div> <div class="row p-2"> - <div class="col-1"> - </div> + <div class="col-3"></div> <div class="col-3"> - <label for="inputLayerActivationFunction" class="col-form-label">Funkcija aktivacije ulaznog - sloja:</label> + <label for="hiddenLayerActivationFunction" class="col-form-label">Funkcija aktivacije skrivenih + slojeva:</label> </div> - <div class="col-2"> - <select id=inputLayerActivationFunctionOptions class="form-control" - name="inputLayerActivationFunction" [(ngModel)]="newModel.inputLayerActivationFunction"> - <option - *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> + <div class="col-3"> + <div *ngFor="let item of [].constructor(newModel.hiddenLayers); let i = index"> + <select [id]="'hiddenLayerActivationFunctionOption_'+i" class="form-control" + [(ngModel)]="newModel.hiddenLayerActivationFunctions[i]"> + <option + *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> </div> + <div class="col-3"></div> + </div> + + <div class="row p-2"> <div class="col-1"> </div> - <div class="col-5"> + <div class="col-3"> <label for="splitYesNo" class="form-check-label">Podela test skupa: <input id="splitYesNo" class="form-check-input" type="checkbox" [checked]="newModel.randomTestSet" (change)="newModel.randomTestSet = !newModel.randomTestSet"> </label> </div> - <div class="col"> - </div> - </div> - - <div class="row p-2"> - <div class="col-1"> - </div> - <div class="col-3"> - <label for="hiddenLayerActivationFunction" class="col-form-label">Funkcija aktivacije skrivenih - slojeva:</label> - </div> - <div class="col-2"> - <select id=hiddenLayerActivationFunctionOptions class="form-control" - name="hiddenLayerActivationFunction" [(ngModel)]="newModel.hiddenLayerActivationFunction"> - <option - *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> <div class="col-1"> </div> <div class="col-2"> @@ -354,7 +342,7 @@ <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="addModel();">Sačuvaj model</button> <div class="col"></div> - <button class="btn btn-lg col-4 disabled" style="background-color:#003459; color:white;" + <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="trainModel();">Treniraj model</button> <div class="col"></div> </div> diff --git a/frontend/src/app/_pages/add-model/add-model.component.ts b/frontend/src/app/_pages/add-model/add-model.component.ts index 995aaa87..77a506d5 100644 --- a/frontend/src/app/_pages/add-model/add-model.component.ts +++ b/frontend/src/app/_pages/add-model/add-model.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import Model, { ReplaceWith } from 'src/app/_data/Model'; -import { ANNType, Encoding, ActivationFunction, LossFunction, Optimizer, NullValueOptions } from 'src/app/_data/Model'; +import { ProblemType, Encoding, ActivationFunction, LossFunction, Optimizer, NullValueOptions } from 'src/app/_data/Model'; import { DatasetLoadComponent } from 'src/app/_elements/dataset-load/dataset-load.component'; import { ModelsService } from 'src/app/_services/models.service'; import shared from 'src/app/Shared'; @@ -23,7 +23,7 @@ export class AddModelComponent implements OnInit { newModel: Model; - ANNType = ANNType; + ProblemType = ProblemType; Encoding = Encoding; ActivationFunction = ActivationFunction; LossFunction = LossFunction; @@ -47,10 +47,13 @@ export class AddModelComponent implements OnInit { tempTestSetDistribution: number = 90; + //accepted: Boolean; + term: string = ""; + constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService) { this.newModel = new Model(); - this.models.getMyDatasets().subscribe((datasets) => { + this.datasets.getMyDatasets().subscribe((datasets) => { this.myDatasets = datasets; }); } @@ -73,19 +76,28 @@ export class AddModelComponent implements OnInit { addModel() { if (!this.showMyDatasets) - this.saveModelWithNewDataset(); + this.saveModelWithNewDataset(_ => { console.log('MODEL ADDED (with new dataset).') }); else - this.saveModelWithExistingDataset(); + this.saveModelWithExistingDataset(_ => { console.log('MODEL ADDED (with existing dataset).') }); } trainModel() { - this.saveModelWithNewDataset().subscribe((modelId: any) => { - if (modelId) - this.models.trainModel(modelId); - }); //privremeno cuvanje modela => vraca id sacuvanog modela koji cemo da treniramo sad + let saveFunc; + + if (!this.showMyDatasets) + saveFunc = (x: (arg0: any) => void) => { this.saveModelWithNewDataset(x) }; + else + saveFunc = (x: (arg0: any) => void) => { this.saveModelWithExistingDataset(x) }; + + saveFunc(((model: any) => { + console.log('Saved, training model...', model); + this.models.trainModel(model).subscribe(response => { + console.log('Train model complete!', response); + }); + })); //privremeno cuvanje modela => vraca id sacuvanog modela koji cemo da treniramo sad } - saveModelWithNewDataset(): any { + saveModelWithNewDataset(callback: ((arg0: any) => void)) { this.getCheckedInputCols(); this.getCheckedOutputCol(); @@ -93,14 +105,14 @@ export class AddModelComponent implements OnInit { if (this.validationInputsOutput()) { console.log('ADD MODEL: STEP 1 - UPLOAD FILE'); if (this.datasetLoadComponent) { - + console.log("this.datasetLoadComponent.files:", this.datasetLoadComponent.files); this.models.uploadData(this.datasetLoadComponent.files[0]).subscribe((file) => { console.log('ADD MODEL: STEP 2 - ADD DATASET WITH FILE ID ' + file._id); if (this.datasetLoadComponent) { this.datasetLoadComponent.dataset.fileId = file._id; this.datasetLoadComponent.dataset.username = shared.username; - this.models.addDataset(this.datasetLoadComponent.dataset).subscribe((dataset) => { + this.datasets.addDataset(this.datasetLoadComponent.dataset).subscribe((dataset) => { console.log('ADD MODEL: STEP 3 - ADD MODEL WITH DATASET ID ', dataset._id); this.newModel.datasetId = dataset._id; @@ -114,7 +126,7 @@ export class AddModelComponent implements OnInit { this.newModel.username = shared.username; this.models.addModel(this.newModel).subscribe((response) => { - console.log('ADD MODEL: DONE! REPLY:\n', response); + callback(response); }, (error) => { alert("Model sa unetim nazivom već postoji u Vašoj kolekciji.\nPromenite naziv modela i nastavite sa kreiranim datasetom."); }); //kraj addModel subscribe @@ -130,8 +142,7 @@ export class AddModelComponent implements OnInit { } //kraj prvog ifa } - saveModelWithExistingDataset(): any { - + saveModelWithExistingDataset(callback: ((arg0: any) => void)): any { if (this.selectedDataset) { //dataset je izabran this.getCheckedInputCols(); this.getCheckedOutputCol(); @@ -144,7 +155,7 @@ export class AddModelComponent implements OnInit { this.newModel.username = shared.username; this.models.addModel(this.newModel).subscribe((response) => { - console.log('ADD MODEL: DONE! REPLY:\n', response); + callback(response); }, (error) => { alert("Model sa unetim nazivom već postoji u Vašoj kolekciji.\nPromenite naziv modela i nastavite sa kreiranim datasetom."); }); @@ -216,12 +227,16 @@ export class AddModelComponent implements OnInit { if (datasets[i]._id == dataset._id) }*/ - //this.datasetFile = csvRecords; this.datasets.getDatasetFile(dataset.fileId).subscribe((file: string | undefined) => { if (file) { this.datasetFile = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter == "") ? "," : dataset.delimiter); - this.datasetFile.length = this.datasetFile.length - 1; + for (let i = this.datasetFile.length - 1; i >= 0; i--) { //moguce da je vise redova na kraju fajla prazno i sl. + if (this.datasetFile[i].length != this.datasetFile[0].length) + this.datasetFile[i].pop(); + else + break; //nema potrebe dalje + } console.log(this.datasetFile); } }); @@ -230,6 +245,15 @@ export class AddModelComponent implements OnInit { this.resetCbsAndRbs(); } + scrollToNextForm() { + console.log("USAO U SCROLL"); + (<HTMLSelectElement>document.getElementById("selectInAndOuts")).scrollIntoView({ + behavior: "smooth", + block: "start", + inline: "nearest" + }); + } + resetSelectedDataset(): boolean { const temp = this.selectedDataset; this.selectedDataset = this.otherDataset; @@ -263,7 +287,7 @@ export class AddModelComponent implements OnInit { } refreshMyDatasetList() { - this.models.getMyDatasets().subscribe((datasets) => { + this.datasets.getMyDatasets().subscribe((datasets) => { this.myDatasets = datasets; }); } diff --git a/frontend/src/app/_pages/home/home.component.html b/frontend/src/app/_pages/home/home.component.html index 7e895a2d..eb59b726 100644 --- a/frontend/src/app/_pages/home/home.component.html +++ b/frontend/src/app/_pages/home/home.component.html @@ -45,12 +45,12 @@ </div> <h2 class="my-4">Pogledajte javne izvore podataka!</h2> - <app-carousel [items]="publicDatasets"> + <app-carousel *ngIf = "publicDatasets" [items]="publicDatasets"> </app-carousel> <h3><a routerLink="browse-datasets">Pogledaj sve javne izvore podataka...</a></h3> <h2 class="my-4">Iskoristite već trenirane modele!</h2> - <app-carousel [items]="publicPredictors"> + <app-carousel *ngIf = "publicPredictors" [items]="publicPredictors"> </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/home/home.component.ts b/frontend/src/app/_pages/home/home.component.ts index 7e4471e8..ed86a329 100644 --- a/frontend/src/app/_pages/home/home.component.ts +++ b/frontend/src/app/_pages/home/home.component.ts @@ -3,6 +3,8 @@ import Dataset from 'src/app/_data/Dataset'; import Predictor from 'src/app/_data/Predictor'; import { ItemDatasetComponent } from 'src/app/_elements/item-dataset/item-dataset.component'; import shared from 'src/app/Shared'; +import { DatasetsService } from 'src/app/_services/datasets.service'; +import { PredictorsService } from 'src/app/_services/predictors.service'; @Component({ selector: 'app-home', @@ -11,32 +13,19 @@ import shared from 'src/app/Shared'; }) export class HomeComponent implements OnInit { - publicDatasets: Dataset[]; - publicPredictors: Predictor[]; + publicDatasets?: Dataset[]; + publicPredictors?: Predictor[]; shared = shared; - constructor() { - this.publicDatasets = [ - new Dataset('Titanik', 'Titanik', ['Kolona1', 'Kolona2', 'Ime', 'OsobaJePreživela']), - new Dataset('Drugi Dataset', 'Lorem ipsum dolor sir amet', ['jabuka', 'kruska', 'jagoda']), - new Dataset('Dataset III', 'Kratak opis izvora podataka', ['c1', 'c2', 'c3', 'c4', 'c5']), - new Dataset('Drugi Dataset', 'Lorem ipsum dolor sir amet', ['jabuka', 'kruska', 'jagoda']), - new Dataset('Dataset III', 'Kratak opis izvora podataka', ['c1', 'c2', 'c3', 'c4', 'c5']), - new Dataset('Drugi Dataset', 'Lorem ipsum dolor sir amet', ['jabuka', 'kruska', 'jagoda']), - new Dataset('Dataset III', 'Kratak opis izvora podataka', ['c1', 'c2', 'c3', 'c4', 'c5']), - new Dataset('Dataset III', 'Kratak opis izvora podataka', ['c1', 'c2', 'c3', 'c4', 'c5']) - ] - this.publicPredictors = [ - new Predictor('Preživeli', 'Za uneto ime osobe, predvidja da li je ta osoba preživela ili ne.', ['Ime'], 'OsobaJePreživela'), - new Predictor('Drugi model', 'Lorem ipsum dolor sir amet', ['kruska'], 'jagoda'), - new Predictor('Treći model', 'Kratak opis modela', ['c1', 'c2', 'c3'], 'c5'), - new Predictor('Drugi model', 'Lorem ipsum dolor sir amet', ['kruska'], 'jagoda'), - new Predictor('Treći model', 'Kratak opis modela', ['c1', 'c2', 'c3'], 'c5'), - new Predictor('Drugi model', 'Lorem ipsum dolor sir amet', ['kruska'], 'jagoda'), - new Predictor('Treći model', 'Kratak opis modela', ['c1', 'c2', 'c3'], 'c5'), - new Predictor('Treći model', 'Kratak opis modela', ['c1', 'c2', 'c3'], 'c5') - ] + constructor(private datasetsService: DatasetsService, private predictorsService: PredictorsService) { + this.datasetsService.getPublicDatasets().subscribe((datasets) => { + this.publicDatasets = datasets; + console.log(datasets); + }); + this.predictorsService.getPublicPredictors().subscribe((predictors) => { + this.publicPredictors = predictors; + }); } ngOnInit(): void { 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 870e0ddb..b6926771 100644 --- a/frontend/src/app/_pages/my-models/my-models.component.html +++ b/frontend/src/app/_pages/my-models/my-models.component.html @@ -9,7 +9,9 @@ <div class="col-sm-4" style="margin-bottom: 10px;" *ngFor="let model of myModels"> <app-item-model [model]="model"></app-item-model> <div style="width: 25%; margin: auto;"> - <button (click)="deleteThisModel(model)" style="margin-top: 3px; width: 100%;">Obriši</button> + <button mat-raised-button color="primary" (click)="deleteThisModel(model)" style="margin-top: 3px; width: 100%;">Obriši</button> + + <button mat-raised-button color="primary" (click)="deleteThisModel(model)" style="margin-top: 3px; width: 100%;">Koristi</button> </div> </div> </div> diff --git a/frontend/src/app/_services/datasets.service.ts b/frontend/src/app/_services/datasets.service.ts index 35ca24e5..0ff63828 100644 --- a/frontend/src/app/_services/datasets.service.ts +++ b/frontend/src/app/_services/datasets.service.ts @@ -16,7 +16,11 @@ export class DatasetsService { return this.http.get<Dataset[]>(`${API_SETTINGS.apiURL}/dataset/publicdatasets`, { headers: this.authService.authHeader() }); } - addDataset(dataset: Dataset): any { + getMyDatasets(): Observable<Dataset[]> { + return this.http.get<Dataset[]>(`${API_SETTINGS.apiURL}/dataset/mydatasets`, { headers: this.authService.authHeader() }); + } + + addDataset(dataset: Dataset): Observable<any> { return this.http.post(`${API_SETTINGS.apiURL}/dataset/add`, dataset, { headers: this.authService.authHeader() }); } diff --git a/frontend/src/app/_services/home.service.spec.ts b/frontend/src/app/_services/home.service.spec.ts new file mode 100644 index 00000000..1afaf229 --- /dev/null +++ b/frontend/src/app/_services/home.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { HomeService } from './home.service'; + +describe('HomeService', () => { + let service: HomeService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(HomeService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_services/home.service.ts b/frontend/src/app/_services/home.service.ts new file mode 100644 index 00000000..0026413a --- /dev/null +++ b/frontend/src/app/_services/home.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class HomeService { + + constructor() { } +} diff --git a/frontend/src/app/_services/models.service.ts b/frontend/src/app/_services/models.service.ts index 58ddb2e6..3fbad109 100644 --- a/frontend/src/app/_services/models.service.ts +++ b/frontend/src/app/_services/models.service.ts @@ -1,10 +1,10 @@ -import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import Model from '../_data/Model'; import { AuthService } from './auth.service'; import { API_SETTINGS } from 'src/config'; -import Dataset from '../_data/Dataset'; import { Observable } from 'rxjs'; +import Dataset from '../_data/Dataset'; @Injectable({ @@ -35,25 +35,23 @@ export class ModelsService { addDataset(dataset: Dataset): Observable<any> { return this.http.post(`${API_SETTINGS.apiURL}/dataset/add`, dataset, { headers: this.authService.authHeader() }); } - trainModel(modelId: string): Observable<any> { - return this.http.post(`${API_SETTINGS.apiURL}/model/train`, modelId, { headers: this.authService.authHeader() }); + trainModel(model: Model): Observable<any> { + return this.http.post(`${API_SETTINGS.apiURL}/model/sendmodel`, model, { headers: this.authService.authHeader(), responseType: 'text' }); } getMyDatasets(): Observable<Dataset[]> { return this.http.get<Dataset[]>(`${API_SETTINGS.apiURL}/dataset/mydatasets`, { headers: this.authService.authHeader() }); } - + getMyModels(): Observable<Model[]> { return this.http.get<Model[]>(`${API_SETTINGS.apiURL}/model/mymodels`, { headers: this.authService.authHeader() }); } - editModel(model:Model) : Observable<Model> - { + editModel(model: Model): Observable<Model> { return this.http.put<Model>(`${API_SETTINGS.apiURL}/model/`, model, { headers: this.authService.authHeader() }); } - deleteModel(model:Model) : Observable<any> - { - return this.http.delete(`${API_SETTINGS.apiURL}/model/`+model.name, { headers: this.authService.authHeader() }); + deleteModel(model: Model) { + return this.http.delete(`${API_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 0cd7f0f6..a2dc012f 100644 --- a/frontend/src/app/_services/predictors.service.ts +++ b/frontend/src/app/_services/predictors.service.ts @@ -15,7 +15,7 @@ export class PredictorsService { constructor(private http: HttpClient, private authService: AuthService) { } getPublicPredictors(): Observable<Predictor[]> { - return this.http.get<Predictor[]>(`${API_SETTINGS.apiURL}/Predictor/publicpredictors`, { headers: this.authService.authHeader() }); + return this.http.get<Predictor[]>(`${API_SETTINGS.apiURL}/predictor/publicpredictors`, { headers: this.authService.authHeader() }); } } diff --git a/frontend/src/app/_services/web-socket.service.ts b/frontend/src/app/_services/web-socket.service.ts index 890ada6b..1a7efa87 100644 --- a/frontend/src/app/_services/web-socket.service.ts +++ b/frontend/src/app/_services/web-socket.service.ts @@ -13,15 +13,15 @@ export class WebSocketService { constructor() { this.ws = new WebsocketBuilder(API_SETTINGS.apiWSUrl) - .withBackoff(new ConstantBackoff(30000)) - .onOpen((i, e) => { console.log('WS: Connected to ' + API_SETTINGS.apiWSUrl) }) + .withBackoff(new ConstantBackoff(120000)) + .onOpen((i, e) => { /*console.log('WS: Connected to ' + API_SETTINGS.apiWSUrl)*/ }) .onMessage((i, e) => { console.log('WS MESSAGE: ', e.data); this.handlers.forEach(handler => { handler(e.data); }) }) - .onClose((i, e) => { console.log('WS: Connection closed!') }) + .onClose((i, e) => { /*console.log('WS: Connection closed!')*/ }) .build(); } diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index f44a6d00..f0e563f4 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,3 +1,4 @@ +<app-reactive-background></app-reactive-background> <app-navbar></app-navbar> <div class="container h-100"> <router-outlet></router-outlet> diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 0531a958..5d7af9d2 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -36,6 +36,7 @@ import { BarchartComponent } from './barchart/barchart.component'; import { NotificationsComponent } from './_elements/notifications/notifications.component'; import { DatatableComponent } from './_elements/datatable/datatable.component'; import { FilterDatasetsComponent } from './_pages/filter-datasets/filter-datasets.component'; +import { ReactiveBackgroundComponent } from './_elements/reactive-background/reactive-background.component'; import { ItemModelComponent } from './_elements/item-model/item-model.component'; @NgModule({ @@ -63,6 +64,7 @@ import { ItemModelComponent } from './_elements/item-model/item-model.component' NotificationsComponent, DatatableComponent, FilterDatasetsComponent, + ReactiveBackgroundComponent, ItemModelComponent ], imports: [ diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 5a30802b..9e2bbc54 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -1,4 +1,5 @@ @import '~bootstrap/dist/css/bootstrap.min.css'; body { - background-image: url('/assets/images/add_model_background.jpg'); + /*background-image: url('/assets/images/add_model_background.jpg');*/ + background-color: cornflowerblue; }
\ No newline at end of file |