diff options
author | Danijel Andjelkovic <adanijel99@gmail.com> | 2022-04-06 13:14:00 +0200 |
---|---|---|
committer | Danijel Andjelkovic <adanijel99@gmail.com> | 2022-04-06 13:14:00 +0200 |
commit | 1b235bb4317477e673806ab9d2835a4dca48f88e (patch) | |
tree | ccc776c46e2f68e4285c7298f10ea8f591058e50 /frontend/src | |
parent | af3333a77e254b3268de38ec397921b43f357949 (diff) | |
parent | 480eb6a4e07b130129171d83ca9ba263dfba32c3 (diff) |
Merge branch 'dev' of http://gitlab.pmf.kg.ac.rs/igrannonica/neuronstellar into dev
# Conflicts:
# frontend/src/app/_pages/add-model/add-model.component.html
# frontend/src/app/_pages/add-model/add-model.component.ts
# frontend/src/app/app.module.ts
Diffstat (limited to 'frontend/src')
41 files changed, 726 insertions, 263 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/Model.ts b/frontend/src/app/_data/Model.ts index 1ad4fc6d..85b6db2b 100644 --- a/frontend/src/app/_data/Model.ts +++ b/frontend/src/app/_data/Model.ts @@ -45,22 +45,22 @@ export enum ProblemType { export enum Encoding { Label = 'label', OneHot = 'onehot', + Ordinal = 'ordinal', + Hashing = 'hashing', + Binary = 'binary', + BaseN = 'baseN' /* BackwardDifference = 'backward difference', - BaseN = 'baseN', - Binary = 'binary', CatBoost = 'cat boost', Count = 'count', GLMM = 'glmm', - Hashing = 'hashing', + Target = 'target', Helmert = 'helmert', JamesStein = 'james stein', LeaveOneOut = 'leave one out', MEstimate = 'MEstimate', - Ordinal = 'ordinal', Sum = 'sum', Polynomial = 'polynomial', - Target = 'target', WOE = 'woe', Quantile = 'quantile' */ @@ -104,7 +104,7 @@ export enum LossFunction { BinaryCrossEntropy = 'binary_crossentropy', SquaredHingeLoss = 'squared_hinge_loss', HingeLoss = 'hinge_loss', - // multi-class classiication loss functions + // multi-class classification loss functions CategoricalCrossEntropy = 'categorical_crossentropy', SparseCategoricalCrossEntropy = 'sparse_categorical_crosentropy', KLDivergence = 'kullback_leibler_divergence', 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.html b/frontend/src/app/_elements/annvisual/annvisual.component.html index f23022de..09251398 100644 --- a/frontend/src/app/_elements/annvisual/annvisual.component.html +++ b/frontend/src/app/_elements/annvisual/annvisual.component.html @@ -1,5 +1,5 @@ -<div style="text-align: center; width: 100%;" > +<div style="text-align: center; " > <button (click)="d3()" mat-raised-button color="primary">Prikaz veštačke neuronske mreže</button> - <div id="graph" align-items-center ></div> + <div id="graph" align-items-center style="width: 12rem;"></div> </div> 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/item-model/item-model.component.html b/frontend/src/app/_elements/item-model/item-model.component.html index 9466da01..c8c1a36d 100644 --- a/frontend/src/app/_elements/item-model/item-model.component.html +++ b/frontend/src/app/_elements/item-model/item-model.component.html @@ -6,9 +6,9 @@ <div class="card-body overflow-hidden"> <p class="card-text"> {{"Opis: "+ model.description}}<br> - {{"Datum kreiranja:" + model.dateCreated}}<br> - {{"Poslednje ažuriranje:" + model.lastUpdated}}<br> + {{"Datum kreiranja: " + model.dateCreated}}<br> + {{"Poslednje ažuriranje: " + model.lastUpdated}}<br> </p> - + <app-annvisual class="align-items-center" [model]="model" style="width: 12rem"></app-annvisual> </div> </div>
\ No newline at end of file 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 b6b5c9db..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/'+ '6244958a26cf2385bc29ba2c']); - //this.router.navigate(['predict'+this.predictor._id]); + this.router.navigate(['predict/'+ this.predictor._id]); } } 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/_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.css b/frontend/src/app/_pages/add-model/add-model.component.css index 6d961287..7f05af0f 100644 --- a/frontend/src/app/_pages/add-model/add-model.component.css +++ b/frontend/src/app/_pages/add-model/add-model.component.css @@ -32,4 +32,11 @@ } ul li:hover { background-color: lightblue; -}
\ No newline at end of file +} + +#divInputs { + margin-left: 20px; +} +#divOutputs { + margin-left: 20px; +} diff --git a/frontend/src/app/_pages/add-model/add-model.component.html b/frontend/src/app/_pages/add-model/add-model.component.html index 3d5fd7b1..179e9aea 100644 --- a/frontend/src/app/_pages/add-model/add-model.component.html +++ b/frontend/src/app/_pages/add-model/add-model.component.html @@ -25,42 +25,8 @@ <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 (selectedDatasetChangeEvent)="datasetHasChanged($event)"></app-dataset-load> - <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> <span id="selectInAndOuts"></span> <div diff --git a/frontend/src/app/_pages/add-model/add-model.component.ts b/frontend/src/app/_pages/add-model/add-model.component.ts index d47b24e6..ba8f7d01 100644 --- a/frontend/src/app/_pages/add-model/add-model.component.ts +++ b/frontend/src/app/_pages/add-model/add-model.component.ts @@ -5,9 +5,7 @@ import { DatasetLoadComponent } from 'src/app/_elements/dataset-load/dataset-loa 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'; @@ -18,11 +16,6 @@ import { CsvParseService } from 'src/app/_services/csv-parse.service'; }) export class AddModelComponent implements OnInit { - @ViewChild(DatasetLoadComponent) datasetLoadComponent?: DatasetLoadComponent; - @ViewChild(DatatableComponent) datatable?: DatatableComponent; - datasetLoaded: boolean = false; - selectedDatasetLoaded: boolean = false; - newModel: Model; ProblemType = ProblemType; @@ -71,8 +64,13 @@ export class AddModelComponent implements OnInit { (<HTMLInputElement>document.getElementById("btnMyDataset")).focus(); } + datasetHasChanged(selectedDataset: Dataset) { + this.selectedDataset = selectedDataset; + this.resetCbsAndRbs(); + this.refreshThreeNullValueRadioOptions(); + } - viewMyDatasetsForm() { + /*viewMyDatasetsForm() { this.showMyDatasets = true; this.resetSelectedDataset(); //this.datasetLoaded = false; @@ -82,7 +80,7 @@ export class AddModelComponent implements OnInit { this.showMyDatasets = false; this.resetSelectedDataset(); this.resetCbsAndRbs(); - } + }*/ addModel() { if (!this.showMyDatasets) @@ -174,7 +172,7 @@ export class AddModelComponent implements OnInit { 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."); + shared.openDialog("Neuspeo pokušaj!", "Model sa unetim nazivom već postoji u Vašoj kolekciji. Promenite naziv modela i nastavite sa kreiranim datasetom."); }); } } @@ -217,47 +215,47 @@ export class AddModelComponent implements OnInit { return false; } else if (this.newModel.inputColumns.length == 0) { - alert("Molimo Vas da izaberete ulaznu kolonu/kolone za mrežu."); + shared.openDialog("Neuspeo pokušaj!", "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."); + shared.openDialog("Neuspeo pokušaj!", "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."); + shared.openDialog("Neuspeo pokušaj!", "Izabrali ste istu kolonu (" + colName + ") kao ulaznu i izlaznu iz mreže. Korigujte izbor."); return false; } } return true;*/ } - selectThisDataset(dataset: Dataset) { + /*selectThisDataset(dataset: Dataset) { this.selectedDataset = dataset; - this.selectedDatasetLoaded = false; + //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 - }*/ + //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.selectedDatasetLoaded = true; this.scrollToNextForm(); } }); //this.datasetHasHeader = false; - } + }*/ scrollToNextForm() { (<HTMLSelectElement>document.getElementById("selectInAndOuts")).scrollIntoView({ @@ -267,7 +265,7 @@ export class AddModelComponent implements OnInit { }); } - resetSelectedDataset(): boolean { + /*resetSelectedDataset(): boolean { const temp = this.selectedDataset; this.selectedDataset = this.otherDataset; this.otherDataset = temp; @@ -275,7 +273,7 @@ export class AddModelComponent implements OnInit { this.datasetFile = this.otherDatasetFile; this.otherDatasetFile = tempFile; return true; - } + }*/ resetCbsAndRbs(): boolean { this.uncheckRbs(); this.checkAllCbs(); @@ -345,7 +343,7 @@ export class AddModelComponent implements OnInit { let colIndex = this.findColIndexByName(colName); let sumOfNulls = 0; - let startValue = (this.datasetLoadComponent?.dataset.hasHeader) ? 1 : 0; + let startValue = (this.selectedDataset!.hasHeader) ? 1 : 0; for (let i = startValue; i < this.datasetFile.length; i++) { if (this.datasetFile[i][colIndex] == "" || this.datasetFile[i][colIndex] == undefined) ++sumOfNulls; @@ -360,7 +358,7 @@ export class AddModelComponent implements OnInit { let sum = 0; let n = 0; - let startValue = (this.datasetLoadComponent?.dataset.hasHeader) ? 1 : 0; + let startValue = (this.selectedDataset!.hasHeader) ? 1 : 0; for (let i = startValue; i < this.datasetFile.length; i++) if (this.datasetFile[i][colIndex] != '') { sum += Number(this.datasetFile[i][colIndex]); @@ -467,9 +465,13 @@ export class AddModelComponent implements OnInit { arrayColumn = (arr: any[][], n: number) => [...this.dropEmptyString(new Set(arr.map(x => x[n])))]; - dropEmptyString(set: Set<string>): Set<string> { + 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; } 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..2e17201d 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,37 @@ -<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..eb5e32f8 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,9 @@ 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'; @Component({ selector: 'app-my-datasets', @@ -7,18 +11,47 @@ 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(); + + } +/* + 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!"); + } + }); + +} + + 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 e2533d89..4aebc1f2 100644 --- a/frontend/src/app/_pages/my-models/my-models.component.html +++ b/frontend/src/app/_pages/my-models/my-models.component.html @@ -8,12 +8,22 @@ <div class="row"> <div class="col-sm-4" style="margin-bottom: 10px;" *ngFor="let model of myModels"> <app-item-model [model]="model"></app-item-model> - <app-annvisual align-items-center [model]="model" style="width: 100%;"></app-annvisual> - <div style="width: 25%; margin: auto;"> - <button mat-raised-button color="primary" (click)="deleteThisModel(model)" style="margin-top: 3px; width: 100%;">Obriši</button> - - <button mat-raised-button color="primary" (click)="deleteThisModel(model)" style="margin-top: 3px; width: 100%;">Koristi</button> - </div> + + <div class="panel-footer row"><!-- panel-footer --> + <div class="col-xs-6 text-center"> + <div> + <button type="button" class="btn btn-default btn-lg" (click)="deleteThisModel(model)" mat-raised-button color="primary">Koristi + <span class="glyphicon glyphicon-search"></span> + </button> + <button type="button" class="btn btn-default btn-lg" mat-raised-button color="primary" (click)="deleteThisModel(model)">Obriši + <span class="glyphicon glyphicon-chevron-right"></span> + </button> + </div> + </div> + </div><!-- end panel-footer --> + + + </div> </div> <div class="text-center" *ngIf="this.myModels.length == 0" > 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.css b/frontend/src/app/_pages/predict/predict.component.css index e69de29b..dab059a5 100644 --- a/frontend/src/app/_pages/predict/predict.component.css +++ b/frontend/src/app/_pages/predict/predict.component.css @@ -0,0 +1,3 @@ +#wrapper { + color: #003459; +}
\ No newline at end of file diff --git a/frontend/src/app/_pages/predict/predict.component.html b/frontend/src/app/_pages/predict/predict.component.html index fe17c96d..13afa8e4 100644 --- a/frontend/src/app/_pages/predict/predict.component.html +++ b/frontend/src/app/_pages/predict/predict.component.html @@ -18,8 +18,25 @@ <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 > <!--input --> @@ -28,44 +45,20 @@ <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> </div> </div> - + <br> </div> <div> <label for="output" class="col-sm-2 col-form-label">Izlaz: <b>{{predictor.output}}</b></label> </div> - <br> - <br> - <br> - <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 3f431fff..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("Uspesno ste poslali preditor!"); + 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.html b/frontend/src/app/_pages/profile/profile.component.html index d082a003..557d69fd 100644 --- a/frontend/src/app/_pages/profile/profile.component.html +++ b/frontend/src/app/_pages/profile/profile.component.html @@ -30,12 +30,14 @@ <label class="small mb-1" for="inputPassword">Važeća lozinka</label> <input class="form-control" id="inputPassword" name="inputPassword" type="password" [(ngModel)]="this.oldPass" placeholder="Trenutna lozinka"> <small *ngIf="wrongPassBool" class="form-text text-danger">Neispravna lozinka.</small> + <small *ngIf="wrongOldPassBool" class="form-text text-danger">Pogrešan format.</small> </div> <!-- Form Group (new password)--> <div class="col-md-6"> <label class="small mb-1" for="inputNewPassword">Nova lozinka</label> <input class="form-control" id="inputNewPassword" name="inputNewPassword" type="password" [(ngModel)]="this.newPass1" placeholder="Ukucaj novu lozinku"> <small *ngIf="wrongNewPassBool" class="form-text text-danger">Lozinke se ne podudaraju.</small> + <small *ngIf="wrongNewPass1Bool" class="form-text text-danger">Pogrešan format.</small> </div> </div> @@ -52,6 +54,7 @@ <label class="small mb-1" for="inputNewPasswordAgain">Ponovo nova lozinka</label> <input class="form-control" id="inputNewPasswordAgain" name="inputNewPasswordAgain" type="password" [(ngModel)]="this.newPass2" placeholder="Ukucaj novu lozinku"> <small *ngIf="wrongNewPassBool" class="form-text text-danger">Lozinke se ne podudaraju.</small> + <small *ngIf="wrongNewPass2Bool" class="form-text text-danger">Pogrešan format.</small> </div> </div> </div> @@ -73,11 +76,13 @@ <div class="col-md-6"> <label class="small mb-1" for="inputUsername">Korisničko ime (kako će ostali korisnici videti tvoje ime)</label> <input class="form-control" id="inputUsername" name="inputUsername" type="text" [(ngModel)]="this.username"> + <small *ngIf="wrongUsernameBool" class="form-text text-danger">Pogrešan format.</small> </div> <!-- Form Group (email address)--> <div class="col-md-6"> <label class="small mb-1" for="inputEmailAddress">Email adresa</label> <input class="form-control" id="inputEmailAddress" name="inputEmailAddress" type="email" [(ngModel)]="this.email"> + <small *ngIf="wrongEmailBool" class="form-text text-danger">Pogrešan format.</small> </div> </div> @@ -87,11 +92,13 @@ <div class="col-md-6"> <label class="small mb-1" for="inputFirstName">Ime</label> <input class="form-control" id="inputFirstName" name="inputFirstName" type="text" [(ngModel)]="this.firstName"> + <small *ngIf="wrongFirstNameBool" class="form-text text-danger">Pogrešan format.</small> </div> <!-- Form Group (last name)--> <div class="col-md-6"> <label class="small mb-1" for="inputLastName">Prezime</label> <input class="form-control" id="inputLastName" name="inputLastName" type="text" [(ngModel)]="this.lastName"> + <small *ngIf="wrongLastNameBool" class="form-text text-danger">Pogrešan format.</small> </div> </div> diff --git a/frontend/src/app/_pages/profile/profile.component.ts b/frontend/src/app/_pages/profile/profile.component.ts index 3e9a0d11..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({ @@ -71,6 +72,9 @@ export class ProfileComponent implements OnInit { } saveInfoChanges() { + if (!(this.checkInfoChanges())) //nije prosao regex + return; + let editedUser: User = { _id: this.user._id, username: this.username, @@ -84,18 +88,20 @@ export class ProfileComponent implements OnInit { this.userInfoService.changeUserInfo(editedUser).subscribe((response: any) =>{ if (this.user.username != editedUser.username) { //promenio username, ide logout this.user = editedUser; - alert("Nakon promene korisničkog imena, moraćete ponovo da se ulogujete."); + this.resetInfo(); + shared.openDialog("Obaveštenje", "Nakon promene korisničkog imena, moraćete ponovo da se ulogujete."); this.authService.logOut(); this.router.navigate(['']); return; } + 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."); - (<HTMLSelectElement>document.getElementById("inputUsername")).focus(); + 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(); } @@ -103,38 +109,41 @@ export class ProfileComponent implements OnInit { } savePasswordChanges() { + this.wrongPassBool = false; + this.wrongNewPassBool = false; + + if (!(this.checkPasswordChanges())) //nije prosao regex + return; + if (this.newPass1 == '' && this.newPass2 == '') //ne zeli da promeni lozinku return; - console.log("zeli da promeni lozinku"); + //console.log("zeli da promeni lozinku"); if (this.newPass1 != this.newPass2) { //netacno ponovio novu lozinku this.wrongNewPassBool = true; this.resetNewPassInputs(); - console.log("Netacno ponovljena lozinka"); + //console.log("Netacno ponovljena lozinka"); return; } - this.wrongPassBool = false; - this.wrongNewPassBool = false; - let passwordArray: string[] = [this.oldPass, this.newPass1]; this.userInfoService.changeUserPassword(passwordArray).subscribe((response: any) => { - console.log("PROMENIO LOZINKU"); + //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) => { console.log("error poruka: ", error.error); if (error.error == 'Wrong old password!') { this.wrongPassBool = true; - (<HTMLSelectElement>document.getElementById("inputPassword")).focus(); + //(<HTMLSelectElement>document.getElementById("inputPassword")).focus(); 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(); + //(<HTMLSelectElement>document.getElementById("inputNewPassword")).focus(); return; } }); @@ -161,5 +170,95 @@ export class ProfileComponent implements OnInit { shared.photoId = this.photoId; } + checkPasswordChanges() : boolean { + this.passwordValidation(); + + if (!(this.wrongOldPassBool || this.wrongNewPass1Bool || this.wrongNewPass2Bool)) + return true; + return false; + } + checkInfoChanges() : boolean { + this.firstName = this.firstName.trim(); + this.lastName = this.lastName.trim(); + this.username = this.username.trim(); + this.email = this.email.trim(); + + this.firstNameValidation(); + this.lastNameValidation(); + this.usernameValidation(); + this.emailValidation(); + + if (!(this.wrongUsernameBool || this.wrongEmailBool || this.wrongFirstNameBool || this.wrongLastNameBool)) + return true; + return false; + } + + isCorrectName(element: string): boolean { + if (this.pattName.test(element) && !(this.pattTwoSpaces.test(element)) && (element.length >= 1 && element.length <= 30)) + return true; + return false; + } + isCorrectUsername(element: string): boolean { + if (this.pattUsername.test(element) && !(this.pattTwoSpaces.test(element)) && (element.length >= 1 && element.length <= 30)) + return true; + return false; + } + isCorrectEmail(element: string): boolean { + if (this.pattEmail.test(element.toLowerCase()) && element.length <= 320) + return true; + return false; + } + isCorrectPassword(element: string): boolean { + if (this.pattPassword.test(element)) + return true; + return false; + } + firstNameValidation() { + if (this.isCorrectName(this.firstName) == true) { + this.wrongFirstNameBool = false; + return; + } + //(<HTMLSelectElement>document.getElementById('firstName')).focus(); + this.wrongFirstNameBool = true; + } + lastNameValidation() { + if (this.isCorrectName(this.lastName) == true) { + this.wrongLastNameBool = false; + return; + } + //(<HTMLSelectElement>document.getElementById('lastName')).focus(); + this.wrongLastNameBool = true; + } + usernameValidation() { + if (this.isCorrectUsername(this.username) == true) { + this.wrongUsernameBool = false; + return; + } + //(<HTMLSelectElement>document.getElementById('username-register')).focus(); + this.wrongUsernameBool = true; + } + emailValidation() { + if (this.isCorrectEmail(this.email) == true) { + this.wrongEmailBool = false; + return; + } + //(<HTMLSelectElement>document.getElementById('email')).focus(); + this.wrongEmailBool = true; + } + passwordValidation() { + if (this.isCorrectPassword(this.oldPass) && this.isCorrectPassword(this.newPass1) && this.newPass1 == this.newPass2) { + this.wrongOldPassBool = false; + this.wrongNewPass1Bool = false; + this.wrongNewPass2Bool = false; + return; + } + this.oldPass = ''; + this.newPass1 = ''; + this.newPass2 = ''; + //(<HTMLSelectElement>document.getElementById('pass1')).focus(); + this.wrongOldPassBool = true; + this.wrongNewPass1Bool = true; + this.wrongNewPass2Bool = true; + } } 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.module.ts b/frontend/src/app/app.module.ts index d701f9d7..04523989 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'; @@ -42,6 +42,8 @@ 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: [ @@ -73,7 +75,9 @@ import { ModelLoadComponent } from './_elements/model-load/model-load.component' AnnvisualComponent, ExperimentComponent, LoadingComponent, - ModelLoadComponent + ModelLoadComponent, + AlertDialogComponent, + AddNewDatasetComponent ], imports: [ BrowserModule, @@ -90,6 +94,8 @@ import { ModelLoadComponent } from './_elements/model-load/model-load.component' Ng2SearchPipeModule, ], providers: [], - bootstrap: [AppComponent] + bootstrap: [AppComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + entryComponents: [AlertDialogComponent] }) export class AppModule { } 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 |