This commit is contained in:
kushiji 2020-06-23 14:50:35 +02:00
commit c6212fd1a3
28 changed files with 195 additions and 405 deletions

View File

@ -0,0 +1,20 @@
import { Post } from '../_interfaces/post';
/**
* Returns hierearchy-like array specified for D3 graph
*/
export default function makeHierarchy(data: Post[]) {
const tree: Post[] = [];
const childOf: any = {};
data.forEach((element) => {
const { id, parent } = element;
childOf[id] = childOf[id] || [];
element.children = childOf[id];
if (parent !== '0') {
(childOf[parent] = childOf[parent] || []).push(element);
} else {
tree.push(element);
}
});
return tree;
}

View File

@ -4,7 +4,7 @@ export interface CustomForumData {
discussions: [ discussions: [
{ {
title: string; title: string;
id: string; id: number;
} }
]; ];
} }

View File

@ -33,7 +33,6 @@ export class SharedDataService {
public addParagraph(paragraph: LabelData) { public addParagraph(paragraph: LabelData) {
const paragraphs = this.paragraphsBS.getValue(); const paragraphs = this.paragraphsBS.getValue();
console.log(paragraphs);
if (paragraphs.find((p) => p.para_id === paragraph.para_id)) { if (paragraphs.find((p) => p.para_id === paragraph.para_id)) {
const updatedParagraphs = paragraphs.map((element) => { const updatedParagraphs = paragraphs.map((element) => {
if (element.para_id === paragraph.para_id) { if (element.para_id === paragraph.para_id) {

View File

@ -0,0 +1,41 @@
import { Injectable } from '@angular/core';
import { NbMenuItem } from '@nebular/theme';
import { BehaviorSubject } from 'rxjs';
/* Service for handling sidebar menu items */
@Injectable()
export class SidebarItemsService {
constructor() {}
/* Initial values */
private menuItemsBS: BehaviorSubject<NbMenuItem[]> = new BehaviorSubject<
NbMenuItem[]
>([
{
title: 'Dyskusje',
link: '/view/discussions',
},
{
title: 'Wizualizacje',
},
]);
/**
*
* @param items children to add
* @param index 0 for disucssions, 1 for visualizations
*/
public addMenuItem(items: NbMenuItem[], index: number) {
const fetchedMenuItems = this.menuItemsBS.getValue();
fetchedMenuItems[index].children = items;
this.menuItemsBS.next(fetchedMenuItems);
}
/**
* Return observable with current sidebar menu items
*/
public getMenuItems() {
return this.menuItemsBS.asObservable();
}
}

View File

@ -16,6 +16,7 @@ import {
import { FrontPageModule } from './front-page/front-page.module'; import { FrontPageModule } from './front-page/front-page.module';
import { SharedDataService } from './_services/shared-data.service'; import { SharedDataService } from './_services/shared-data.service';
import { SidebarItemsService } from './_services/sidebar-items.service';
@NgModule({ @NgModule({
declarations: [AppComponent], declarations: [AppComponent],
@ -31,6 +32,6 @@ import { SharedDataService } from './_services/shared-data.service';
FrontPageModule, FrontPageModule,
], ],
bootstrap: [AppComponent], bootstrap: [AppComponent],
providers: [SharedDataService, NbSidebarService], providers: [SharedDataService, NbSidebarService, SidebarItemsService],
}) })
export class AppModule {} export class AppModule {}

View File

@ -6,6 +6,7 @@
*ngFor="let item of data.discussions" *ngFor="let item of data.discussions"
class="picker-container__discussion" class="picker-container__discussion"
[accent]="getRandomColor()" [accent]="getRandomColor()"
(click)="onDiscussionClick(item.id)"
> >
<h3 class="picker-container__disc-title">{{ item.title }}</h3> <h3 class="picker-container__disc-title">{{ item.title }}</h3>
</nb-card> </nb-card>

View File

@ -9,6 +9,7 @@ import { Colors } from '../../_types/color';
import { CustomForumData } from '../../_interfaces/customforumdata'; import { CustomForumData } from '../../_interfaces/customforumdata';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { NbMenuItem } from '@nebular/theme'; import { NbMenuItem } from '@nebular/theme';
import { Router } from '@angular/router';
// interface CustomForumData { // interface CustomForumData {
// id: string; // id: string;
@ -32,24 +33,16 @@ export class DiscussionChooserComponent implements OnInit, OnDestroy {
colors: Colors[] = ['primary', 'danger', 'info', 'success', 'warning']; colors: Colors[] = ['primary', 'danger', 'info', 'success', 'warning'];
private dataSub: Subscription; private dataSub: Subscription;
constructor(private sharedDataService: SharedDataService) {} constructor(
private sharedDataService: SharedDataService,
private router: Router
) {}
ngOnInit(): void { ngOnInit(): void {
this.dataSub = this.sharedDataService.getData().subscribe((res) => { this.dataSub = this.sharedDataService.getData().subscribe((res) => {
if (res) { if (res) {
const parsedObj = JSON.parse(res as string); const parsedObj = JSON.parse(res as string);
this.data = parsedObj as CustomForumData; this.data = parsedObj as CustomForumData;
const fetchedDiscussions = this.data.discussions.map(
(element): NbMenuItem => {
return {
title: element.title,
link: `/view/forum/${element.id}`,
};
}
);
this.sharedDataService.setDiscussions(fetchedDiscussions);
} }
}); });
} }
@ -61,4 +54,8 @@ export class DiscussionChooserComponent implements OnInit, OnDestroy {
getRandomColor(): Colors { getRandomColor(): Colors {
return this.colors[Math.floor(Math.random() * this.colors.length)]; return this.colors[Math.floor(Math.random() * this.colors.length)];
} }
onDiscussionClick(id: number) {
this.router.navigate([`/view/forum/${id}`]);
}
} }

View File

@ -3,6 +3,14 @@
<button nbButton status="primary" class="discussion-viewer__save-button"> <button nbButton status="primary" class="discussion-viewer__save-button">
Zapisz zmiany Zapisz zmiany
</button> </button>
<button
nbButton
status="success"
class="discussion-viewer__back-button"
(click)="onBackButtonClick()"
>
Wróć do wyboru dyskusji
</button>
</div> </div>
<div class="discussion-viewer__posts-container"> <div class="discussion-viewer__posts-container">
<nb-card *ngFor="let item of data"> <nb-card *ngFor="let item of data">

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { GetDiscussionService } from '../../_services/get-discussion.service'; import { GetDiscussionService } from '../../_services/get-discussion.service';
import { PredictedPost } from '../../_interfaces/predictedposts'; import { PredictedPost } from '../../_interfaces/predictedposts';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
@ -17,11 +17,12 @@ export class DiscussionViewerComponent implements OnInit {
constructor( constructor(
private getDiscussionService: GetDiscussionService, private getDiscussionService: GetDiscussionService,
private router: ActivatedRoute private activatedRouter: ActivatedRoute,
private router: Router
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.paramSub = this.router.params.subscribe((params) => { this.paramSub = this.activatedRouter.params.subscribe((params) => {
this.id = params.id; this.id = params.id;
this.discussionSub = this.getDiscussionService this.discussionSub = this.getDiscussionService
.getDiscussion(this.id) .getDiscussion(this.id)
@ -31,4 +32,8 @@ export class DiscussionViewerComponent implements OnInit {
}); });
}); });
} }
onBackButtonClick() {
this.router.navigate(['/view/discussions/']);
}
} }

View File

@ -1,5 +1,5 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { Routes, RouterModule, Router } from '@angular/router'; import { Routes, RouterModule } from '@angular/router';
import { MainViewComponent } from './main-view.component'; import { MainViewComponent } from './main-view.component';
import { DiscussionChooserComponent } from './discussion-chooser/discussion-chooser.component'; import { DiscussionChooserComponent } from './discussion-chooser/discussion-chooser.component';
import { DiscussionViewerComponent } from './discussion-viewer/discussion-viewer.component'; import { DiscussionViewerComponent } from './discussion-viewer/discussion-viewer.component';
@ -17,6 +17,13 @@ const routes: Routes = [
path: 'forum/:id', path: 'forum/:id',
component: DiscussionViewerComponent, component: DiscussionViewerComponent,
}, },
{
path: 'visualize/:id',
loadChildren: () =>
import('./visualize-forum/visualize-forum.module').then(
(m) => m.VisualizeForumModule
),
},
{ {
path: '', path: '',
redirectTo: 'discussions', redirectTo: 'discussions',

View File

@ -1,26 +1,28 @@
import { Component, AfterContentInit } from '@angular/core'; import { Component, OnInit, AfterContentInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { SharedDataService } from '../_services/shared-data.service'; import { SharedDataService } from '../_services/shared-data.service';
import { NbSidebarService, NbMenuItem } from '@nebular/theme'; import { NbSidebarService, NbMenuItem } from '@nebular/theme';
import { Subscription } from 'rxjs';
import { SidebarItemsService } from '../_services/sidebar-items.service';
import { CustomForumData } from '../_interfaces/customforumdata';
@Component({ @Component({
selector: 'app-main-view', selector: 'app-main-view',
templateUrl: './main-view.component.html', templateUrl: './main-view.component.html',
styleUrls: ['./main-view.component.scss'], styleUrls: ['./main-view.component.scss'],
}) })
export class MainViewComponent implements AfterContentInit { export class MainViewComponent implements OnInit, AfterContentInit {
items: NbMenuItem[] = [ items: NbMenuItem[];
{ menuItemsSub: Subscription;
title: 'Dyskusje', dataSub: Subscription;
link: '/view/discussions',
},
];
constructor( constructor(
private sidebarService: NbSidebarService, private sidebarService: NbSidebarService,
private sharedDataService: SharedDataService, private sharedDataService: SharedDataService,
private router: Router private router: Router,
private menuItemsService: SidebarItemsService
) {} ) {}
toggleSidebar() { toggleSidebar() {
@ -31,15 +33,35 @@ export class MainViewComponent implements AfterContentInit {
this.router.navigate(['/']); this.router.navigate(['/']);
} }
ngAfterContentInit(): void { ngOnInit(): void {
this.sharedDataService.getDiscussions().subscribe((res) => { this.dataSub = this.sharedDataService.getData().subscribe((result) => {
this.items[0].children = res; if (result) {
const parsedObj = JSON.parse(result as string) as CustomForumData;
const fetchedDiscussions = parsedObj.discussions.map(
(element): NbMenuItem => {
return {
title: element.title,
link: `/view/forum/${element.id}`,
};
}
);
const fetchedVisualizations = parsedObj.discussions.map(
(element): NbMenuItem => {
return {
title: element.title,
link: `/view/visualize/${element.id}`,
};
}
);
this.menuItemsService.addMenuItem(fetchedDiscussions, 0);
this.menuItemsService.addMenuItem(fetchedVisualizations, 1);
}
}); });
} }
// onActivate(event: any) { ngAfterContentInit(): void {
// event.discussions.subscribe((res: NbMenuItem[]) => { this.menuItemsService.getMenuItems().subscribe((result) => {
// this.items[0].children = res; this.items = result;
// }); });
// } }
} }

View File

@ -6,6 +6,11 @@
> >
{{ message }} {{ message }}
</span> </span>
<div class="discussion-viewer__select">
<label class="label">
Wybierz etykietę
</label>
<nb-select <nb-select
placeholder="Wybierz etykietę" placeholder="Wybierz etykietę"
(selectedChange)="fetchLabel($event, paragraphId)" (selectedChange)="fetchLabel($event, paragraphId)"
@ -15,4 +20,5 @@
_label _label
}}</nb-option> }}</nb-option>
</nb-select> </nb-select>
</div>
</div> </div>

View File

@ -55,4 +55,8 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
&__select {
display: flex;
flex-flow: column;
}
} }

View File

@ -0,0 +1,16 @@
import { Routes, RouterModule } from '@angular/router';
import { VisualizeForumComponent } from './visualize-forum.component';
import { NgModule } from '@angular/core';
const routes: Routes = [
{
path: '',
component: VisualizeForumComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class VisualizeForumRoutingModule {}

View File

@ -0,0 +1,13 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'visualize-forum',
templateUrl: './visualize-forum.component.html',
styleUrls: ['./visualize-forum.component.scss'],
})
export class VisualizeForumComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {}
}

View File

@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { VisualizeForumComponent } from './visualize-forum.component';
import { VisualizeForumRoutingModule } from './visualize-forum-routing.module';
@NgModule({
declarations: [VisualizeForumComponent],
imports: [CommonModule, VisualizeForumRoutingModule],
})
export class VisualizeForumModule {}

View File

@ -1,27 +0,0 @@
<!-- <nb-card>
<nb-card-body>
<p>Aby skorzystać z aplikacji dodaj plik</p>
<p>{{ fileName }}</p>
<div>
<button nbButton status="primary" (click)="openFileDialog($event)">
Wybierz plik
</button>
</div>
<div>
<button nbButton status="success" (click)="sendFile($event)">
Wyślij!
</button>
</div>
</nb-card-body>
</nb-card> -->
<!-- This is some kind of magic -->
<button nbButton status="primary" (click)="openFileDialog($event)">
Wybierz plik
</button>
<input
id="input-for-file"
type="file"
(change)="fetchAndEmitFile($event)"
accept=".xml"
/>

View File

@ -1,3 +0,0 @@
input[type="file"] {
display: none;
}

View File

@ -1,25 +0,0 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { OpenFileButtonComponent } from './open-file-button.component';
describe('OpenFileButtonComponent', () => {
let component: OpenFileButtonComponent;
let fixture: ComponentFixture<OpenFileButtonComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ OpenFileButtonComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OpenFileButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,27 +0,0 @@
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-open-file-button',
templateUrl: './open-file-button.component.html',
styleUrls: ['./open-file-button.component.scss'],
})
export class OpenFileButtonComponent implements OnInit {
constructor() {}
@Output()
selectedFile: EventEmitter<File> = new EventEmitter<File>();
ngOnInit(): void {}
openFileDialog(event: any): void {
event.preventDefault();
const element: HTMLElement = document.getElementById(
'input-for-file'
) as HTMLElement;
element.click();
}
fetchAndEmitFile(event: any) {
this.selectedFile.emit(event.target.files[0]);
}
}

View File

@ -1,16 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { SendDataService } from './send-data.service';
describe('SendDataService', () => {
let service: SendDataService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SendDataService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -1,23 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class SendDataService {
constructor(private http: HttpClient) {}
postFile(file: File): Observable<File> {
const formData: FormData = new FormData();
formData.append('file', file, file.name);
const requestOptions = {
responseType: 'text' as 'json',
};
return this.http.post<File>(
'http://127.0.0.1:8000/prototype/form/',
formData,
requestOptions
);
}
}

View File

@ -1,115 +0,0 @@
<nb-layout>
<nb-layout-header></nb-layout-header>
<nb-layout-column class="column">
<section class="column__header">
<div>
<h1>Narzędzie do komputerowej analizy dyskusji na forum</h1>
<button
status="success"
size="large"
nbButton
(click)="scrollTo(xmltutorial)"
>
Jak wyeksportować pliki XML?
</button>
</div>
<div>
<p class="h6">Załaduj plik aby rozpocząć</p>
<app-open-file-button
(selectedFile)="parseFile($event)"
></app-open-file-button>
<button nbButton status="success" (click)="sendFile($event)">
Wyślij!
</button>
</div>
</section>
<section class="properties">
<div class="properties__element">
<div class="properties__icon">
<nb-icon icon="info-outline"></nb-icon>
</div>
<h2>Lorem Ipsum</h2>
<p>
Nasza aplikacja pozwala na szczegółowe interpretowanie odpowiedzi z
wątków na forach platform e-learningowych, takich jak Moodle i
Edumatic
</p>
</div>
<div class="properties__element">
<div class="properties__icon">
<nb-icon icon="person-outline"></nb-icon>
</div>
<h2>Lorem Ipsum</h2>
<p>
System opiera się na przetwarzaniu języka naturalnego, które umożliwia
coś tam
</p>
</div>
<div class="properties__element">
<div class="properties__icon">
<nb-icon icon="trending-up-outline"></nb-icon>
</div>
<h2>Lorem Ipsum</h2>
<p>
Dzięki ogromnej gamie możliwości wizualizacji danych, użytkownik może
zrobić coś tam
</p>
</div>
</section>
<div class="column__wave">
<img src="../../assets/wave.svg" />
</div>
<section class="column__instruction">
<h1 class="text-alternate" #xmltutorial>Jak wyeksportować pliki XML?</h1>
<nb-card class="column__instruction-card">
<nb-stepper orientation="vertical">
<nb-step label="Krok 1">
<h5>Krok #1</h5>
<p class="paragraph">
Proin varius accumsan semper. Praesent consequat tincidunt
sagittis. Curabitur egestas sem a ipsum bibendum, sit amet
fringilla orci efficitur. Nam bibendum lectus ut viverra
tristique. Fusce eu pulvinar magna, quis viverra ex. Lorem ipsum
dolor sit amet, consectetur adipiscing elit. Praesent metus
turpis, commodo vel placerat quis, lobortis in ligula.
</p>
<button nbButton disabled nbStepperNext>Poprzedni krok</button>
<button nbButton nbStepperNext>Następny krok</button>
</nb-step>
<nb-step label="Krok 2">
<h5>Krok #2</h5>
<p class="paragraph">
Proin varius accumsan semper. Praesent consequat tincidunt
sagittis. Curabitur egestas sem a ipsum bibendum, sit amet
fringilla orci efficitur. Nam bibendum lectus ut viverra
tristique. Fusce eu pulvinar magna, quis viverra ex. Lorem ipsum
dolor sit amet, consectetur adipiscing elit. Praesent metus
turpis, commodo vel placerat quis, lobortis in ligula.
</p>
<button nbButton nbStepperPrevious>Poprzedni krok</button>
<button nbButton nbStepperNext>Następny krok</button>
</nb-step>
<nb-step label="Krok 3">
<h5>Krok #3</h5>
<p class="paragraph">
Proin varius accumsan semper. Praesent consequat tincidunt
sagittis. Curabitur egestas sem a ipsum bibendum, sit amet
fringilla orci efficitur. Nam bibendum lectus ut viverra
tristique. Fusce eu pulvinar magna, quis viverra ex. Lorem ipsum
dolor sit amet, consectetur adipiscing elit. Praesent metus
turpis, commodo vel placerat quis, lobortis in ligula.
</p>
<button nbButton nbStepperPrevious>Poprzedni krok</button>
<button nbButton nbStepperNext disabled>Następny krok</button>
</nb-step>
</nb-stepper>
</nb-card>
</section>
</nb-layout-column>
</nb-layout>

View File

@ -1,64 +0,0 @@
.upload-card {
&__header {
max-width: 30rem;
}
}
.column {
display: flex;
flex-flow: column nowrap;
padding: 0 !important;
margin: 0 auto;
&__header {
display: flex;
flex-flow: row nowrap;
justify-content: space-around;
h1 {
width: 30rem;
}
}
&__wave {
display: flex;
img {
width: 100%;
}
}
&__instruction {
margin-top: -1px;
background-color: #3366ff;
display: flex;
flex-flow: column nowrap;
align-items: center;
}
&__instruction-card {
max-width: 50%;
padding: 2.25rem;
}
}
.properties {
margin: 4rem 3rem 0;
display: flex;
flex-flow: row;
justify-content: center;
&__element {
display: flex;
flex-flow: column;
align-items: center;
max-width: 20rem;
margin: 0 2rem;
}
nb-icon {
font-size: 3rem;
}
h2 {
margin: 1rem 0;
}
p {
text-align: center;
}
}

View File

@ -1,31 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { SendDataService } from './send-data.service';
@Component({
selector: 'app-upload',
templateUrl: './upload.component.html',
styleUrls: ['./upload.component.scss'],
})
export class UploadComponent implements OnInit {
public fileName: string;
private file: File;
constructor(private sendService: SendDataService, private router: Router) {}
ngOnInit(): void {}
scrollTo(el: HTMLElement) {
el.scrollIntoView({ behavior: 'smooth' });
}
parseFile(event: any) {
this.file = event;
}
sendFile(event: any) {
this.sendService.postFile(this.file).subscribe((response: any) => {
this.router.navigate(['/view'], { state: { data: response } });
});
}
}

View File

@ -1,29 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import {
NbButtonModule,
NbCardModule,
NbLayoutModule,
NbIconModule,
NbStepperModule,
} from '@nebular/theme';
import { OpenFileButtonComponent } from './open-file-button/open-file-button.component';
import { UploadComponent } from './upload.component';
import { SendDataService } from './send-data.service';
@NgModule({
declarations: [OpenFileButtonComponent, UploadComponent],
imports: [
CommonModule,
HttpClientModule,
NbLayoutModule,
NbButtonModule,
NbCardModule,
NbIconModule,
NbStepperModule,
],
exports: [UploadComponent],
providers: [SendDataService],
})
export class UploadModule {}