diff options
Diffstat (limited to 'frontend/src/app')
58 files changed, 1485 insertions, 1239 deletions
diff --git a/frontend/src/app/Shared.ts b/frontend/src/app/Shared.ts index 31afb1a6..86e26687 100644 --- a/frontend/src/app/Shared.ts +++ b/frontend/src/app/Shared.ts @@ -1,9 +1,32 @@ +import { ElementRef } from "@angular/core"; +import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { AlertDialogComponent } from './_modals/alert-dialog/alert-dialog.component'; + class Shared { constructor( public loggedIn: boolean, public username: string = '', - public photoId: string = '1' + public photoId: string = '1', + public dialog?: MatDialog + //public alertDialog?: ElementRef ) { } + + + openDialog(title: string, message: string): void { + console.log("USAO U OPEN DIALOG 1"); + + if (this.dialog) { + console.log("USAO U OPEN DIALOG 2"); + const dialogRef = this.dialog.open(AlertDialogComponent, { + //width: '250px', + data: { title: title, message: message } + }); + dialogRef.afterClosed().subscribe(res => { + //nesto + }); + } + } } export default new Shared(false);
\ No newline at end of file diff --git a/frontend/src/app/_data/Dataset.ts b/frontend/src/app/_data/Dataset.ts index c5b56957..dd751947 100644 --- a/frontend/src/app/_data/Dataset.ts +++ b/frontend/src/app/_data/Dataset.ts @@ -12,6 +12,22 @@ export default class Dataset { public lastUpdated: Date = new Date(), public username: string = '', public delimiter: string = '', - public hasHeader: boolean = true + public hasHeader: boolean = true, + + public columnInfo: ColumnInfo[] = [], + public preview: string[][] = [[]] + ) { } +} + +export class ColumnInfo { + constructor( + public name: string = '', + public isNumber: boolean = false, + public numNull: number = 0, + public uniqueValues?: string[], + public median?: number, + public mean?: number, + public min?: number, + public max?: number ) { } }
\ No newline at end of file diff --git a/frontend/src/app/_data/Experiment.ts b/frontend/src/app/_data/Experiment.ts new file mode 100644 index 00000000..10320ab6 --- /dev/null +++ b/frontend/src/app/_data/Experiment.ts @@ -0,0 +1,34 @@ +export default class Experiment { + _id: string = ''; + constructor( + public name: string = 'Novi experiment', + public description: string = '', + public datasetId: string = '', + public inputColumns: string[] = [], + public columnToPredict: string = '', + public nullValues: NullValueOptions = NullValueOptions.DeleteRows, + public nullValuesReplacers: NullValReplacer[] = [], + public dateCreated: Date = new Date(), + public lastUpdated: Date = new Date() + ) { } +} + +export enum NullValueOptions { + DeleteRows = 'delete_rows', + DeleteColumns = 'delete_columns', + Replace = 'replace' +} + +export enum ReplaceWith { + None = 'Popuni...', + Mean = 'Srednja vrednost', + Median = 'Medijana', + Min = 'Minimum', + Max = 'Maksimum' +} + +export class NullValReplacer { + "column": string; + "option": NullValueOptions; + "value": string; +}
\ No newline at end of file diff --git a/frontend/src/app/_data/Model.ts b/frontend/src/app/_data/Model.ts index d9ba7885..9ea437b1 100644 --- a/frontend/src/app/_data/Model.ts +++ b/frontend/src/app/_data/Model.ts @@ -7,11 +7,9 @@ export default class Model { public description: string = '', public dateCreated: Date = new Date(), public lastUpdated: Date = new Date(), - public datasetId: string = '', + public experimentId: string = '', // Test set settings - public inputColumns: string[] = [], - public columnToPredict: string = '', public randomOrder: boolean = true, public randomTestSet: boolean = true, public randomTestSetDistribution: number = 0.1, //0.1-0.9 (10% - 90%) JESTE OVDE ZAKUCANO 10, AL POSLATO JE KAO 0.1 BACK-U @@ -26,11 +24,8 @@ export default class Model { public hiddenLayers: number = 1, public batchSize: number = 5, public hiddenLayerActivationFunctions: string[] = ['sigmoid'], - //public inputLayerActivationFunction: ActivationFunction = ActivationFunction.Sigmoid, public outputLayerActivationFunction: ActivationFunction = ActivationFunction.Sigmoid, public username: string = '', - public nullValues: NullValueOptions = NullValueOptions.DeleteRows, - public nullValuesReplacers: NullValReplacer[] = [], public metrics: string[] = [], // TODO add to add-model form public epochs: number = 5 // TODO add to add-model form ) { } @@ -85,7 +80,6 @@ export enum ActivationFunction { Linear = 'linear', //Sigmoid='sigmoid', Softmax = 'softmax', - } /* export enum ActivationFunctionHiddenLayer @@ -117,7 +111,6 @@ export enum LossFunction { MeanSquaredError = 'mean_squared_error', MeanSquaredLogarithmicError = 'mean_squared_logarithmic_error', HuberLoss = 'Huber' - } export enum LossFunctionRegression { MeanAbsoluteError = 'mean_absolute_error', @@ -168,31 +161,28 @@ export enum Metrics { MSE = 'mse', MAE = 'mae', RMSE = 'rmse' - + } -export enum MetricsRegression -{ +export enum MetricsRegression { Mse = 'mse', Mae = 'mae', Mape = 'mape', - Msle='msle', - CosineProximity='cosine' + Msle = 'msle', + CosineProximity = 'cosine' } -export enum MetricsBinaryClassification -{ - Accuracy='binary_accuracy', - Auc="AUC", - Precision='precision_score', - Recall='recall_score', - F1='f1_score', - +export enum MetricsBinaryClassification { + Accuracy = 'binary_accuracy', + Auc = "AUC", + Precision = 'precision_score', + Recall = 'recall_score', + F1 = 'f1_score', + } -export enum MetricsMultiClassification -{ - Accuracy='categorical_accuracy', - Auc="AUC", - Precision='precision_score', - Recall='recall_score', - F1='f1_score', +export enum MetricsMultiClassification { + Accuracy = 'categorical_accuracy', + Auc = "AUC", + Precision = 'precision_score', + Recall = 'recall_score', + F1 = 'f1_score', } diff --git a/frontend/src/app/_data/Notification.ts b/frontend/src/app/_data/Notification.ts new file mode 100644 index 00000000..181bb332 --- /dev/null +++ b/frontend/src/app/_data/Notification.ts @@ -0,0 +1,8 @@ +export default class Notification { + _id: string = ''; + constructor( + public title: string = 'Treniranje u toku...', + public id: string = '042', + public progress: number = 0.5 + ) { } +}
\ No newline at end of file diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.css b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.css 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 new file mode 100644 index 00000000..dfeb4f62 --- /dev/null +++ b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.html @@ -0,0 +1,41 @@ +<div class="row mb-4"> + <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="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? + <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? + <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"> + + <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? + <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"> + </div> + </div>
\ No newline at end of file diff --git a/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.spec.ts b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.spec.ts new file mode 100644 index 00000000..a9ea25b4 --- /dev/null +++ b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddNewDatasetComponent } from './add-new-dataset.component'; + +describe('AddNewDatasetComponent', () => { + let component: AddNewDatasetComponent; + let fixture: ComponentFixture<AddNewDatasetComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AddNewDatasetComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AddNewDatasetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); 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 new file mode 100644 index 00000000..fceb53cf --- /dev/null +++ b/frontend/src/app/_elements/add-new-dataset/add-new-dataset.component.ts @@ -0,0 +1,78 @@ +import { Component, EventEmitter, Output, ViewChild } from '@angular/core'; +import { NgxCsvParser, NgxCSVParserError } from 'ngx-csv-parser'; +import Dataset from 'src/app/_data/Dataset'; + +@Component({ + selector: 'app-add-new-dataset', + templateUrl: './add-new-dataset.component.html', + styleUrls: ['./add-new-dataset.component.css'] +}) +export class AddNewDatasetComponent { + + @Output() loaded = new EventEmitter<string>(); + + delimiterOptions: Array<string> = [",", ";", "\t", "razmak", "|"]; //podrazumevano "," + + //hasHeader: boolean = true; + hasInput: boolean = false; + + csvRecords: any[] = []; + files: File[] = []; + rowsNumber: number = 0; + colsNumber: number = 0; + + dataset: Dataset; //dodaj ! potencijalno + + constructor(private ngxCsvParser: NgxCsvParser) { + this.dataset = new Dataset(); + } + + //@ViewChild('fileImportInput', { static: false }) fileImportInput: any; cemu je ovo sluzilo? + + 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.hasInput = false; + return; + } + else + this.hasInput = true; + + this.update(); + } + + update() { + + if (this.files.length < 1) + return; + + this.ngxCsvParser.parse(this.files[0], { header: false, delimiter: (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter }) + .pipe().subscribe((result) => { + + console.log('Result', result); + if (result.constructor === Array) { + this.csvRecords = result; + if (this.dataset.hasHeader) + this.rowsNumber = this.csvRecords.length - 1; + else + this.rowsNumber = this.csvRecords.length; + this.colsNumber = this.csvRecords[0].length; + + if (this.dataset.hasHeader) //kasnije dodati opciju kada nema header da korisnik rucno unosi header-e + this.dataset.header = this.csvRecords[0]; + + this.loaded.emit("loaded"); + } + }, (error: NgxCSVParserError) => { + console.log('Error', error); + }); + } + + checkAccessible() { + if (this.dataset.isPublic) + this.dataset.accessibleByLink = true; + } + +} diff --git a/frontend/src/app/_elements/annvisual/annvisual.component.ts b/frontend/src/app/_elements/annvisual/annvisual.component.ts index 53e4e2ca..df0a3898 100644 --- a/frontend/src/app/_elements/annvisual/annvisual.component.ts +++ b/frontend/src/app/_elements/annvisual/annvisual.component.ts @@ -18,7 +18,7 @@ export class AnnvisualComponent implements OnInit { let hiddenlayerstring: string = ''; let digraphstring: string = 'digraph {'; - for (let i = 0; i < this.model.inputColumns.length; i++) { + for (let i = 0; i < /*this.model.inputColumns.length*/ 10; i++) { inputlayerstring = inputlayerstring + 'i' + i + ','; } inputlayerstring = inputlayerstring.slice(0, -1); @@ -35,7 +35,6 @@ export class AnnvisualComponent implements OnInit { } digraphstring = digraphstring + 'o}'; - graphviz('#graph').renderDot(digraphstring); } diff --git a/frontend/src/app/_elements/dataset-load/dataset-load.component.css b/frontend/src/app/_elements/dataset-load/dataset-load.component.css index 05819702..54e0738e 100644 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.css +++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.css @@ -1,6 +1,13 @@ -#divInputs { - margin-left: 20px; +.btnType1 { + background-color: #003459; + color: white; } -#divOutputs { - margin-left: 20px; +.btnType2 { + background-color: white; + color: #003459; + border-color: #003459; +} +.selectedDatasetClass { + /*border-color: 2px solid #003459;*/ + background-color: lightblue; }
\ No newline at end of file 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 76e46092..674e5990 100644 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.html +++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.html @@ -1,44 +1,42 @@ <div> - <div class="row mb-4"> - <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="desc" class="col-sm-2 col-form-label">Opis:</label> - <div> - <textarea class="form-control" name="desc" rows="3" [(ngModel)]="dataset.description"></textarea> - </div> + <!--Sklonjeno ucitavanje novog dataseta i sve opcije u vezi sa tim, premesteno u add-new-dataset--> - <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? - <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="dataset.accessibleByLink" type="checkbox" - value="" id="checkboxAccessibleByLink"> - </label> - </div> + <div class="col-12 d-flex my-5"> + <h2 class="">Izvor podataka:</h2> <div class="col-1"> </div> - <div class="col-4 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> - <option *ngFor="let option of delimiterOptions">{{option}}</option> - </datalist> + <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}"> + 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|filter:term" + [ngClass]="{'selectedDatasetClass': this.selectedDataset == dataset}"> + <app-item-dataset name="usersDataset" [dataset]="dataset" + (click)="selectThisDataset(dataset);"></app-item-dataset> + </li> + </ul> + </div> + </div> - <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"> - </div> + <app-add-new-dataset [style]="(showMyDatasets)?'display:none;visibility:hidden;':''" id="dataset" + (loaded)="datasetLoaded = true; selectedDataset = addNewDatasetComponent?.dataset; datasetFile = addNewDatasetComponent?.csvRecords; datasetHasHeader = addNewDatasetComponent?.dataset!.hasHeader"> + </app-add-new-dataset> + <div class="px-5 mt-5"> + <app-datatable [data]="datasetFile" [hasHeader]="datasetHasHeader"></app-datatable> </div> + </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 f9343117..ed71dc3c 100644 --- a/frontend/src/app/_elements/dataset-load/dataset-load.component.ts +++ b/frontend/src/app/_elements/dataset-load/dataset-load.component.ts @@ -1,6 +1,12 @@ -import { Component, EventEmitter, Output, ViewChild } from '@angular/core'; -import { NgxCsvParser, NgxCSVParserError } from 'ngx-csv-parser'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { AddNewDatasetComponent } from '../add-new-dataset/add-new-dataset.component'; +import { ModelsService } from 'src/app/_services/models.service'; +import shared from 'src/app/Shared'; import Dataset from 'src/app/_data/Dataset'; +import { DatatableComponent } from 'src/app/_elements/datatable/datatable.component'; +import { DatasetsService } from 'src/app/_services/datasets.service'; +import { CsvParseService } from 'src/app/_services/csv-parse.service'; +import { Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-dataset-load', @@ -9,70 +15,77 @@ import Dataset from 'src/app/_data/Dataset'; }) export class DatasetLoadComponent { - @Output() loaded = new EventEmitter<string>(); + @Output() selectedDatasetChangeEvent = new EventEmitter<Dataset>(); - delimiterOptions: Array<string> = [",", ";", "\t", "razmak", "|"]; //podrazumevano "," + @ViewChild(AddNewDatasetComponent) addNewDatasetComponent?: AddNewDatasetComponent; + @ViewChild(AddNewDatasetComponent) datatable?: DatatableComponent; + datasetLoaded: boolean = false; + selectedDatasetLoaded: boolean = false; - //hasHeader: boolean = true; - hasInput: boolean = false; + showMyDatasets: boolean = true; + myDatasets?: Dataset[]; + existingDatasetSelected: boolean = false; + selectedDataset?: Dataset; + otherDataset?: Dataset; + otherDatasetFile?: any[]; + datasetFile?: any[]; + datasetHasHeader?: boolean = true; - csvRecords: any[] = []; - files: File[] = []; - rowsNumber: number = 0; - colsNumber: number = 0; + term: string = ""; - dataset: Dataset; //dodaj ! potencijalno - - constructor(private ngxCsvParser: NgxCsvParser) { - this.dataset = new Dataset(); + constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService) { + this.datasets.getMyDatasets().subscribe((datasets) => { + this.myDatasets = datasets; + }); } - @ViewChild('fileImportInput', { static: false }) fileImportInput: any; - - 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.hasInput = false; - return; - } - else - this.hasInput = true; - - this.update(); + viewMyDatasetsForm() { + this.showMyDatasets = true; + this.resetSelectedDataset(); + //this.resetCbsAndRbs(); //TREBA DA SE DESI + } + viewNewDatasetForm() { + this.showMyDatasets = false; + this.resetSelectedDataset(); + //this.resetCbsAndRbs(); //TREBA DA SE DESI } - update() { - - if (this.files.length < 1) - return; - - this.ngxCsvParser.parse(this.files[0], { header: false, delimiter: (this.dataset.delimiter == "razmak") ? " " : (this.dataset.delimiter == "") ? "," : this.dataset.delimiter }) - .pipe().subscribe((result) => { - - console.log('Result', result); - if (result.constructor === Array) { - this.csvRecords = result; - if (this.dataset.hasHeader) - this.rowsNumber = this.csvRecords.length - 1; + selectThisDataset(dataset: Dataset) { + this.selectedDataset = dataset; + this.selectedDatasetLoaded = false; + this.existingDatasetSelected = true; + this.datasetHasHeader = this.selectedDataset.hasHeader; + + this.datasets.getDatasetFile(dataset.fileId).subscribe((file: string | undefined) => { + if (file) { + this.datasetFile = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter == "") ? "," : dataset.delimiter); + /*for (let i = this.datasetFile.length - 1; i >= 0; i--) { //moguce da je vise redova na kraju fajla prazno i sl. + if (this.datasetFile[i].length != this.datasetFile[0].length) + this.datasetFile[i].pop(); else - this.rowsNumber = this.csvRecords.length; - this.colsNumber = this.csvRecords[0].length; + break; //nema potrebe dalje + }*/ + //console.log(this.datasetFile); + //this.resetCbsAndRbs(); //TREBA DA SE DESI + //this.refreshThreeNullValueRadioOptions(); //TREBA DA SE DESI + this.selectedDatasetLoaded = true; + //this.scrollToNextForm(); + } + }); + } - if (this.dataset.hasHeader) //kasnije dodati opciju kada nema header da korisnik rucno unosi header-e - this.dataset.header = this.csvRecords[0]; + resetSelectedDataset(): boolean { + const temp = this.selectedDataset; + this.selectedDataset = this.otherDataset; + this.otherDataset = temp; + const tempFile = this.datasetFile; + this.datasetFile = this.otherDatasetFile; + this.otherDatasetFile = tempFile; - this.loaded.emit("loaded"); - } - }, (error: NgxCSVParserError) => { - console.log('Error', error); - }); - } + this.selectedDatasetChangeEvent.emit(this.selectedDataset); - checkAccessible() { - if (this.dataset.isPublic) - this.dataset.accessibleByLink = true; + return true; } + } diff --git a/frontend/src/app/_elements/datatable/datatable.component.html b/frontend/src/app/_elements/datatable/datatable.component.html index bd9e7a13..8fcd44ac 100644 --- a/frontend/src/app/_elements/datatable/datatable.component.html +++ b/frontend/src/app/_elements/datatable/datatable.component.html @@ -1,25 +1,31 @@ <div *ngIf="data"> - <div class="table-responsive" style="max-height: 50vh; max-width: 100%; overflow: scroll;"> - <table *ngIf="hasHeader" class="table table-bordered table-light"> - <thead> - <tr> - <th *ngFor="let item of data[0]; let i = index">{{item}}</th> - </tr> - </thead> - <tbody> - <tr *ngFor="let row of data | slice:1"> - <td *ngFor="let col of row">{{col}}</td> - </tr> - </tbody> - </table> + <div class="table-responsive" style="height: 34rem; overflow: auto; border-radius: 5px;" class="mh-5"> + <div *ngIf="!loaded" style="background-color: #003459; width: 100%; height: 100%;" + class="d-flex justify-content-center align-items-center"> + <app-loading></app-loading> + </div> + <div *ngIf="loaded"> + <table *ngIf="hasHeader" class="table table-bordered table-light"> + <thead> + <tr> + <th *ngFor="let item of data[0]; let i = index">{{item}}</th> + </tr> + </thead> + <tbody> + <tr *ngFor="let row of data | slice:1"> + <td *ngFor="let col of row">{{col}}</td> + </tr> + </tbody> + </table> - <table *ngIf="data.length > 0 && !hasHeader" class="table table-bordered table-light"> - <tbody> - <tr *ngFor="let row of data"> - <td *ngFor="let col of row">{{col}}</td> - </tr> - </tbody> - </table> + <table *ngIf="data.length > 0 && !hasHeader" class="table table-bordered table-light"> + <tbody> + <tr *ngFor="let row of data"> + <td *ngFor="let col of row">{{col}}</td> + </tr> + </tbody> + </table> + </div> </div> <div id="info"> diff --git a/frontend/src/app/_elements/datatable/datatable.component.ts b/frontend/src/app/_elements/datatable/datatable.component.ts index d3740d83..3343f6f0 100644 --- a/frontend/src/app/_elements/datatable/datatable.component.ts +++ b/frontend/src/app/_elements/datatable/datatable.component.ts @@ -11,6 +11,8 @@ export class DatatableComponent implements OnInit { @Input() data?: any[] = []; + loaded = false; + constructor() { } ngOnInit(): void { diff --git a/frontend/src/app/_elements/item-predictor/item-predictor.component.ts b/frontend/src/app/_elements/item-predictor/item-predictor.component.ts index 41d2ab9c..246032e0 100644 --- a/frontend/src/app/_elements/item-predictor/item-predictor.component.ts +++ b/frontend/src/app/_elements/item-predictor/item-predictor.component.ts @@ -17,8 +17,7 @@ export class ItemPredictorComponent implements OnInit { } openPredictor() { - this.router.navigate(['predict/'+ '6245a5958a9c76c1bf7ff9b6']); - //this.router.navigate(['predict'+this.predictor._id]); + this.router.navigate(['predict/'+ this.predictor._id]); } } diff --git a/frontend/src/app/_elements/loading/loading.component.css b/frontend/src/app/_elements/loading/loading.component.css new file mode 100644 index 00000000..f39f60ee --- /dev/null +++ b/frontend/src/app/_elements/loading/loading.component.css @@ -0,0 +1,81 @@ +.lds-grid { + display: inline-block; + position: relative; + width: 80px; + height: 80px; +} + +.lds-grid div { + position: absolute; + width: 16px; + height: 16px; + border-radius: 50%; + background: #fff; + animation: lds-grid 1.2s linear infinite; +} + +.lds-grid div:nth-child(1) { + top: 8px; + left: 8px; + animation-delay: 0s; +} + +.lds-grid div:nth-child(2) { + top: 8px; + left: 32px; + animation-delay: -0.4s; +} + +.lds-grid div:nth-child(3) { + top: 8px; + left: 56px; + animation-delay: -0.8s; +} + +.lds-grid div:nth-child(4) { + top: 32px; + left: 8px; + animation-delay: -0.4s; +} + +.lds-grid div:nth-child(5) { + top: 32px; + left: 32px; + animation-delay: -0.8s; +} + +.lds-grid div:nth-child(6) { + top: 32px; + left: 56px; + animation-delay: -1.2s; +} + +.lds-grid div:nth-child(7) { + top: 56px; + left: 8px; + animation-delay: -0.8s; +} + +.lds-grid div:nth-child(8) { + top: 56px; + left: 32px; + animation-delay: -1.2s; +} + +.lds-grid div:nth-child(9) { + top: 56px; + left: 56px; + animation-delay: -1.6s; +} + +@keyframes lds-grid { + + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.5; + } +}
\ No newline at end of file diff --git a/frontend/src/app/_elements/loading/loading.component.html b/frontend/src/app/_elements/loading/loading.component.html new file mode 100644 index 00000000..d2c7b74e --- /dev/null +++ b/frontend/src/app/_elements/loading/loading.component.html @@ -0,0 +1,11 @@ +<div class="lds-grid"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_pages/add-model/add-model.component.spec.ts b/frontend/src/app/_elements/loading/loading.component.spec.ts index 2926e1c4..7aacfad9 100644 --- a/frontend/src/app/_pages/add-model/add-model.component.spec.ts +++ b/frontend/src/app/_elements/loading/loading.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AddModelComponent } from './add-model.component'; +import { LoadingComponent } from './loading.component'; -describe('AddModelComponent', () => { - let component: AddModelComponent; - let fixture: ComponentFixture<AddModelComponent>; +describe('LoadingComponent', () => { + let component: LoadingComponent; + let fixture: ComponentFixture<LoadingComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ AddModelComponent ] + declarations: [ LoadingComponent ] }) .compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(AddModelComponent); + fixture = TestBed.createComponent(LoadingComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/frontend/src/app/_elements/loading/loading.component.ts b/frontend/src/app/_elements/loading/loading.component.ts new file mode 100644 index 00000000..4dff0cdf --- /dev/null +++ b/frontend/src/app/_elements/loading/loading.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-loading', + templateUrl: './loading.component.html', + styleUrls: ['./loading.component.css'] +}) +export class LoadingComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/frontend/src/app/_elements/model-load/model-load.component.css b/frontend/src/app/_elements/model-load/model-load.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_elements/model-load/model-load.component.css diff --git a/frontend/src/app/_elements/model-load/model-load.component.html b/frontend/src/app/_elements/model-load/model-load.component.html new file mode 100644 index 00000000..0c6735a9 --- /dev/null +++ b/frontend/src/app/_elements/model-load/model-load.component.html @@ -0,0 +1,221 @@ +<div> + <div class="form-group row mt-3 mb-2 d-flex justify-content-center"> + <h2 class="col-2"> Nov model: </h2> + <div class="col-3"> + <label for="name" class="col-form-label">Naziv modela:</label> + <input type="text" class="form-control" name="name" placeholder="Naziv..." [(ngModel)]="newModel.name"> + </div> + <div class="col-5"> + <label for="desc" class="col-sm-2 col-form-label">Opis:</label> + <div> + <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> + <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">Parametri treniranja:</h2> + <div> + <div class="row p-2"> + <div class="col-1"> + </div> + <div class="col-3"> + <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()"> + <option + *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + <div class="col-1"> + </div> + <div class="col-3"> + <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])"> + </div> + </div> + + <div class="row p-2"> + <div class="col-1"> + </div> + <div class="col-3"> + <label for="encoding" class="col-form-label">Enkoding: </label> + </div> + <div class="col-2"> + <select id=encodingOptions class="form-control" name="encoding" [(ngModel)]="newModel.encoding"> + <option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + + <div class="col-1"> + </div> + <div class="col-3"> + <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"> + </div> + </div> + + <div class="row p-2"> + <div class="col-1"> + </div> + <div class="col-3"> + <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"> + <option *ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + <div class="col-1"> + </div> + <div class="col-3"> + <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label> + </div> + <div class="col-1"> + <input type="number" min="1" class="form-control" name="batchSize" [(ngModel)]="newModel.batchSize"> + </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> + </div> + <div class="col-2"> + <select id=lossFunctionOptions class="form-control" name="lossFunction" + [(ngModel)]="newModel.lossFunction" aria-checked="true"> + <option + *ngFor="let option of Object.keys(lossFunction); let optionName of Object.values(lossFunction)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + <div class="col-1"></div> + <div class="col-3 mt-2"> + <label for="type" class="form-check-label">Nasumičan redosled podataka?</label> + <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="newModel.randomOrder" type="checkbox" + value="" checked> + </div> + </div> + <div class="border m-3"> + <div class="row p-2 m-2"> + <div class="col-4"> + <label for="splitYesNo" class="form-check-label"> + <h3>Podela test skupa: + <input id="splitYesNo" class="form-check-input" type="checkbox" + [checked]="newModel.randomTestSet" + (change)="newModel.randomTestSet = !newModel.randomTestSet"> + </h3> + </label> + </div> + <div class="col-8"> + trening + <mat-slider style="width: 85%;" min="10" max="90" step="10" value="10" + name="randomTestSetDistribution" thumbLabel [disabled]="!newModel.randomTestSet" + [(ngModel)]="tempTestSetDistribution"> + </mat-slider> + test + </div> + </div> + + <div class="row p-2 mx-2"> + <div class="col-4"> + <label for="percentage" class="form-label">Procenat podataka koji se uzima za trening + skup:</label> + </div> + <div class="col-2"> + <input id="percentage" type="number" class="form-control" min="10" max="90" step="10" value="90" + [(ngModel)]="tempTestSetDistribution" [disabled]="!newModel.randomTestSet"> + </div> + </div> + </div> + <h3>Aktivacione funkcije:</h3> + + <div class="row p-2 m-2" style="align-self: center;"> + <div class="col-3"> + <label for="hiddenLayerActivationFunction" class="col-form-label" style="text-align: center;">Funkcija + aktivacije skrivenih + slojeva:</label> + </div> + <div class="col-3"> + <div *ngFor="let item of [].constructor(newModel.hiddenLayers); let i = index"> + <div class="input-group mb-2"> + <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]"> + <option + *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + </div> + </div> + <div class="col-3"> + <label for="outputLayerActivationFunction" class="col-form-label" style="text-align: center;">Funkcija + aktivacije izlaznog + sloja:</label> + </div> + <div class="col-3"> + <select id=outputLayerActivationFunctionOptions class="form-control" + name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction"> + <option + *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" + [value]="option"> + {{ optionName }} + </option> + </select> + </div> + <div class="col"> + </div> + </div> + </div> + <br><br> + <hr> + <div class="form-check form-check-inline overflow-auto " style="width: max-content;"> + <h3>Izaberite metrike:</h3> + <div id="divMetricsinput"> + + <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;"> + <label class="form-check-label" for="metrics_{{option}}" for="inlineCheckbox2"> + {{optionName}} + </label> + </div> + </div> + </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)="addModel();">Sačuvaj + model</button> + <div class="col"></div> + </div> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_elements/model-load/model-load.component.spec.ts b/frontend/src/app/_elements/model-load/model-load.component.spec.ts new file mode 100644 index 00000000..1dafd966 --- /dev/null +++ b/frontend/src/app/_elements/model-load/model-load.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModelLoadComponent } from './model-load.component'; + +describe('ModelLoadComponent', () => { + let component: ModelLoadComponent; + let fixture: ComponentFixture<ModelLoadComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ModelLoadComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ModelLoadComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_elements/model-load/model-load.component.ts b/frontend/src/app/_elements/model-load/model-load.component.ts new file mode 100644 index 00000000..1d38de68 --- /dev/null +++ b/frontend/src/app/_elements/model-load/model-load.component.ts @@ -0,0 +1,79 @@ +import { Component, OnInit } from '@angular/core'; +import Shared from 'src/app/Shared'; +import Model, { ActivationFunction, Encoding, LossFunction, LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, Metrics, MetricsBinaryClassification, MetricsMultiClassification, MetricsRegression, NullValueOptions, Optimizer, ProblemType } from 'src/app/_data/Model'; +import { ModelsService } from 'src/app/_services/models.service'; + +@Component({ + selector: 'app-model-load', + templateUrl: './model-load.component.html', + styleUrls: ['./model-load.component.css'] +}) +export class ModelLoadComponent implements OnInit { + + newModel: Model = new Model(); + + ProblemType = ProblemType; + Encoding = Encoding; + ActivationFunction = ActivationFunction; + metrics: any = Metrics; + LossFunction = LossFunction; + Optimizer = Optimizer; + Object = Object; + document = document; + shared = Shared; + + term: string = ""; + selectedProblemType: string = ''; + selectedMetrics = []; + tempTestSetDistribution = 90; + lossFunction: any = LossFunction; + + constructor(private models: ModelsService) { } + + ngOnInit(): void { + } + + getMetrics() { + this.newModel.metrics = []; + let cb = document.getElementsByName("cbmetrics"); + + for (let i = 0; i < cb.length; i++) { + let chb = <HTMLInputElement>cb[i]; + if (chb.checked == true) + this.newModel.metrics.push(chb.value); + } + } + + addModel() { + this.getMetrics(); + + this.newModel.randomTestSetDistribution = 1 - Math.round(this.tempTestSetDistribution / 100 * 10) / 10; + this.newModel.username = Shared.username; + + this.models.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 + }, (error) => { + Shared.openDialog('Greška', 'Model sa unetim nazivom već postoji u Vašoj kolekciji.\nPromenite naziv modela i nastavite sa kreiranim datasetom.'); + }); + } + + filterOptions() { + switch (this.newModel.type) { + case 'regresioni': + this.lossFunction = LossFunctionRegression; + this.metrics = MetricsRegression; + break; + case 'binarni-klasifikacioni': + this.lossFunction = LossFunctionBinaryClassification; + this.metrics = MetricsBinaryClassification; + break; + case 'multi-klasifikacioni': + this.lossFunction = LossFunctionMultiClassification; + this.metrics = MetricsMultiClassification; + break; + default: + break; + } + } +} diff --git a/frontend/src/app/_elements/navbar/navbar.component.html b/frontend/src/app/_elements/navbar/navbar.component.html index 82a1ea07..dbee4773 100644 --- a/frontend/src/app/_elements/navbar/navbar.component.html +++ b/frontend/src/app/_elements/navbar/navbar.component.html @@ -8,8 +8,9 @@ <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="add-model" class="nav-link px-2" - [class]="(currentUrl === '/add-model') ? 'text-secondary' : 'text-white'">Dodaj model</a></li> + <li><a routerLink="experiment" class="nav-link px-2" + [class]="(currentUrl === '/add-model') ? 'text-secondary' : 'text-white'">Eksperimentiši</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> @@ -18,7 +19,8 @@ <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"> + <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);" diff --git a/frontend/src/app/_elements/navbar/navbar.component.ts b/frontend/src/app/_elements/navbar/navbar.component.ts index 2e4bde91..368508ed 100644 --- a/frontend/src/app/_elements/navbar/navbar.component.ts +++ b/frontend/src/app/_elements/navbar/navbar.component.ts @@ -3,6 +3,7 @@ import { Location } from '@angular/common'; 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'; @Component({ selector: 'app-navbar', @@ -14,7 +15,8 @@ export class NavbarComponent implements OnInit { currentUrl: string; shared = shared; - constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService) { + constructor(public location: Location, private auth: AuthService, private userInfoService: UserInfoService, private matDialog: MatDialog) { + shared.dialog = matDialog; this.currentUrl = this.location.path(); this.location.onUrlChange(() => { this.currentUrl = this.location.path(); diff --git a/frontend/src/app/_elements/notifications/notifications.component.html b/frontend/src/app/_elements/notifications/notifications.component.html index d1da41b4..0b87e4fc 100644 --- a/frontend/src/app/_elements/notifications/notifications.component.html +++ b/frontend/src/app/_elements/notifications/notifications.component.html @@ -1,3 +1,20 @@ -<div *ngIf="notifications" class="position-fixed card card-body bg-dark text-white m-3" style="bottom: 0; right: 0;"> - <h3>Notifikacije</h3> -</div>
\ No newline at end of file +<div *ngIf="notifications" class="position-fixed card card-body p-1 m-3" style="bottom: 0; right: 0; width: 18rem;"> + <h2 class="m-auto" (click)="closed = !closed;" data-bs-toggle="collapse" href="#collapseNotifs" role="button" + aria-expanded="true" aria-controls="collapseNotifs">Notifikacije + <button class="border-0 bg-white"> + <mat-icon class="position-absolute" style="top: 8px; right: 12px;">{{closed ? 'keyboard_arrow_up' : + 'keyboard_arrow_down'}}</mat-icon> + </button> + </h2> + + <div id="collapseNotifs" class="collapse show"> + <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 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> + </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 6c1d555b..82613448 100644 --- a/frontend/src/app/_elements/notifications/notifications.component.ts +++ b/frontend/src/app/_elements/notifications/notifications.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { WebSocketService } from 'src/app/_services/web-socket.service'; +import Notification from 'src/app/_data/Notification'; @Component({ selector: 'app-notifications', @@ -8,9 +9,16 @@ import { WebSocketService } from 'src/app/_services/web-socket.service'; }) export class NotificationsComponent implements OnInit { - notifications?: any[]; + notifications?: Notification[]; + closed: boolean = false; - constructor(private wsService: WebSocketService) { } + constructor(private wsService: WebSocketService) { + this.notifications = [ + new Notification("Titanik (Preziveli)", "79768456867", 0.2), + new Notification("Test Prediktor 1", "56758768678", 0.4), + new Notification("Test Prediktor 2", "11344556425", 0.7) + ] + } ngOnInit(): void { // this.wsService.send('test'); diff --git a/frontend/src/app/_modals/alert-dialog/alert-dialog.component.css b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.css new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.css diff --git a/frontend/src/app/_modals/alert-dialog/alert-dialog.component.html b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.html new file mode 100644 index 00000000..82365193 --- /dev/null +++ b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.html @@ -0,0 +1,7 @@ +<h2 mat-dialog-title class="text-muted">{{data.title}}</h2> +<div mat-dialog-content class="mt-4" style="color: rgb(81, 76, 76);"> + {{data.message}} +</div> +<div mat-dialog-actions class="d-flex justify-content-center mt-4"> + <button mat-button cdkFocusInitial (click)="onOkClick()" style="background-color: lightgray;">OK</button> +</div>
\ No newline at end of file diff --git a/frontend/src/app/_modals/alert-dialog/alert-dialog.component.spec.ts b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.spec.ts new file mode 100644 index 00000000..a93fc493 --- /dev/null +++ b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AlertDialogComponent } from './alert-dialog.component'; + +describe('AlertDialogComponent', () => { + let component: AlertDialogComponent; + let fixture: ComponentFixture<AlertDialogComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AlertDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AlertDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/_modals/alert-dialog/alert-dialog.component.ts b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.ts new file mode 100644 index 00000000..e15f3c6f --- /dev/null +++ b/frontend/src/app/_modals/alert-dialog/alert-dialog.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core'; +import { Inject} from '@angular/core'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; + +interface DialogData { + title: string; + message: string; +} + +@Component({ + selector: 'app-alert-dialog', + templateUrl: './alert-dialog.component.html', + styleUrls: ['./alert-dialog.component.css'] +}) +export class AlertDialogComponent { + + constructor( + public dialogRef: MatDialogRef<AlertDialogComponent>, + @Inject(MAT_DIALOG_DATA) public data: DialogData, + //public dialog: MatDialog + ) {} + + onOkClick(): void { + this.dialogRef.close(); + } + + +} diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.html b/frontend/src/app/_modals/login-modal/login-modal.component.html index d7836848..03048155 100644 --- a/frontend/src/app/_modals/login-modal/login-modal.component.html +++ b/frontend/src/app/_modals/login-modal/login-modal.component.html @@ -3,7 +3,7 @@ <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-header" style="background-color: #003459;"> - <button id="closeButton" type="button" class="btn-close" style="background-color:white;" data-bs-dismiss="modal" aria-label="Close" (click)="resetData()"></button> + <button #closeButton type="button" class="btn-close" style="background-color:white;" data-bs-dismiss="modal" aria-label="Close" (click)="resetData()"></button> </div> <div class="modal-body px-5" style="color:#003459"> <h1 class="text-center mt-2 mb-4">Prijavite se</h1> diff --git a/frontend/src/app/_modals/login-modal/login-modal.component.ts b/frontend/src/app/_modals/login-modal/login-modal.component.ts index c86c269a..e1535a25 100644 --- a/frontend/src/app/_modals/login-modal/login-modal.component.ts +++ b/frontend/src/app/_modals/login-modal/login-modal.component.ts @@ -4,6 +4,7 @@ import { CookieService } from 'ngx-cookie-service'; import { AuthService } from 'src/app/_services/auth.service'; import { UserInfoService } from 'src/app/_services/user-info.service'; import shared from '../../Shared'; +import {AfterViewInit, ElementRef} from '@angular/core'; @Component({ selector: 'app-login-modal', @@ -12,6 +13,8 @@ import shared from '../../Shared'; }) export class LoginModalComponent implements OnInit { + @ViewChild('closeButton') closeButton?: ElementRef; + username: string = ''; password: string = ''; @@ -38,7 +41,7 @@ export class LoginModalComponent implements OnInit { } else { this.authService.authenticate(response); - (<HTMLSelectElement>document.getElementById('closeButton')).click(); + (<HTMLSelectElement>this.closeButton?.nativeElement).click(); this.userInfoService.getUserInfo().subscribe((response) => { shared.photoId = response.photoId; }); diff --git a/frontend/src/app/_modals/register-modal/register-modal.component.ts b/frontend/src/app/_modals/register-modal/register-modal.component.ts index 13ef7eba..05888589 100644 --- a/frontend/src/app/_modals/register-modal/register-modal.component.ts +++ b/frontend/src/app/_modals/register-modal/register-modal.component.ts @@ -1,6 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { AuthService } from 'src/app/_services/auth.service'; import User from 'src/app/_data/User'; +import { DOCUMENT } from '@angular/common'; +import { Inject } from '@angular/core'; +import shared from 'src/app/Shared'; @Component({ selector: 'app-register-modal', @@ -29,8 +32,11 @@ export class RegisterModalComponent implements OnInit { pattEmail: RegExp = /^[a-zA-Z0-9]+([\.\-\+][a-zA-Z0-9]+)*\@([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}$/; pattPassword: RegExp = /.{6,30}$/; + shared = shared; + constructor( - private authService: AuthService + private authService: AuthService, + @Inject(DOCUMENT) document: Document ) { } ngOnInit(): void { @@ -149,11 +155,11 @@ export class RegisterModalComponent implements OnInit { }, (error) => console.warn(error)); } else if (response == 'Email Already Exists') { - alert('Nalog sa unetim email-om već postoji!'); + shared.openDialog("Greška!", "Nalog sa unetim email-om već postoji!"); (<HTMLSelectElement>document.getElementById('email')).focus(); } else if (response == 'Username Already Exists') { - alert('Nalog sa unetim korisničkim imenom već postoji!'); + shared.openDialog("Greška!", "Nalog sa unetim korisničkim imenom već postoji!"); (<HTMLSelectElement>document.getElementById('username-register')).focus(); } } diff --git a/frontend/src/app/_pages/add-model/add-model.component.html b/frontend/src/app/_pages/add-model/add-model.component.html deleted file mode 100644 index 97b35b7a..00000000 --- a/frontend/src/app/_pages/add-model/add-model.component.html +++ /dev/null @@ -1,487 +0,0 @@ -<div id="header"> - <h1>Napravite svoj model veštačke neuronske mreže</h1> -</div> - -<div id="wrapper"> - <div id="container" class="container p-5" style="background-color: white; min-height: 100%;"> - <div class="form-group row mt-3 mb-2 d-flex justify-content-center"> - <!--justify-content-center--> - <h2 class="col-2"> Nov model: </h2> - <div class="col-3"> - <label for="name" class="col-form-label">Naziv modela:</label> - <input type="text" class="form-control" name="name" placeholder="Naziv..." [(ngModel)]="newModel.name"> - </div> - <div class="col-5"> - <label for="desc" class="col-sm-2 col-form-label">Opis:</label> - <div> - <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> - <input type="text" class="form-control-plaintext" id="dateCreated" placeholder="--/--/--" - value="{{newModel.dateCreated | date: 'dd/MM/yyyy'}}" readonly> - </div> - </div> - - <div class="py-3 pr-5 justify-content-center"> - - <div class="col-12 d-flex my-5"> - <h2 class="">Izvor podataka:</h2> - <div class="col-1"> - </div> - <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}"> - 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|filter:term" - [ngClass]="{'selectedDatasetClass': this.selectedDataset == dataset}"> - <app-item-dataset name="usersDataset" [dataset]="dataset" - (click)="selectThisDataset(dataset);"></app-item-dataset> - </li> - </ul> - </div> - </div> - - <app-dataset-load [style]="(showMyDatasets)?'display:none;visibility:hidden;':''" id="dataset" - (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> - </div> - </div> - - <!-- ULAZNE/IZLAZNE KOLONE --> - <span id="selectInAndOuts"></span> - <div - *ngIf="selectedDataset && ((showMyDatasets && selectedDatasetLoaded) || (!showMyDatasets && datasetLoaded))"> - <!--postignuto da se kod newdataseta ucita tabela pa ulazi/izlazi. ostalo srediti to kod mydatasets(dopuna 2. uslova)--> - <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.header; let i = index"> - <input class="form-check-input" type="checkbox" value="{{item}}" id="cb_{{item}}" - name="cbsNew" [checked]="this.selectedOutputColumnVal != item" - [disabled]="this.selectedOutputColumnVal == item"> - <label class="form-check-label" for="cb_{{item}}"> - {{item}} - </label> - </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.header; let i = index"> - <input class="form-check-input" type="radio" value="{{item}}" id="rb_{{item}}" name="rbsNew" - (change)="this.selectedOutputColumnVal = item"> - <label class="form-check-label" for="rb_{{item}}"> - {{item}} - </label> - </div> - </div> - </div> - - - <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" - 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</label><br> - <input type="radio" [(ngModel)]="newModel.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</label><br> - <input type="radio" [(ngModel)]="newModel.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 id="columnReplacers"> - <!--Ulazne kolone - popunjavanje null vrednosti --> - <div *ngFor="let column of selectedDataset.header; let i = index" class="my-3"> - <div *ngIf="getInputById('cb_'+column).checked" class=""> - <span class="w-20 mx-3"> - {{column}} <span class="small" - style="color:gray;">({{calculateSumOfNullValuesInCol(column)}} - null)</span> - </span> - - <label *ngIf="calculateSumOfNullValuesInCol(column)==0" - class="text-center form-control mx-3 text-secondary">Ova kolona nema - nedostajućih - vrednosti.</label> - - <div *ngIf="calculateSumOfNullValuesInCol(column)!=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" class="form-control"> - Zameni - <input type="radio" [id]="'fillCol_'+column" - [name]="'delOp_'+column"> - </label> - </div> - <input type="text" class="form-control" - [id]="'fillText_'+column" - (keyup)="checkFillColRadio(column)" - placeholder="Unesi vrednost..."> - - <div class="input-group-append"> - <select [id]="'replaceOptions'+i" - class="form-control btn-primary" - *ngIf="isNumber(datasetFile[1][i])" - (change)="replaceWithSelectedNumber($event);"> - <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="!isNumber(datasetFile[1][i]) && calculateSumOfNullValuesInCol(column)!=0" - (change)="replaceWithSelectedString($event);"> - <option - *ngFor="let option of arrayColumn(datasetFile, i)" - [value]="option"> - {{ option }} - </option> - </select> - </div> - </div> - </div> - - <div class="flex-shrink-1 mx-3"> - <div class="input-group"> - <label class="form-control" [for]="'delCol_'+column">Izbriši - kolonu - <input type="radio" [id]="'delCol_'+column" - [name]="'delOp_'+column" - (change)="emptyFillTextInput(column)"></label> - </div> - </div> - - <div class="flex-shrink-1 mx-3"> - <div class="input-group"> - <label class="form-control" [for]="'delRows_'+column">Izbriši - redove - <input type="radio" [id]="'delRows_'+column" - [name]="'delOp_'+column" checked - (change)="emptyFillTextInput(column)"></label> - </div> - </div> - </div> - </div> - </div> - <!--Izlazna kolona - popunjavanje null vrednosti --> - <div *ngFor="let column of selectedDataset.header; let i = index" class="my-3"> - <!--moze bez for petlje (this.selectedOutputColumnVal je id), al ne moze ova fja array column onda--> - <div class="input-group row" *ngIf="getInputById('rb_'+column).checked"> - <span class="input-group-text col-2 text-center"> - {{column}} <span class="small" - style="color:gray;">({{calculateSumOfNullValuesInCol(column)}} - null)</span> - </span> - <label *ngIf="calculateSumOfNullValuesInCol(column)==0" - class="form-control">Ova kolona nema nedostajućih vrednosti.</label> - - <input *ngIf="calculateSumOfNullValuesInCol(column)!=0" type="radio" - [id]="'fillCol_'+column" class="col-1 mt-2" [name]="'delOp_'+column"> - <!--OVDE SREDI IZGLED--> - <select [id]="'replaceOptions'+i" class="form-control col-2" - *ngIf="isNumber(datasetFile[1][i]) && calculateSumOfNullValuesInCol(column)!=0" - (change)="replaceWithSelectedNumber($event);"> - <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 col-2" - *ngIf="!isNumber(datasetFile[1][i]) && calculateSumOfNullValuesInCol(column)!=0" - (change)="replaceWithSelectedString($event);"> - <option *ngFor="let option of arrayColumn(datasetFile, i)" - [value]="option"> - {{ option }} - </option> - </select> - <input *ngIf="calculateSumOfNullValuesInCol(column)!=0" type="text" - class="form-control col-1" [id]="'fillText_'+column" - (keyup)="checkFillColRadio(column)" placeholder="Unesi vrednost..."> - - <label *ngIf="calculateSumOfNullValuesInCol(column)!=0" - class="form-control col-2" [for]="'delCol_'+column">Izbriši kolonu - <input type="radio" [id]="'delCol_'+column" [name]="'delOp_'+column" - (change)="emptyFillTextInput(column)"></label> - <label *ngIf="calculateSumOfNullValuesInCol(column)!=0" - class="form-control col-2" [for]="'delRows_'+column">Izbriši redove - <input type="radio" [id]="'delRows_'+column" [name]="'delOp_'+column" - (change)="emptyFillTextInput(column)" checked></label> - </div> - </div> - - </div> - </div> - </div> - </div> - </div> - </div> - </div> - - <h2 class="mt-5 mb-4">Parametri treniranja:</h2> - - <!--**********************************************************TIP*********************************************************--> - <div> - <div class="row p-2"> - <div class="col-1"> - </div> - <div class="col-3"> - <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()"> - <option - *ngFor="let option of Object.keys(ProblemType); let optionName of Object.values(ProblemType)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <!--******************************************************************************************************************--> - <div class="col-1"> - </div> - <div class="col-3"> - <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])"> - </div> - </div> - - <div class="row p-2"> - <!--***********************************ENKODER*********************************************--> - - <div class="col-1"> - </div> - <div class="col-3"> - <label for="encoding" class="col-form-label">Enkoding: </label> - </div> - <div class="col-2"> - <select id=encodingOptions class="form-control" name="encoding" [(ngModel)]="newModel.encoding"> - <option *ngFor="let option of Object.keys(Encoding); let optionName of Object.values(Encoding)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - - <!--***********************************BROJ NEURONA SKRIVENOG SLOJA*********************************************--> - - <div class="col-1"> - </div> - <div class="col-3"> - <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"> - </div> - </div> - <!--***********************************OPTIMIZACIJA*********************************************--> - - <div class="row p-2"> - <div class="col-1"> - </div> - <div class="col-3"> - <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"> - <option - *ngFor="let option of Object.keys(Optimizer); let optionName of Object.values(Optimizer)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <div class="col-1"> - </div> - <div class="col-3"> - <label for="batchSize" class="col-form-label">Broj uzorka po iteraciji: </label> - </div> - <div class="col-1"> - <input type="number" min="1" class="form-control" name="batchSize" [(ngModel)]="newModel.batchSize"> - </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> - </div> - <div class="col-2"> - <select id=lossFunctionOptions class="form-control" name="lossFunction" - [(ngModel)]="newModel.lossFunction" aria-checked="true"> - <option - *ngFor="let option of Object.keys(lossFunction); let optionName of Object.values(lossFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <div class="col-1"></div> - <div class="col-3 mt-2"> - <label for="type" class="form-check-label">Nasumičan redosled podataka?</label> - <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="newModel.randomOrder" - type="checkbox" value="" checked> - </div> - </div> - <!--************************************************************************************************* --> - <div class="border m-3"> - <div class="row p-2 m-2"> - <div class="col-4"> - <label for="splitYesNo" class="form-check-label"> - <h3>Podela test skupa: - <input id="splitYesNo" class="form-check-input" type="checkbox" - [checked]="newModel.randomTestSet" - (change)="newModel.randomTestSet = !newModel.randomTestSet"> - </h3> - </label> - </div> - <div class="col-8"> - trening - <mat-slider style="width: 85%;" min="10" max="90" step="10" value="10" - name="randomTestSetDistribution" thumbLabel [disabled]="!newModel.randomTestSet" - [(ngModel)]="tempTestSetDistribution"> - </mat-slider> - test - </div> - </div> - - <div class="row p-2 mx-2"> - <div class="col-4"> - <label for="percentage" class="form-label">Procenat podataka koji se uzima za trening - skup:</label> - </div> - <div class="col-2"> - <input id="percentage" type="number" class="form-control" min="10" max="90" step="10" value="90" - [(ngModel)]="tempTestSetDistribution" [disabled]="!newModel.randomTestSet"> - </div> - </div> - </div> - - <!--***********************************AKTIVACIONE FUNKCIJE*********************************************--> - <h3>Aktivacione funkcije:</h3> - - <div class="row p-2 m-2" style="align-self: center;"> - <div class="col-3"> - <label for="hiddenLayerActivationFunction" class="col-form-label" - style="text-align: center;">Funkcija aktivacije skrivenih - slojeva:</label> - </div> - <div class="col-3"> - <div *ngFor="let item of [].constructor(newModel.hiddenLayers); let i = index"> - <div class="input-group mb-2"> - <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]"> - <option - *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - </div> - </div> - <div class="col-3"> - <label for="outputLayerActivationFunction" class="col-form-label" - style="text-align: center;">Funkcija aktivacije izlaznog - sloja:</label> - </div> - <div class="col-3"> - <select id=outputLayerActivationFunctionOptions class="form-control" - name="outputLayerActivationFunction" [(ngModel)]="newModel.outputLayerActivationFunction"> - <option - *ngFor="let option of Object.keys(ActivationFunction); let optionName of Object.values(ActivationFunction)" - [value]="option"> - {{ optionName }} - </option> - </select> - </div> - <div class="col"> - </div> - </div> - </div> - - <br><br> - <!--**********************************************METRIKE ZA KOMPAJLER I GRAFIKE***********************************************--> - - <hr> - <div class="form-check form-check-inline overflow-auto " style="width: max-content;"> - <h3>Izaberite metrike:</h3> - <div id="divMetricsinput"> - - <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;"> - <label class="form-check-label" for="metrics_{{option}}" for="inlineCheckbox2"> - {{optionName}} - </label> - </div> - </div> - </div> - <!--<app-annvisual [model]="newModel"></app-annvisual>--> - <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)="addModel();">Sačuvaj model</button> - <div class="col"></div> - <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" - (click)="trainModel();">Treniraj model</button> - <div class="col"></div> - </div> - - <div class="m-3" *ngIf="trainingResult"> - <h2>Rezultati treniranja</h2> - <p> - {{trainingResult}} - </p> - </div> - </div> -</div> - -<!--<button - (click)="calculateSumOfNullValuesInCol('Embarked'); calculateMeanColValue('Age');calculateMedianColValue('Age'); getNullValuesReplacersArray();">DUGME</button>-->
\ No newline at end of file diff --git a/frontend/src/app/_pages/add-model/add-model.component.ts b/frontend/src/app/_pages/add-model/add-model.component.ts deleted file mode 100644 index 2121dc3b..00000000 --- a/frontend/src/app/_pages/add-model/add-model.component.ts +++ /dev/null @@ -1,510 +0,0 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; -import Model, { LossFunctionBinaryClassification, LossFunctionMultiClassification, LossFunctionRegression, NullValReplacer, ReplaceWith } from 'src/app/_data/Model'; -import { ProblemType, Encoding, ActivationFunction, LossFunction, Optimizer, NullValueOptions, Metrics, MetricsRegression, MetricsBinaryClassification, MetricsMultiClassification } 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'; -import Dataset from 'src/app/_data/Dataset'; -import { DatatableComponent } from 'src/app/_elements/datatable/datatable.component'; -import { DatasetsService } from 'src/app/_services/datasets.service'; -import { NgxCsvParser } from 'ngx-csv-parser'; -import { CsvParseService } from 'src/app/_services/csv-parse.service'; - - -@Component({ - selector: 'app-add-model', - templateUrl: './add-model.component.html', - styleUrls: ['./add-model.component.css'] -}) -export class AddModelComponent implements OnInit { - - @ViewChild(DatasetLoadComponent) datasetLoadComponent?: DatasetLoadComponent; - @ViewChild(DatatableComponent) datatable?: DatatableComponent; - datasetLoaded: boolean = false; - selectedDatasetLoaded: boolean = false; - - newModel: Model; - - ProblemType = ProblemType; - Encoding = Encoding; - ActivationFunction = ActivationFunction; - metrics: any = Metrics; - LossFunction = LossFunction; - lossFunction: any = LossFunction; - Optimizer = Optimizer; - NullValueOptions = NullValueOptions; - ReplaceWith = ReplaceWith; - Object = Object; - document = document; - shared = shared; - - selectedOutputColumnVal: string = ''; - - showMyDatasets: boolean = true; - myDatasets?: Dataset[]; - existingDatasetSelected: boolean = false; - selectedDataset?: Dataset; - otherDataset?: Dataset; - otherDatasetFile?: any[]; - datasetFile?: any[]; - datasetHasHeader?: boolean = true; - - tempTestSetDistribution: number = 90; - - //accepted: Boolean; - term: string = ""; - - selectedProblemType: string = ''; - selectedMetrics = []; - - trainingResult: string | undefined; - - constructor(private models: ModelsService, private datasets: DatasetsService, private csv: CsvParseService) { - this.newModel = new Model(); - - this.datasets.getMyDatasets().subscribe((datasets) => { - this.myDatasets = datasets; - }); - } - - ngOnInit(): void { - (<HTMLInputElement>document.getElementById("btnMyDataset")).focus(); - } - - - viewMyDatasetsForm() { - this.showMyDatasets = true; - this.resetSelectedDataset(); - //this.datasetLoaded = false; - this.resetCbsAndRbs(); - } - viewNewDatasetForm() { - this.showMyDatasets = false; - this.resetSelectedDataset(); - this.resetCbsAndRbs(); - } - - addModel() { - if (!this.showMyDatasets) - this.saveModelWithNewDataset(_ => { console.log('MODEL ADDED (with new dataset).') }); - else - this.saveModelWithExistingDataset(_ => { console.log('MODEL ADDED (with existing dataset).') }); - } - - trainModel() { - let saveFunc; - this.trainingResult = undefined; - - 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); - this.trainingResult = response; - }); - })); //privremeno cuvanje modela => vraca id sacuvanog modela koji cemo da treniramo sad - } - - saveModelWithNewDataset(callback: ((arg0: any) => void)) { - - this.getCheckedInputCols(); - this.getCheckedOutputCol(); - this.getMetrics(); - - 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.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; - - //da se doda taj dataset u listu postojecih, da bude izabran - this.refreshMyDatasetList(); - this.showMyDatasets = true; - this.selectThisDataset(dataset); - - this.newModel.randomTestSetDistribution = 1 - Math.round(this.tempTestSetDistribution / 100 * 10) / 10; - this.tempTestSetDistribution = 90; - this.newModel.username = shared.username; - - this.newModel.nullValuesReplacers = this.getNullValuesReplacersArray(); - - this.models.addModel(this.newModel).subscribe((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 - }, (error) => { - alert("Dataset sa unetim nazivom već postoji u Vašoj kolekciji.\nIzmenite naziv ili iskoristite postojeći dataset."); - }); //kraj addDataset subscribe - } //kraj treceg ifa - }, (error) => { - //alert("greska uploadData"); - }); //kraj uploadData subscribe - - } //kraj drugog ifa - } //kraj prvog ifa - } - - saveModelWithExistingDataset(callback: ((arg0: any) => void)): any { - if (this.selectedDataset) { //dataset je izabran - this.getCheckedInputCols(); - this.getCheckedOutputCol(); - this.getMetrics(); - if (this.validationInputsOutput()) { - this.newModel.datasetId = this.selectedDataset._id; - - this.newModel.randomTestSetDistribution = 1 - Math.round(this.tempTestSetDistribution / 100 * 10) / 10; - this.tempTestSetDistribution = 90; - this.newModel.username = shared.username; - - this.newModel.nullValuesReplacers = this.getNullValuesReplacersArray(); - - this.models.addModel(this.newModel).subscribe((response) => { - callback(response); - }, (error) => { - alert("Model sa unetim nazivom već postoji u Vašoj kolekciji.\nPromenite naziv modela i nastavite sa kreiranim datasetom."); - }); - } - } - else { - alert("Molimo Vas da izaberete neki dataset iz kolekcije."); - } - } - - getCheckedInputCols() { - this.newModel.inputColumns = []; - let checkboxes: any; - - checkboxes = document.getElementsByName("cbsNew"); - - for (let i = 0; i < checkboxes.length; i++) { - let thatCb = <HTMLInputElement>checkboxes[i]; - if (thatCb.checked == true) // && thatCb.disabled == false ne treba nam ovo vise - this.newModel.inputColumns.push(thatCb.value); - } - //console.log(this.checkedInputCols); - } - getCheckedOutputCol() { - this.newModel.columnToPredict = ''; - let radiobuttons: any; - - radiobuttons = document.getElementsByName("rbsNew"); - - for (let i = 0; i < radiobuttons.length; i++) { - let thatRb = <HTMLInputElement>radiobuttons[i]; - if (thatRb.checked) { - this.newModel.columnToPredict = thatRb.value; - break; - } - } - //console.log(this.checkedOutputCol); - } - validationInputsOutput(): boolean { - if (this.newModel.inputColumns.length == 0 && this.newModel.columnToPredict == '') { - alert("Molimo Vas da izaberete ulazne i izlazne kolone za mrežu."); - return false; - } - else if (this.newModel.inputColumns.length == 0) { - alert("Molimo Vas da izaberete ulaznu kolonu/kolone za mrežu."); - return false; - } - else if (this.newModel.columnToPredict == '') { - alert("Molimo Vas da izaberete izlaznu kolonu za mrežu."); - return false; - } - for (let i = 0; i < this.newModel.inputColumns.length; i++) { - if (this.newModel.inputColumns[i] == this.newModel.columnToPredict) { - let colName = this.newModel.columnToPredict; - alert("Izabrali ste istu kolonu (" + colName + ") kao ulaznu i izlaznu iz mreže. Korigujte izbor."); - return false; - } - } - return true; - } - - selectThisDataset(dataset: Dataset) { - this.selectedDataset = dataset; - this.selectedDatasetLoaded = false; - this.existingDatasetSelected = true; - this.datasetHasHeader = this.selectedDataset.hasHeader; - - this.datasets.getDatasetFile(dataset.fileId).subscribe((file: string | undefined) => { - if (file) { - this.datasetFile = this.csv.csvToArray(file, (dataset.delimiter == "razmak") ? " " : (dataset.delimiter == "") ? "," : dataset.delimiter); - /*for (let i = this.datasetFile.length - 1; i >= 0; i--) { //moguce da je vise redova na kraju fajla prazno i sl. - if (this.datasetFile[i].length != this.datasetFile[0].length) - this.datasetFile[i].pop(); - else - break; //nema potrebe dalje - }*/ - //console.log(this.datasetFile); - this.resetCbsAndRbs(); - this.refreshThreeNullValueRadioOptions(); - this.selectedDatasetLoaded = true; - this.scrollToNextForm(); - } - }); - //this.datasetHasHeader = false; - } - - scrollToNextForm() { - (<HTMLSelectElement>document.getElementById("selectInAndOuts")).scrollIntoView({ - behavior: "smooth", - block: "start", - inline: "nearest" - }); - } - - resetSelectedDataset(): boolean { - const temp = this.selectedDataset; - this.selectedDataset = this.otherDataset; - this.otherDataset = temp; - const tempFile = this.datasetFile; - this.datasetFile = this.otherDatasetFile; - this.otherDatasetFile = tempFile; - return true; - } - resetCbsAndRbs(): boolean { - this.uncheckRbs(); - this.checkAllCbs(); - return true; - } - checkAllCbs() { - let checkboxes: any; - - checkboxes = document.getElementsByName("cbsNew"); - for (let i = 0; i < checkboxes.length; i++) { - (<HTMLInputElement>checkboxes[i]).checked = true; - (<HTMLInputElement>checkboxes[i]).disabled = false; - } - } - uncheckRbs() { - this.selectedOutputColumnVal = ''; - let radiobuttons: any; - - radiobuttons = document.getElementsByName("rbsNew"); - for (let i = 0; i < radiobuttons.length; i++) - (<HTMLInputElement>radiobuttons[i]).checked = false; - } - - refreshMyDatasetList() { - this.datasets.getMyDatasets().subscribe((datasets) => { - this.myDatasets = datasets; - }); - } - - refreshThreeNullValueRadioOptions() { - this.newModel.nullValues = NullValueOptions.DeleteRows; - } - - isChecked(someId: string) { //proveri ako je element sa datim ID-em cek - return (<HTMLInputElement>document.getElementById(someId)).checked; - } - - isNumber(value: string | number): boolean { - return ((value != null) && - (value !== '') && - !isNaN(Number(value.toString()))); - } - - findColIndexByName(colName: string): number { - if (this.datasetFile) - for (let i = 0; i < this.datasetFile[0].length; i++) - if (colName === this.datasetFile[0][i]) - return i; - return -1; - } - findColNameByIndex(index: number): string { - if (this.datasetFile) - if (this.datasetHasHeader && index < this.datasetFile[0].length) - return this.datasetFile[0][index]; - return ''; - } - emptyFillTextInput(colName: string) { - (<HTMLInputElement>document.getElementById("fillText_" + colName)).value = ""; - } - - checkFillColRadio(colName: string) { - (<HTMLInputElement>document.getElementById("fillCol_" + colName)).checked = true; - } - calculateSumOfNullValuesInCol(colName: string): number { - //console.log(this.datasetFile); - if (this.datasetFile) { - let colIndex = this.findColIndexByName(colName); - let sumOfNulls = 0; - - let startValue = (this.datasetLoadComponent?.dataset.hasHeader) ? 1 : 0; - for (let i = startValue; i < this.datasetFile.length; i++) { - if (this.datasetFile[i][colIndex] == "" || this.datasetFile[i][colIndex] == undefined) - ++sumOfNulls; - } - return sumOfNulls; - } - return -1; - } - calculateMeanColValue(colName: string): number { - if (this.datasetFile) { - let colIndex = this.findColIndexByName(colName); - let sum = 0; - let n = 0; - - let startValue = (this.datasetLoadComponent?.dataset.hasHeader) ? 1 : 0; - for (let i = startValue; i < this.datasetFile.length; i++) - if (this.datasetFile[i][colIndex] != '') { - sum += Number(this.datasetFile[i][colIndex]); - ++n; - } - console.log(sum / n); - return (sum != 0) ? (sum / n) : 0; - } - return 0; - } - calculateMedianColValue(colName: string): number { - if (this.datasetFile) { - let array = []; - let colIndex = this.findColIndexByName(colName); - - let startValue = (this.datasetHasHeader) ? 1 : 0; - for (let i = startValue; i < this.datasetFile.length; i++) - if (this.datasetFile[i][colIndex] != '') - array.push(Number(this.datasetFile[i][colIndex])); - - array.sort(); - if (array.length % 2 == 0) - return array[array.length / 2 - 1] / 2; - else - return array[(array.length - 1) / 2]; - } - return 0; - } - replaceWithSelectedString(event: Event) { - let value = (<HTMLInputElement>event.target).value; - let colIndex = Number(((<HTMLSelectElement>event.target).id).split("replaceOptions")[1]); - let colName = this.findColNameByIndex(colIndex); - - (<HTMLInputElement>document.getElementById("fillCol_" + colName)).checked = true; - - if (!this.datasetHasHeader) - (<HTMLInputElement>document.getElementById("fillText_" + colName)).value = value; - else { - if (value == colName) - (<HTMLInputElement>document.getElementById("fillText_" + colName)).value = ""; - else - (<HTMLInputElement>document.getElementById("fillText_" + colName)).value = value; - } - } - replaceWithSelectedNumber(event: Event) { - let option = (<HTMLInputElement>event.target).value; - let colIndex = Number(((<HTMLSelectElement>event.target).id).split("replaceOptions")[1]); - let colName = this.findColNameByIndex(colIndex); - - (<HTMLInputElement>document.getElementById("fillCol_" + colName)).checked = true; - - if (option == ReplaceWith.Mean) - (<HTMLInputElement>document.getElementById("fillText_" + colName)).value = this.calculateMeanColValue(colName).toString(); - else if (option == ReplaceWith.Median) - (<HTMLInputElement>document.getElementById("fillText_" + colName)).value = this.calculateMedianColValue(colName).toString(); - else if (option == ReplaceWith.None) - (<HTMLInputElement>document.getElementById("fillText_" + colName)).value = ""; - } - - - getNullValuesReplacersArray(): NullValReplacer[] { - let array: NullValReplacer[] = []; - - if (this.datasetFile) { - - if (this.newModel.nullValues == NullValueOptions.Replace) { - - for (let i = 0; i < this.datasetFile[0].length; i++) { - let column = this.datasetFile[0][i]; - - if (this.calculateSumOfNullValuesInCol(column) > 0) { //ako kolona nema null vrednosti, ne dodajemo je u niz - if ((<HTMLInputElement>document.getElementById("delCol_" + column)).checked) { - array.push({ - column: column, - option: NullValueOptions.DeleteColumns, - value: "" - }); - } - else if ((<HTMLInputElement>document.getElementById("delRows_" + column)).checked) { - array.push({ - column: column, - option: NullValueOptions.DeleteRows, - value: "" - }); - } - else if (((<HTMLInputElement>document.getElementById("fillCol_" + column)).checked)) { - array.push({ - column: column, - option: NullValueOptions.Replace, - value: (<HTMLInputElement>document.getElementById("fillText_" + column)).value - }); - } - } - } - } - } - //console.log(array); - return array; - } - - getInputById(id: string): HTMLInputElement { - return document.getElementById(id) as HTMLInputElement; - } - - arrayColumn = (arr: any[][], n: number) => [...this.dropEmptyString(new Set(arr.map(x => x[n])))]; - - dropEmptyString(set: Set<any>): Set<string> { - if (set.has("")) - set.delete(""); - if (set.has(null)) - set.delete(null); - if (set.has(undefined)) - set.delete(undefined); - return set; - } - - filterOptions() { - switch (this.newModel.type) { - case 'regresioni': - this.lossFunction = LossFunctionRegression; - this.metrics = MetricsRegression; - break; - case 'binarni-klasifikacioni': - this.lossFunction = LossFunctionBinaryClassification; - this.metrics = MetricsBinaryClassification; - break; - case 'multi-klasifikacioni': - this.lossFunction = LossFunctionMultiClassification; - this.metrics = MetricsMultiClassification; - break; - default: - break; - } - } - - getMetrics() { - this.newModel.metrics = []; - let cb = document.getElementsByName("cbmetrics"); - - for (let i = 0; i < cb.length; i++) { - let chb = <HTMLInputElement>cb[i]; - if (chb.checked == true) - this.newModel.metrics.push(chb.value); - } - - } -} diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html index a4ab6e2c..27e06884 100644 --- a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html +++ b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.html @@ -5,7 +5,9 @@ <div class="row mt-3 mb-2 d-flex justify-content-center"> <div class="col-sm-6" style="margin-bottom: 10px;"> + <p class="glyphicon glyphicon-search"></p> <input type="text" class="form-control" placeholder="Pretraga" [(ngModel)]="term"> + </div> <div class="row"> @@ -14,7 +16,7 @@ <div class="card-body"> <h3 class="card-title"><b>{{predictor.name}}</b></h3> <p class="card-text">{{predictor.description}}</p> - <a class="btn btn-primary" (click)="openPredictor(predictor._id)">Otvori</a> + <a class="btn btn-primary" (click)="openPredictor(predictor._id)">Iskoristi</a> </div> <div class="card-footer text-muted"> Kreirao: {{predictor.username}} <br> diff --git a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts index 4f96fc36..891b3cab 100644 --- a/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts +++ b/frontend/src/app/_pages/browse-predictors/browse-predictors.component.ts @@ -20,7 +20,7 @@ export class BrowsePredictorsComponent implements OnInit { ngOnInit(): void { } openPredictor(id:string):void{ - this.router.navigateByUrl('/predict?id='+id); + this.router.navigate(['predict/'+id]); }; } 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 b75decf2..fc146046 100644 --- a/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts +++ b/frontend/src/app/_pages/filter-datasets/filter-datasets.component.ts @@ -4,6 +4,7 @@ import Dataset from 'src/app/_data/Dataset'; import {Router} from '@angular/router' import { JwtHelperService } from '@auth0/angular-jwt'; import { CookieService } from 'ngx-cookie-service'; +import shared from 'src/app/Shared'; @Component({ selector: 'app-filter-datasets', @@ -12,6 +13,7 @@ import { CookieService } from 'ngx-cookie-service'; }) export class FilterDatasetsComponent implements OnInit { + shared = shared; publicDatasets?: Dataset[]; term: string = ""; constructor(private datasets: DatasetsService,private router:Router, private cookie: CookieService) { @@ -37,11 +39,9 @@ export class FilterDatasetsComponent implements OnInit { if(name!=null && name!="") this.datasets.addDataset(newDataset).subscribe((response:string)=>{ console.log(response); - alert("Uspenso ste dodali dataset sa imenom "+newDataset.name); + shared.openDialog("Obaveštenje", "Uspešno ste dodali dataset sa nazivom " + newDataset.name); },(error)=>{ - alert("Vec imate dataset sa istim imenom molim vas unesite drugo ime"); - - + shared.openDialog("Obaveštenje", "U svojoj kolekciji već imate dataset sa ovim imenom. Molimo Vas da unesete drugo ime."); }); }; diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.html b/frontend/src/app/_pages/my-datasets/my-datasets.component.html index 623b9ac8..d996bf31 100644 --- a/frontend/src/app/_pages/my-datasets/my-datasets.component.html +++ b/frontend/src/app/_pages/my-datasets/my-datasets.component.html @@ -1,5 +1,34 @@ -<ul class="list-group my-2"> - <li class="list-group-item" *ngFor="let dataset of myDatasets"> - <app-item-dataset [dataset]="dataset"></app-item-dataset> - </li> -</ul>
\ No newline at end of file +<div id="wrapper"> + <div id="container" class="container p-5" style="background-color: white; 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 dataset of myDatasets"> + <app-item-dataset [dataset]="dataset"></app-item-dataset> + + <div class="panel-footer row"><!-- panel-footer --> + <div class="col-xs-6 text-center"> + <div> + <button type="button" class="btn btn-default btn-lg" mat-raised-button color="primary" (click)="deleteThisDataset(dataset)">Obriši + <span class="glyphicon glyphicon-chevron-right"></span> + </button> + </div> + </div> + </div><!-- end panel-footer --> + </div> + </div> + <div class="text-center" *ngIf="this.myDatasets.length == 0" > + <h2>Nema rezultata</h2> + </div> + </div> + + </div> + + + + + + </div> diff --git a/frontend/src/app/_pages/my-datasets/my-datasets.component.ts b/frontend/src/app/_pages/my-datasets/my-datasets.component.ts index 13b0c47b..1551a3c8 100644 --- a/frontend/src/app/_pages/my-datasets/my-datasets.component.ts +++ b/frontend/src/app/_pages/my-datasets/my-datasets.component.ts @@ -1,5 +1,10 @@ import { Component, OnInit } from '@angular/core'; +import {Router} from '@angular/router'; +import { DatasetsService } from 'src/app/_services/datasets.service'; import Dataset from 'src/app/_data/Dataset'; +import { JwtHelperService } from '@auth0/angular-jwt'; +import { CookieService } from 'ngx-cookie-service'; +import shared from 'src/app/Shared'; @Component({ selector: 'app-my-datasets', @@ -7,18 +12,54 @@ import Dataset from 'src/app/_data/Dataset'; styleUrls: ['./my-datasets.component.css'] }) export class MyDatasetsComponent implements OnInit { + myDatasets: Dataset[] = []; - myDatasets?: Dataset[]; + constructor(private datasetsS : DatasetsService) { - constructor() { - this.myDatasets = [ - new Dataset('Titanik', 'Opis titanik', ['K1', 'K2', 'K3', 'Ime', 'Preziveli']), - new Dataset('Neki drugi set', 'opis', ['a', 'b', 'c']), - new Dataset('Treci set', 'opis', ['a', 'b', 'c']) - ]; + + + } + + ngOnInit(): void { + + this.datasetsS.getMyDatasets().subscribe((response) => { + this.myDatasets = response; + }, (error) => { + if (error.error == "Dataset with...") { + shared.openDialog("Greska", "Niste dobro uneli nesto"); + } + }); + } + +/* + editModel(): void{ + this.modelsS.editModel().subscribe(m => { + this.myModel = m; + + }) } +*/ + +deleteThisDataset(dataset: Dataset): void{ + console.log("OK"); + this.datasetsS.deleteDataset(dataset).subscribe((response) => { + console.log("OBRISANO JE", response); + //na kraju uspesnog + this.getAllMyDatasets(); + }, (error) =>{ + if (error.error == "Dataset with name = {name} deleted") { + alert("Greška pri brisanju dataseta!"); + } + }); + +} - ngOnInit(): void { + getAllMyDatasets(): void{ + this.datasetsS.getMyDatasets().subscribe(m => { + + this.myDatasets = m; + console.log(this.myDatasets); + }); } } 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 ec6c83d5..4aebc1f2 100644 --- a/frontend/src/app/_pages/my-models/my-models.component.html +++ b/frontend/src/app/_pages/my-models/my-models.component.html @@ -13,7 +13,7 @@ <div class="col-xs-6 text-center"> <div> <button type="button" class="btn btn-default btn-lg" (click)="deleteThisModel(model)" mat-raised-button color="primary">Koristi - <span class="glyphicon glyphicon-chevron-left"></span> + <span class="glyphicon glyphicon-search"></span> </button> <button type="button" class="btn btn-default btn-lg" mat-raised-button color="primary" (click)="deleteThisModel(model)">Obriši <span class="glyphicon glyphicon-chevron-right"></span> 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 bd6b0a2b..6086b1b1 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 shared from 'src/app/Shared'; import Model from 'src/app/_data/Model'; import { ModelsService } from 'src/app/_services/models.service'; @@ -38,7 +39,7 @@ deleteThisModel(model: Model): void{ this.getAllMyModels(); }, (error) =>{ if (error.error == "Model with name = {name} deleted") { - alert("Greška pri brisanju modela!"); + shared.openDialog("Obaveštenje", "Greška prilikom brisanja modela."); } }); 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 d739f561..3746d35e 100644 --- a/frontend/src/app/_pages/my-predictors/my-predictors.component.html +++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.html @@ -7,7 +7,7 @@ <app-item-predictor [predictor]="predictor"></app-item-predictor> </div> <div> - <button (click)="delete()" mat-raised-button color="warn" style="min-width: 15rem;float: right" ><mat-icon>delete</mat-icon></button> + <button (click)="delete(predictor)" mat-raised-button color="warn" style="min-width: 15rem;float: right" ><mat-icon>delete</mat-icon></button> </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 58daa44f..13cfdab2 100644 --- a/frontend/src/app/_pages/my-predictors/my-predictors.component.ts +++ b/frontend/src/app/_pages/my-predictors/my-predictors.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import Predictor from 'src/app/_data/Predictor'; +import { PredictorsService } from 'src/app/_services/predictors.service'; @Component({ selector: 'app-my-predictors', @@ -7,22 +8,38 @@ import Predictor from 'src/app/_data/Predictor'; styleUrls: ['./my-predictors.component.css'] }) export class MyPredictorsComponent implements OnInit { - predictors: Predictor[]; - constructor() { - this.predictors = [ - new Predictor('Titanik', 'Opis titanik', ['K1', 'K2', 'K3', 'Ime', 'Preziveli'],'Preziveli'), - new Predictor('Neki drugi set', 'opis', ['a', 'b', 'c'],'c'), - 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')]; + predictors: Predictor[] = []; + constructor(private predictorsS : PredictorsService) { } ngOnInit(): void { + this.getAllMyPredictors(); + } - delete(){ - confirm("IZABRANI MODEL ĆE BITI IZBRISAN") + delete(predictor: Predictor){ + if(window.confirm("IZABRANI MODEL ĆE BITI IZBRISAN")) + { + this.predictorsS.deletePredictor(predictor).subscribe((response) => { + console.log("OBRISANOOO JEE", response); + //na kraju uspesnog + this.getAllMyPredictors(); + }, (error) =>{ + if (error.error == "Predictor with name = {name} deleted") { + alert("Greška pri brisanju modela!"); + } + }); + } + } - + + getAllMyPredictors(): void{ + this.predictorsS.getMyPredictors().subscribe(m => { + + this.predictors = m; + console.log(this.predictors); + }); + } } diff --git a/frontend/src/app/_pages/predict/predict.component.html b/frontend/src/app/_pages/predict/predict.component.html index bf4d9a1e..13afa8e4 100644 --- a/frontend/src/app/_pages/predict/predict.component.html +++ b/frontend/src/app/_pages/predict/predict.component.html @@ -18,7 +18,24 @@ <div> <label for="output" class="col-sm-5 col-form-label">Opis prediktora: <b>{{predictor.description}}</b></label> </div> + + + </div> + <br> + <label for="type" class="form-check-label" ><b>Informacije o prediktoru</b></label> + <div class="col-5 mt-2"> + <label for="type" class="form-check-label" >Prediktor {{predictor.isPublic?"je":"nije"}} javni.</label> + </div> + <div class="col-5 mt-2"> + <label for="type" class="form-check-label" >Prediktor {{predictor.accessibleByLink?"je":"nije"}} dostupan za deljenje.</label> </div> + <br> + <div class="col-2"> + <label for="dateCreated" class="col-form-label">Datum:</label> + <input type="text" class="form-control-plaintext" id="dateCreated" placeholder="--/--/--" + value="{{predictor.dateCreated | date: 'dd/MM/yyyy'}}" readonly> + </div> + <br> <div > @@ -28,7 +45,7 @@ <div *ngIf="predictor" class="form-group row mt-3 mb-2 d-flex justify-content-left"> <div *ngFor="let input of predictor.inputs; let i = index"> <label for="{{input}}" class="col-sm-2 col-form-label"><b>{{input}}</b></label> - <input name="{{input}}" type="text" [(ngModel)]="inputs[i]" > + <input name="{{input}}" type="text" [(ngModel)]="inputs[i].value" > </div> @@ -41,28 +58,7 @@ <div> <label for="output" class="col-sm-2 col-form-label">Izlaz: <b>{{predictor.output}}</b></label> </div> - <br> - <br> - - <div class="col-5 mt-2"> - <label for="type" class="form-check-label">Da li je prediktor javan?</label> - <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="predictor.isPublic" - type="checkbox" value=""> - </div> - <div class="col-5 mt-2"> - <label for="type" class="form-check-label">Da li je dostupan za deljenje?</label> - <input class="mx-3 form-check-input" type="checkbox" [(ngModel)]="predictor.accessibleByLink" - type="checkbox" value="" > - </div> - <br> - <div class="col-2"> - <label for="dateCreated" class="col-form-label">Datum:</label> - <input type="text" class="form-control-plaintext" id="dateCreated" placeholder="--/--/--" - value="{{predictor.dateCreated | date: 'dd/MM/yyyy'}}" readonly> - </div> - - <br><br> <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;" diff --git a/frontend/src/app/_pages/predict/predict.component.ts b/frontend/src/app/_pages/predict/predict.component.ts index 71cf93ce..1c1c7425 100644 --- a/frontend/src/app/_pages/predict/predict.component.ts +++ b/frontend/src/app/_pages/predict/predict.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import Predictor from 'src/app/_data/Predictor'; import { PredictorsService } from 'src/app/_services/predictors.service'; +import shared from 'src/app/Shared'; @Component({ selector: 'app-predict', @@ -10,7 +11,8 @@ import { PredictorsService } from 'src/app/_services/predictors.service'; }) export class PredictComponent implements OnInit { - inputs : String[] = []; + inputs : Column[] = []; + predictor:Predictor; constructor(private predictS : PredictorsService, private route: ActivatedRoute) { @@ -22,6 +24,7 @@ export class PredictComponent implements OnInit { this.predictS.getPredictor(url["id"]).subscribe(p => { this.predictor = p; + this.predictor.inputs.forEach((p,index)=> this.inputs[index] = new Column(p, "")); console.log(this.predictor); }) }); @@ -29,9 +32,16 @@ export class PredictComponent implements OnInit { usePredictor(): void{ this.predictS.usePredictor(this.predictor, this.inputs).subscribe(p => { - - alert("Prediktor je uspesno poslat na treniranje!"); + shared.openDialog("Obaveštenje", "Prediktor je uspešno poslat na probu."); //pisalo je "na treniranje" ?? }) console.log(this.inputs); } } + + +export class Column { + constructor( + public name : string, + public value : (number | string)){ + } +}
\ No newline at end of file diff --git a/frontend/src/app/_pages/profile/profile.component.ts b/frontend/src/app/_pages/profile/profile.component.ts index 7a7796da..d055fad3 100644 --- a/frontend/src/app/_pages/profile/profile.component.ts +++ b/frontend/src/app/_pages/profile/profile.component.ts @@ -6,6 +6,7 @@ import { Router } from '@angular/router'; import { PICTURES } from 'src/app/_data/ProfilePictures'; import { Picture } from 'src/app/_data/ProfilePictures'; import shared from '../../Shared'; +import { share } from 'rxjs'; @Component({ @@ -15,8 +16,6 @@ import shared from '../../Shared'; }) export class ProfileComponent implements OnInit { - - user: User = new User(); pictures: Picture[] = PICTURES; @@ -90,18 +89,18 @@ export class ProfileComponent implements OnInit { if (this.user.username != editedUser.username) { //promenio username, ide logout this.user = editedUser; this.resetInfo(); - alert("Nakon promene korisničkog imena, moraćete ponovo da se ulogujete."); + shared.openDialog("Obaveštenje", "Nakon promene korisničkog imena, moraćete ponovo da se ulogujete."); this.authService.logOut(); this.router.navigate(['']); return; } - alert("Podaci su uspešno promenjeni."); + shared.openDialog("Obaveštenje", "Podaci su uspešno promenjeni."); this.user = editedUser; console.log(this.user); this.resetInfo(); }, (error: any) =>{ if (error.error == "Username already exists!") { - alert("Ukucano korisničko ime je već zauzeto!\nIzaberite neko drugo."); + shared.openDialog("Obaveštenje", "Ukucano korisničko ime je već zauzeto! Izaberite neko drugo."); //(<HTMLSelectElement>document.getElementById("inputUsername")).focus(); //poruka obavestenja ispod inputa this.resetInfo(); @@ -131,7 +130,7 @@ export class ProfileComponent implements OnInit { this.userInfoService.changeUserPassword(passwordArray).subscribe((response: any) => { //console.log("PROMENIO LOZINKU"); this.resetNewPassInputs(); - alert("Nakon promene lozinke, moraćete ponovo da se ulogujete."); + shared.openDialog("Obaveštenje", "Nakon promene lozinke, moraćete ponovo da se ulogujete."); this.authService.logOut(); this.router.navigate(['']); }, (error: any) => { @@ -142,7 +141,7 @@ export class ProfileComponent implements OnInit { return; } else if (error.error == 'Identical password!') { - alert("Stara i nova lozinka su identične."); + shared.openDialog("Obaveštenje", "Stara i nova lozinka su identične."); this.resetNewPassInputs(); //(<HTMLSelectElement>document.getElementById("inputNewPassword")).focus(); return; diff --git a/frontend/src/app/_services/datasets.service.ts b/frontend/src/app/_services/datasets.service.ts index 0ff63828..cd901481 100644 --- a/frontend/src/app/_services/datasets.service.ts +++ b/frontend/src/app/_services/datasets.service.ts @@ -27,4 +27,12 @@ export class DatasetsService { getDatasetFile(fileId: any): any { return this.http.get(`${API_SETTINGS.apiURL}/file/download?id=${fileId}`, { headers: this.authService.authHeader(), responseType: 'text' }); } + + editDataset(dataset: Dataset): Observable<Dataset> { + return this.http.put<Dataset>(`${API_SETTINGS.apiURL}/dataset/`, dataset, { headers: this.authService.authHeader() }); + } + + deleteDataset(dataset: Dataset) { + return this.http.delete(`${API_SETTINGS.apiURL}/dataset/` + dataset.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 54ae5694..844cd706 100644 --- a/frontend/src/app/_services/predictors.service.ts +++ b/frontend/src/app/_services/predictors.service.ts @@ -3,6 +3,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { API_SETTINGS } from 'src/config'; import Predictor from '../_data/Predictor'; +import { Column } from '../_pages/predict/predict.component'; import { AuthService } from './auth.service'; @Injectable({ @@ -21,7 +22,15 @@ export class PredictorsService { return this.http.get<Predictor>(`${API_SETTINGS.apiURL}/predictor/getpredictor/`+ id, { headers: this.authService.authHeader() }); } - usePredictor(predictor: Predictor, inputs : String[]) { + usePredictor(predictor: Predictor, inputs : Column[]) { return this.http.post(`${API_SETTINGS.apiURL}/predictor/usepredictor/` + predictor._id, inputs, { headers: this.authService.authHeader() }); } + + deletePredictor(predictor: Predictor) { + return this.http.delete(`${API_SETTINGS.apiURL}/predictor/` + predictor.name, { headers: this.authService.authHeader(), responseType: "text" }); + } + + getMyPredictors(): Observable<Predictor[]> { + return this.http.get<Predictor[]>(`${API_SETTINGS.apiURL}/predictor/mypredictors`, { headers: this.authService.authHeader() }); + } } diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index fd827ac9..93431397 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AuthGuardService } from './_services/auth-guard.service'; -import { AddModelComponent } from './_pages/add-model/add-model.component'; import { HomeComponent } from './_pages/home/home.component'; import { MyDatasetsComponent } from './_pages/my-datasets/my-datasets.component'; import { MyModelsComponent } from './_pages/my-models/my-models.component'; @@ -13,10 +12,12 @@ import { SettingsComponent } from './_pages/settings/settings.component'; import { ProfileComponent } from './_pages/profile/profile.component'; import { PredictComponent } from './_pages/predict/predict.component'; import { FilterDatasetsComponent } from './_pages/filter-datasets/filter-datasets.component'; +import { ExperimentComponent } from './experiment/experiment.component'; const routes: Routes = [ { path: '', component: HomeComponent, data: { title: 'Početna strana' } }, - { path: 'add-model', component: AddModelComponent, data: { title: 'Dodaj model' } }, + /*{ path: 'add-model', component: AddModelComponent, data: { title: 'Dodaj model' } },*/ + { path: 'experiment', component: ExperimentComponent, data: { title: 'Dodaj model' } }, { path: 'my-datasets', component: MyDatasetsComponent, canActivate: [AuthGuardService], data: { title: 'Moji izvori podataka' } }, { path: 'my-models', component: MyModelsComponent, canActivate: [AuthGuardService], data: { title: 'Moji modeli' } }, { path: 'my-predictors', component: MyPredictorsComponent, canActivate: [AuthGuardService], data: { title: 'Moji trenirani modeli' } }, diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index d90de54d..c3a2ce7a 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -1,4 +1,4 @@ -import { NgModule } from '@angular/core'; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; @@ -11,7 +11,6 @@ import { Ng2SearchPipeModule } from 'ng2-search-filter'; import { AppComponent } from './app.component'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { DatasetLoadComponent } from './_elements/dataset-load/dataset-load.component'; -import { AddModelComponent } from './_pages/add-model/add-model.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { LoginModalComponent } from './_modals/login-modal/login-modal.component'; import { ReactiveFormsModule } from '@angular/forms'; @@ -39,12 +38,16 @@ import { FilterDatasetsComponent } from './_pages/filter-datasets/filter-dataset import { ReactiveBackgroundComponent } from './_elements/reactive-background/reactive-background.component'; import { ItemModelComponent } from './_elements/item-model/item-model.component'; import { AnnvisualComponent } from './_elements/annvisual/annvisual.component'; +import { ExperimentComponent } from './experiment/experiment.component'; +import { LoadingComponent } from './_elements/loading/loading.component'; +import { ModelLoadComponent } from './_elements/model-load/model-load.component'; +import { AlertDialogComponent } from './_modals/alert-dialog/alert-dialog.component'; +import { AddNewDatasetComponent } from './_elements/add-new-dataset/add-new-dataset.component'; @NgModule({ declarations: [ AppComponent, DatasetLoadComponent, - AddModelComponent, LoginModalComponent, RegisterModalComponent, HomeComponent, @@ -67,7 +70,12 @@ import { AnnvisualComponent } from './_elements/annvisual/annvisual.component'; FilterDatasetsComponent, ReactiveBackgroundComponent, ItemModelComponent, - AnnvisualComponent + AnnvisualComponent, + ExperimentComponent, + LoadingComponent, + ModelLoadComponent, + AlertDialogComponent, + AddNewDatasetComponent ], imports: [ BrowserModule, @@ -84,6 +92,8 @@ import { AnnvisualComponent } from './_elements/annvisual/annvisual.component'; Ng2SearchPipeModule, ], providers: [], - bootstrap: [AppComponent] + bootstrap: [AppComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + entryComponents: [AlertDialogComponent] }) export class AppModule { } diff --git a/frontend/src/app/_pages/add-model/add-model.component.css b/frontend/src/app/experiment/experiment.component.css index 6d961287..ee4b0448 100644 --- a/frontend/src/app/_pages/add-model/add-model.component.css +++ b/frontend/src/app/experiment/experiment.component.css @@ -3,6 +3,7 @@ padding-top: 30px; padding-bottom: 20px; } + #header h1 { font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; text-align: center; @@ -21,15 +22,18 @@ background-color: #003459; color: white; } + .btnType2 { background-color: white; color: #003459; border-color: #003459; } + .selectedDatasetClass { /*border-color: 2px solid #003459;*/ background-color: lightblue; } + ul li:hover { background-color: lightblue; }
\ No newline at end of file diff --git a/frontend/src/app/experiment/experiment.component.html b/frontend/src/app/experiment/experiment.component.html new file mode 100644 index 00000000..8d2c86b3 --- /dev/null +++ b/frontend/src/app/experiment/experiment.component.html @@ -0,0 +1,201 @@ +<div id="header"> + <h1>Napravite svoju veštačku neuronske mrežu</h1> +</div> +<div id="wrapper"> + <div id="container" class="container p-5" style="background-color: white; min-height: 100%;"> + + + <h2>1. Izvor podataka</h2> + <app-dataset-load></app-dataset-load> + + + <h2>2. Preprocesiranje</h2> + <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.header; let i = index"> + <input class="form-check-input" type="checkbox" value="{{item}}" id="cb_{{item}}" + name="cbsNew" [checked]="this.selectedOutputColumnVal != item" + [disabled]="this.selectedOutputColumnVal == item"> + <label class="form-check-label" for="cb_{{item}}"> + {{item}} + </label> + </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.header; let i = index"> + <input class="form-check-input" type="radio" value="{{item}}" id="rb_{{item}}" name="rbsNew" + (change)="this.selectedOutputColumnVal = item"> + <label class="form-check-label" for="rb_{{item}}"> + {{item}} + </label> + </div> + </div> + </div> + </div> + </div> + <h3>Popunjavanje nedostajućih vrednosti:</h3> + <div class="form-check" *ngIf="selectedDataset"> + <input type="radio" [(ngModel)]="experiment.nullValues" [value]="NullValueOptions.DeleteRows" + class="form-check-input" value="deleteRows" name="fillMissing" id="delRows" checked + data-bs-toggle="collapse" data-bs-target="#fillMissingCustom.show"> + <label 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</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 + 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 id="columnReplacers"> + <div *ngFor="let column of selectedDataset.columnInfo; let i = index" class="my-3"> + <div *ngIf="getInputById('cb_'+column).checked" class=""> + <span class="w-20 mx-3"> + {{column.name}} <span class="small" style="color:gray;">( + <!--{{//column.numNulls}}--> + TODO BROJ null) + </span> + </span> + + <label *ngIf="column.numNull <= 0" class="text-center form-control mx-3 text-secondary"> + Ova kolona nema + nedostajućih + vrednosti. + </label> + + <div *ngIf="column.numNull > 0" class="d-flex flex-row justify-content-end"> + <!-- ngIf colummn.numNulls == 0 --> + <div class="flex-grow-3 mx-3 me-auto"> + <div class="input-group"> + <div class="input-group-prepend"> + <label [for]="'fillCol_'+column.name" class="form-control"> + Zameni + <input type="radio" [id]="'fillCol_'+column.name" + [name]="'delOp_'+column.name"> + </label> + </div> + <input type="text" class="form-control" [id]="'fillText_'+column.name" + (keyup)="checkFillColRadio(column.name)" + placeholder="Unesi vrednost..."> + + <div class="input-group-append"> + <select [id]="'replaceOptions'+i" class="form-control btn-primary" + *ngIf="column.isNumber" (change)="replace($event);"> + <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.numNull > 0" + (change)="replace($event);"> + <option *ngFor="let option of column.uniqueValues" [value]="option"> + {{ option }} + </option> + </select> + </div> + </div> + </div> + + <div class="flex-shrink-1 mx-3"> + <div class="input-group"> + <label class="form-control" [for]="'delCol_'+column.name">Izbriši + kolonu + <input type="radio" [id]="'delCol_'+column" + [name]="'delOp_'+column.name" + (change)="emptyFillTextInput(column.name)"></label> + </div> + </div> + + <div class="flex-shrink-1 mx-3"> + <div class="input-group"> + <label class="form-control" [for]="'delRows_'+column.name">Izbriši + redove + <input type="radio" [id]="'delRows_'+column.name" + [name]="'delOp_'+column.name" checked + (change)="emptyFillTextInput(column.name)"></label> + </div> + </div> + </div> + </div> + </div> + <div *ngFor="let column of selectedDataset.columnInfo; let i = index" class="my-3"> + <div class="input-group row" *ngIf="getInputById('rb_'+column.name).checked"> + <span class="input-group-text col-2 text-center"> + {{column}} <span class="small" style="color:gray;">(br + null)</span> + </span> + <label *ngIf="true" class="form-control">Ova + kolona nema nedostajućih vrednosti.</label> + + <input *ngIf="true" type="radio" [id]="'fillCol_'+column.name" class="col-1 mt-2" + [name]="'delOp_'+column.name"> + <select [id]="'replaceOptions'+i" class="form-control col-2" *ngIf="column.isNumber" + (change)="replace($event);"> + <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 col-2" *ngIf="!column.isNumber" + (change)="replace($event);"> + <option *ngFor="let option of []" [value]="option"> + <!--/*arrayColumn(datasetFile, i)*/--> + {{ option }} + </option> + </select> + <input *ngIf="true" type="text" class="form-control col-1" + [id]="'fillText_'+column.name" (keyup)="checkFillColRadio(column.name)" + placeholder="Unesi vrednost..."> + + <label *ngIf="true" class="form-control col-2" [for]="'delCol_'+column" .name>Izbriši + kolonu + <input type="radio" [id]="'delCol_'+column.name" [name]="'delOp_'+column.name" + (change)="emptyFillTextInput(column.name)"></label> + <label *ngIf="true" class="form-control col-2" [for]="'delRows_'+column.name">Izbriši + redove + <input type="radio" [id]="'delRows_'+column.name" [name]="'delOp_'+column.name" + (change)="emptyFillTextInput(column.name)" checked></label> + </div> + </div> + + </div> + </div> + </div> + </div> + + + <h2>3. Podešavanja mreže</h2> + <app-model-load></app-model-load> + + + <h2>4. Treniraj model</h2> + <button class="btn btn-lg col-4" style="background-color:#003459; color:white;" (click)="trainModel();">Treniraj + model</button> + <h3>Rezultati treniranja</h3> + <div class="m-3" *ngIf="trainingResult"> + <h2 class="my-2">Rezultati treniranja:</h2> + <p> + {{trainingResult}} + </p> + </div> + </div> +</div>
\ No newline at end of file diff --git a/frontend/src/app/experiment/experiment.component.spec.ts b/frontend/src/app/experiment/experiment.component.spec.ts new file mode 100644 index 00000000..fd2bbd30 --- /dev/null +++ b/frontend/src/app/experiment/experiment.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExperimentComponent } from './experiment.component'; + +describe('ExperimentComponent', () => { + let component: ExperimentComponent; + let fixture: ComponentFixture<ExperimentComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ExperimentComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ExperimentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/experiment/experiment.component.ts b/frontend/src/app/experiment/experiment.component.ts new file mode 100644 index 00000000..2309dcd7 --- /dev/null +++ b/frontend/src/app/experiment/experiment.component.ts @@ -0,0 +1,118 @@ +import { Component, OnInit } from '@angular/core'; +import Experiment, { NullValReplacer, NullValueOptions, ReplaceWith } from '../_data/Experiment'; +import Model from '../_data/Model'; +import Dataset from '../_data/Dataset'; +import { ModelsService } from '../_services/models.service'; +import Shared from '../Shared'; + +@Component({ + selector: 'app-experiment', + templateUrl: './experiment.component.html', + styleUrls: ['./experiment.component.css'] +}) +export class ExperimentComponent implements OnInit { + + experiment: Experiment = new Experiment(); + selectedModel?: Model; + selectedDataset?: Dataset; + trainingResult: any; // any za sad, promeni kasnije + + NullValueOptions = NullValueOptions; + ReplaceWith = ReplaceWith; + Object = Object; + + selectedOutputColumnVal: string = ''; + + constructor(private models: ModelsService) { } + + ngOnInit(): void { + } + + getInputById(id: string): HTMLInputElement { + return document.getElementById(id) as HTMLInputElement; + } + + arrayColumn = (arr: any[][], n: number) => [...this.dropEmptyString(new Set(arr.map(x => x[n])))]; + + dropEmptyString(set: Set<any>): Set<string> { + if (set.has("")) + set.delete(""); + if (set.has(null)) + set.delete(null); + if (set.has(undefined)) + set.delete(undefined); + return set; + } + + emptyFillTextInput(colName: string) { + (<HTMLInputElement>document.getElementById("fillText_" + colName)).value = ""; + } + + checkFillColRadio(colName: string) { + (<HTMLInputElement>document.getElementById("fillCol_" + colName)).checked = true; + } + + replace(event: Event) { + let option = (<HTMLInputElement>event.target).value; + // TODO + } + + getNullValuesReplacersArray()/*: NullValReplacer[]*/ { + let array: NullValReplacer[] = []; + + // TODO ispravi + /*if (this.datasetFile) { + + if (this.newModel.nullValues == NullValueOptions.Replace) { + + for (let i = 0; i < this.datasetFile[0].length; i++) { + let column = this.datasetFile[0][i]; + + if (this.calculateSumOfNullValuesInCol(column) > 0) { //ako kolona nema null vrednosti, ne dodajemo je u niz + if ((<HTMLInputElement>document.getElementById("delCol_" + column)).checked) { + array.push({ + column: column, + option: NullValueOptions.DeleteColumns, + value: "" + }); + } + else if ((<HTMLInputElement>document.getElementById("delRows_" + column)).checked) { + array.push({ + column: column, + option: NullValueOptions.DeleteRows, + value: "" + }); + } + else if (((<HTMLInputElement>document.getElementById("fillCol_" + column)).checked)) { + array.push({ + column: column, + option: NullValueOptions.Replace, + value: (<HTMLInputElement>document.getElementById("fillText_" + column)).value + }); + } + } + } + } + } + //console.log(array); + return array;*/ + } + + trainModel() { + this.trainingResult = undefined; + console.log('Training model...', this.selectedModel); + if (!this.selectedDataset) { + Shared.openDialog('Greška', 'Izvor podataka nije izabran!'); + return; + } + // TODO proveri nullValues + if (!this.selectedModel) { + Shared.openDialog('Greška', 'Model nije izabran!'); + return; + } + this.models.trainModel(this.selectedModel).subscribe((response: any) => { + console.log('Train model complete!', response); + this.trainingResult = response; + }); + } +} diff --git a/frontend/src/app/material.module.ts b/frontend/src/app/material.module.ts index e85419ee..d16cef3d 100644 --- a/frontend/src/app/material.module.ts +++ b/frontend/src/app/material.module.ts @@ -1,18 +1,21 @@ import { NgModule } from '@angular/core'; - +import { CommonModule } from '@angular/common'; import { MatDialogModule } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; @NgModule({ exports: [ + CommonModule, MatDialogModule, MatButtonModule, MatFormFieldModule, MatInputModule, - MatCheckboxModule + MatCheckboxModule, + MatIconModule ] }) export class MaterialModule {}
\ No newline at end of file |