Commit 088ea251 authored by Νίκος Κατσαούνος's avatar Νίκος Κατσαούνος
Browse files

added minister-informstudents component, added new reports, added d3 data chart support

parent adc40507
......@@ -12,6 +12,7 @@
"typings": "rimraf typings/ && typings install"
},
"devDependencies": {
"@types/d3": "^4.8.0",
"@types/node": "^6.0.48",
"angular2-router-loader": "^0.3.4",
"angular2-template-loader": "^0.6.0",
......@@ -53,7 +54,7 @@
"bootstrap": "^4.0.0-alpha.6",
"core-js": "^2.4.1",
"css-loader": "^0.25.0",
"d3": "^3.5.16",
"d3": "^4.4.0",
"es6-promise": "^4.0.5",
"es6-shim": "^0.35.0",
"file-loader": "^0.9.0",
......
export const API_ENDPOINT = 'http://eduslim2.minedu.gov.gr/drupal';
export const API_ENDPOINT = 'http://eduslim2.minedu.gov.gr/drupal-8.2.6';
// export const API_ENDPOINT = 'http://eduslim2.minedu.gov.gr/angular/eepal-front/drupal';
export class AppSettings {
public static get API_ENDPOINT(): string {
return 'http://eduslim2.minedu.gov.gr/drupal';
return 'http://eduslim2.minedu.gov.gr/drupal-8.2.6';
// return 'http://eduslim2.minedu.gov.gr/angular/eepal-front/drupal';
}
}
......@@ -25,6 +25,19 @@ import { API_ENDPOINT } from '../../app.settings';
selector: 'minister-informstudents',
template: `
<div class="alert alert-success" *ngIf="successSending == 1">
Έγινε αποστολή {{numSuccessMails}} e-mails!
</div>
<div class="alert alert-warning" *ngIf="successSending == 0">
Κάποια e-mail δεν έχουν σταλεί. Έγινε αποστολή {{numSuccessMails}} e-mails!
</div>
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn-primary btn-md" *ngIf="(loginInfo$ | async).size !== 0" (click)="informUnlocatedStudents()" >
Μαζική αποστολή e-mail στους μαθητές που δεν τοποθετήθηκαν<span class="glyphicon glyphicon-menu-right"></span>
</button>
</div>
`
......@@ -36,6 +49,8 @@ import { API_ENDPOINT } from '../../app.settings';
//private loginInfo$: Observable<ILoginInfo>;
loginInfo$: BehaviorSubject<ILoginInfo>;
loginInfoSub: Subscription;
private numSuccessMails:number;
private successSending:number;
private apiEndPoint = API_ENDPOINT;
private minedu_userName: string;
private minedu_userPassword: string;
......@@ -75,6 +90,41 @@ import { API_ENDPOINT } from '../../app.settings';
return state.loginInfo;
}).subscribe(this.loginInfo$);
this.numSuccessMails = 0;
this.successSending = -1;
}
informUnlocatedStudents() {
/*
this._hds.informUnlocatedStudents(this.minedu_userName, this.minedu_userPassword)
.catch(err => {console.log(err); })
.then(msg => {
console.log("Success");
});
*/
this._hds.informUnlocatedStudents(this.minedu_userName, this.minedu_userPassword).subscribe(data => {
//this.data = data;
//this.successSending = 0;
this.numSuccessMails = data.num_success_mail;
console.log("HERE!");
console.log(this.numSuccessMails);
//console.log(this.data[0].num_success_mail);
//console.log(this.data.length);
},
error => {
console.log("Error");
this.successSending = 0;
},
() => {
console.log("Success");
this.successSending = 1;
//this.validCreator = true;
}
)
}
......
import { Component, OnInit, OnDestroy, ElementRef, ViewChild, Input} from "@angular/core";
import { Component, OnInit, OnDestroy, ElementRef, ViewChild, Input, ViewEncapsulation } from "@angular/core";
import { Injectable } from "@angular/core";
import { AppSettings } from '../../app.settings';
import { HelperDataService } from '../../services/helper-data-service';
......@@ -14,54 +14,34 @@ import { LOGININFO_INITIAL_STATE } from '../../store/logininfo/logininfo.initial
import {reportsSchema, TableColumn} from './reports-schema';
//import * as _ from '..';
import * as d3 from 'd3';
/*
import {
FormBuilder,
FormGroup,
FormControl,
FormArray,
Validators,
} from '@angular/forms';
*/
//import * as _ from '..';
import { API_ENDPOINT } from '../../app.settings';
/*
class TableColumn {
field: string;
title: string;
type: string;
isDisplay: boolean;
isExport: boolean;
valuePrepareFunction: Function;
}
*/
@Component({
selector: 'minister-reports',
encapsulation: ViewEncapsulation.None,
template: `
<div>
<!--<form [formGroup]="formGroup" #form>-->
<div
class = "loading" *ngIf="!validCreator && reportId" >
</div>
<button type="submit" class="btn btn-default btn-block" (click)="createReport('/ministry/general-report/')" [hidden]="minedu_userName == ''" >
Συγκεντρωτικά Αποτελέσματα Κατανομής
Κατανομή Μαθητών με Βάση τη Σειρά Προτίμησης
</button>
<button type="submit" class="btn btn-default btn-block" (click)="createReport('/ministry/report-completeness/')" [hidden]="minedu_userName == ''" >
Κατανομή Μαθητών ανά Σχολείο
Πληρότητα Σχολείων
</button>
<button type="submit" class="btn btn-default btn-block" (click)="createReport('/ministry/report-all-stat/')" [hidden]="minedu_userName == ''" >
Μαθητές ανά Τάξη/Τομέα/Ειδικότητα
</button>
<!--<div *ngFor="let generalReports$ of generalReport$ | async; let i=index">-->
<div *ngIf="validCreator ">
<input #search class="search" type="text" placeholder="Αναζήτηση..." (keydown.enter)="onSearch(search.value)">
<div class="smart-table-container" reportScroll>
......@@ -69,21 +49,22 @@ class TableColumn {
</div>
</div>
<!--
<div *ngIf="validCreator && reportId == 2">
<div class="smart-table-container" reportScroll>
<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>
</div>
</div>-->
<button type="submit" class="alert alert-info" (click)="export2Csv()" [hidden]="!validCreator" >
<button type="button" class="alert alert-info pull-right" (click)="export2Csv()" [hidden]="!validCreator">
<i class="fa fa-download"></i>
Εξαγωγή σε csv
</button>
<button type="button" class="alert alert-info pull-left" (click)="createDiagram(true)" [hidden]="!validCreator || reportId === 2 ">
<i class="fa fa-bar-chart"></i>
Διάγραμμα
</button>
<!--</form>-->
</div>
<div class="d3-chart" *ngIf = "reportId === 1 && validCreator" #chart>
</div>
<!--<div *ngFor="let generalReports$ of generalReport$ | async; let i=index">-->
`
})
......@@ -98,9 +79,9 @@ class TableColumn {
private minedu_userName: string;
private minedu_userPassword: string;
private distStatus = "READY";
//private data: string;
private data;
private validCreator: boolean;
private createGraph: boolean;
private reportId: number;
private source: LocalDataSource;
......@@ -108,82 +89,19 @@ class TableColumn {
@Input() settings: any;
private reportSchema = new reportsSchema();
/*
genReportSchema = {
actions: false,
columns: {
name: {
title: 'Κατηγορία',
filter: false
},
numStudents: {
title: 'Αριθμός',
filter: false
}
}
};
report1Schema = {
actions: false,
columns: {
schoolName: {
title: 'Σχολείο',
filter: false
},
numStudents: {
title: 'Αριθμός',
filter: false
},
capacityTotal: {
title: 'Χωρ/τα',
filter: false
},
percTotal: {
title: 'Πληρότητα',
filter: false
},
numStudentsA: {
title: 'Α Τάξη',
filter: false
},
capacityA: {
title: 'Χωρ/τα',
filter: false
},
percA: {
title: 'Πληρότητα',
filter: false
},
numStudentsB: {
title: 'Β Τάξη',
filter: false
},
capacityB: {
title: 'Χωρ/τα',
filter: false
},
percB: {
title: 'Πληρότητα',
filter: false
},
numStudentsC: {
title: 'Γ Τάξη',
filter: false
},
capacityC: {
title: 'Χωρ/τα',
filter: false
},
percC: {
title: 'Πληρότητα',
filter: false
},
}
};
*/
//d3 test
@ViewChild('chart') private chartContainer: ElementRef;
//@Input() private d3data: Array<any>;
private d3data: Array<any>;
private margin: any = { top: 20, bottom: 20, left: 20, right: 20};
private chart: any;
private width: number;
private height: number;
private xScale: any;
private yScale: any;
private colors: any;
private xAxis: any;
private yAxis: any;
constructor(/*private fb: FormBuilder,*/
......@@ -200,6 +118,7 @@ class TableColumn {
this.generalReport$ = new BehaviorSubject([{}]);
this.minedu_userName = '';
this.validCreator = false;
this.createGraph = false;
this.reportId = 0;
//this.source = new LocalDataSource(this.data);
......@@ -230,26 +149,52 @@ class TableColumn {
return state.loginInfo;
}).subscribe(this.loginInfo$);
/*
setTimeout(() => {
this.generateData();
// change the data periodically
setInterval(() => this.generateData(), 3000);
}, 1000);
*/
//this.generateData();
//this.createChart();
//this.updateChart();
}
createReport(routePath) {
this.reportId = 0;
this.validCreator = false;
this.createGraph = false;
if (routePath === "/ministry/general-report/") {
this.reportId = 1;
//this.settings = this.genReportSchema;
this.settings = this.reportSchema.genReportSchema;
}
else if (routePath === "/ministry/report-completeness/") {
this.reportId = 2;
//this.settings = this.report1Schema;
this.settings = this.reportSchema.reportCompletenessSchema;
this.settings = this.reportSchema.reportCompletenessSchema;
}
else if (routePath === "/ministry/report-all-stat/") {
this.reportId = 3;
this.settings = this.reportSchema.reportAllStatSchema;
}
this.generalReportSub = this._hds.makeReport(this.minedu_userName, this.minedu_userPassword, routePath).subscribe(data => {
this.generalReport$.next(data);
this.data = data;
console.log("Let see..");
//console.log(this.data);
//console.log(this.data[0].name);
//console.log(this.data.length);
},
error => {
this.generalReport$.next([{}]);
......@@ -262,7 +207,13 @@ createReport(routePath) {
//this.source.load(this.data);
//this.settings = _.merge(this.defaultSettings, this.report1Schema);
//this.settings = this.report1Schema;
this.columnMap = new Map<string,TableColumn>();
//columnMap: Map<string,TableColumn> = new Map<string,TableColumn>();
this.prepareColumnMap();
//this.generateData2();
//this.createChart();
//this.updateChart();
}
)
......@@ -368,19 +319,126 @@ prepareColumnMap(): void {
}
}
/*
private doFilterLocal(): void {
this.easyqService.getData({
table: this.table,
filter: '((date >="' + this.from + '") and (date <="' + this.to + '"))',
order: 'date desc'
}).subscribe((rows) => {
this.dataSource.load(rows);
this.msgs.push({severity: 'info', summary: '刷新成功', detail: ''});
});
createDiagram(isCreated) {
if (isCreated && !this.createGraph) {
this.generateGraphData();
this.createChart();
this.updateChart();
this.createGraph = true;
}
}
*/
/*
generateData() {
this.d3data = [];
for (let i = 0; i < (8 + Math.floor(Math.random() * 10)); i++) {
this.d3data.push([
`Index ${i}`,
Math.floor(Math.random() * 100)
]);
}
}
*/
generateGraphData() {
this.d3data = [];
if (this.reportId === 1) {
let labelsX = [];
labelsX.push("1η Προτίμηση");
labelsX.push("2η Προτίμηση");
labelsX.push("3η Προτίμηση");
//for (let i = 0; i < this.data.length; i++) {
for (let i = 1; i <= 3; i++) {
this.d3data.push([
//this.data[i].name,
labelsX[i-1],
this.data[i].numStudents
]);
}
}
}
createChart() {
let element = this.chartContainer.nativeElement;
this.width = element.offsetWidth - this.margin.left - this.margin.right;
this.height = element.offsetHeight - this.margin.top - this.margin.bottom;
let svg = d3.select(element).append('svg')
.attr('width', element.offsetWidth)
.attr('height', element.offsetHeight);
// chart plot area
this.chart = svg.append('g')
.attr('class', 'bars')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`);
// define X & Y domains
let xDomain = this.d3data.map(d => d[0]);
let yDomain = [0, d3.max(this.d3data, d => d[1])];
// create scales
this.xScale = d3.scaleBand().padding(0.1).domain(xDomain).rangeRound([0, this.width]);
this.yScale = d3.scaleLinear().domain(yDomain).range([this.height, 0]);
// bar colors
this.colors = d3.scaleLinear().domain([0, this.d3data.length]).range(<any[]>['red', 'blue']);
// x & y axis
this.xAxis = svg.append('g')
.attr('class', 'axis axis-x')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top + this.height})`)
.call(d3.axisBottom(this.xScale));
this.yAxis = svg.append('g')
.attr('class', 'axis axis-y')
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`)
.call(d3.axisLeft(this.yScale));
}
updateChart() {
// update scales & axis
this.xScale.domain(this.d3data.map(d => d[0]));
this.yScale.domain([0, d3.max(this.d3data, d => d[1])]);
this.colors.domain([0, this.d3data.length]);
this.xAxis.transition().call(d3.axisBottom(this.xScale));
this.yAxis.transition().call(d3.axisLeft(this.yScale));
let update = this.chart.selectAll('.bar')
.data(this.d3data);
// remove exiting bars
update.exit().remove();
// update existing bars
this.chart.selectAll('.bar').transition()
.attr('x', d => this.xScale(d[0]))
.attr('y', d => this.yScale(d[1]))
.attr('width', d => this.xScale.bandwidth())
.attr('height', d => this.height - this.yScale(d[1]))
.style('fill', (d, i) => this.colors(i));
// add new bars
update
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', d => this.xScale(d[0]))
.attr('y', d => this.yScale(0))
.attr('width', this.xScale.bandwidth())
.attr('height', 0)
.style('fill', (d, i) => this.colors(i))
.transition()
.delay((d, i) => i * 10)
.attr('y', d => this.yScale(d[1]))
.attr('height', d => this.height - this.yScale(d[1]));
}
......
......@@ -9,6 +9,7 @@ import { IAppState } from '../../store/store';
import { Router, ActivatedRoute, Params} from '@angular/router';
import { BehaviorSubject, Subscription } from 'rxjs/Rx';
import { ILoginInfo } from '../../store/logininfo/logininfo.types';
import { ModalDirective } from 'ng2-bootstrap/modal';
import { LOGININFO_INITIAL_STATE } from '../../store/logininfo/logininfo.initial-state';
......@@ -26,31 +27,64 @@ import { API_ENDPOINT } from '../../app.settings';
selector: 'minister-view',
template: `
<div
class = "loading" *ngIf=" distStatus === 'STARTED'" >
<!--
<div *ngIf="(isModalShownMy)" [config]="{ show: true }" (onHidden)="onHidden()" bsModal #autoShownModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title pull-left">Auto shown modal</h4>
<button type="button" class="close pull-right" aria-label="Close" (click)="hideModal()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Καλημέρα σας.</p>
<p>Αυτό είναι ένα μήνυμα</p>
<p>από το ng2-bootstrap/modal</p>
</div>
</div>
</div>
</div>
-->
<div
class = "loading" *ngIf=" distStatus === 'STARTED'" >
</div>
<div class="alert alert-info" *ngIf="distStatus === 'STARTED'">
Παρακαλώ περιμένετε...Η εκτέλεση της κατανομής ενδέχεται να διαρκέσει μερικά λεπτά. Παρακαλώ μην εκτελείται οποιαδήποτε ενέργεια μετακίνησης στον φυλλομετρητή σας, μέχρι να ολοκληρωθεί η κατανομή.
</div>
<div class="alert alert-info" *ngIf="distStatus === 'FINISHED'">
<div class="alert alert-success" *ngIf="distStatus === 'FINISHED'">
Η κατανομή ολοκληρώθηκε με επιτυχία!
</div>
<div class="alert alert-info" *ngIf="distStatus === 'ERROR'">
<div class="alert alert-warning" *ngIf="distStatus === 'ERROR'">
Αποτυχία κατανομής!
</div>
<div>
<!--
<form [formGroup]="formGroup" method = "POST" action="{{apiEndPoint}}/epal/distribution" #form>
-->
<div>
<form [formGroup]="formGroup" #form>
<!--<div *ngFor="let loginInfoToken$ of loginInfo$ | async; let i=index"></div>-->
<!--<button type="submit" class="btn-primary btn-md" (click)="form.submit()" >-->
<button type="submit" class="btn-primary btn-md" *ngIf="(loginInfo$ | async).size !== 0" (click)="runDistribution()" >
Εκτέλεση Κατανομής Μαθητών
</button>
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn-primary btn-md" *ngIf="(loginInfo$ | async).size !== 0" (click)="runDistribution()" >
Εκτέλεση Κατανομής Μαθητών<span class="glyphicon glyphicon-menu-right"></span>
</button>
</div>
</form>
<button type="button" class="btn-primary btn-md" (click)="testModal()" >
Test Modal
</button>
</div>
`