Commit 47f27445 authored by Florent Chehab's avatar Florent Chehab
Browse files

begin switch to react

parent 3fc9de99
......@@ -5,4 +5,6 @@ data
__pycache__
*.php
UVS_evals
\ No newline at end of file
UVS_evals
build
node_modules
\ No newline at end of file
image: python:3.6.5-jessie
# image: python:3.6.5-jessie
render:
stage: build
script:
- rm -rf ./data
- git clone --depth=1 $DATA_REPO_URL data && rm -rf ./data/.git
- pip install -r ./requierements.txt --quiet --quiet
- jupyter nbconvert --execute --to html_toc analysis.ipynb
artifacts:
paths:
- analysis.html
expire_in: 1 week
tags:
- docker
# render:
# stage: build
# script:
# - rm -rf ./data
# - git clone --depth=1 $DATA_REPO_URL data && rm -rf ./data/.git
# - pip install -r ./requierements.txt --quiet --quiet
# - jupyter nbconvert --execute --to html_toc analysis.ipynb
# artifacts:
# paths:
# - analysis.html
# expire_in: 1 week
# tags:
# - docker
deploy-stage:
image: floawfloaw/sshpass_bash
stage: deploy
script:
- bash deploy.sh
only:
- master
tags:
- docker
\ No newline at end of file
# deploy-stage:
# image: floawfloaw/sshpass_bash
# stage: deploy
# script:
# - bash deploy.sh
# only:
# - master
# tags:
# - docker
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
{
"name": "test",
"version": "0.1.0",
"private": true,
"dependencies": {
"bootstrap": "^4.1.1",
"plotly.js": "^1.38.3",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-plotly.js": "^2.2.0",
"react-scripts": "1.1.4",
"reactstrap": "^6.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
#Observatoire-Des-UVs
jupyter==1.0.0
plotly==2.6.0
cufflinks==0.12.1
pandas==0.22.0
numpy==1.14.2
jupyter_contrib_nbextensions==0.5.0
\ No newline at end of file
data
\ No newline at end of file
// BSD 2-Clause License
// Copyright (c) 2018, Florent Chehab
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
const fs = require('fs');
const path = require('path');
var all_data = {};
const data_dir = path.join(__dirname,'data');
list_of_files = [];
fs.readdirSync(data_dir).forEach(file => {
list_of_files.push(file);
})
list_of_files.sort(function (sem1,sem2){
var s1 = sem1[0], s2 = sem2[0];
var y1 = sem1.substring(1,4), y2 = sem2.substring(1,4);
if (y1!=y2){
return y1 < y2;
}
return sem1 < sem2;
});
for (i in list_of_files){
file = list_of_files[i];
if (file != 'all_data.json'){
semester = file.split('.')[0];
all_data[semester] = JSON.parse(fs.readFileSync(path.join(data_dir, file)));
}
}
fs.writeFileSync(path.join(data_dir,'all_data.json'), JSON.stringify(all_data));
\ No newline at end of file
{
"--": -3,
"-": -1,
"+": 1,
"++": 3
}
\ No newline at end of file
{
"SCALE_PERCENTAGE":[0,100]
}
\ No newline at end of file
<style type="text/css">
.tg {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
text-align: left !important;
}
.rendered_html tr,
.rendered_html th,
.rendered_html td {
text-align: left !important;
}
.tg td {
font-size: 14px;
padding: 10px 5px;
border-style: solid;
border-width: 1px;
overflow: hidden;
word-break: normal;
border-color: black;
}
.tg th {
font-size: 14px;
font-weight: normal;
padding: 10px 5px;
border-style: solid;
border-width: 1px;
overflow: hidden;
word-break: normal;
border-color: black;
}
.tg .tg-gcw3 {
border-color: #000000
}
.styled-select select {
padding: 5px;
font-size: 16px;
line-height: 1;
border: 1px;
border-radius: 0;
height: 34px;
}
.background-white {
background-color: white;
}
</style>
<script>
var table_header = function (content) {
return `
<table id="table-details-uv" class="tg">
<tr>
<th class="tg-gcw3" style="width:10%;"><strong>Semestre</strong></th>
<th class="tg-gcw3" style="width:50%;"><strong>Données</strong></th>
<th class="tg-gcw3"><strong>Commentaires</strong></th>
</tr>
${content}
</table>
`;
}
var table_row = function (semester, resp) {
return `
<tr>
<td class="tg-gcw3" rowspan="2"><strong>${semester}</strong>${resp}</td>
<td class="tg-gcw3" rowspan="2"><div id="details-${semester}"></div><div id="graph-${semester}"></div></td>
<td class="tg-gcw3"><div id="com-resp-${semester}"></div></td>
</tr>
<tr>
<td class="tg-gcw3"><div id="com-conseil-${semester}"></div></td>
</tr>
`;
}
var uv_intro = function (uv, name) {
return `
<center>
<h2> ${uv} : ${name} </h2>
</center>
`;
}
var uv_details = function (uv) {
var ratio = function (what) {
return (100 * what / (uv.nb_etu_registered - uv.nb_etu_abs)).toFixed(2);
}
return `<div class="alert alert-info background-white" ><center><i>
${uv.nb_etu_registered} inscrits, ${uv.nb_etu_abs} absent(s), ${uv.nb_etu_passed} étudiants reçus à l'uv (Réussite : ${ratio(uv.nb_etu_passed)}%) - ${uv.nb_evals} évaluations (${ratio(uv.nb_evals)}%)
</i></center></div>
`;
}
var comment_html = function (date, comment, alert_type) {
return `
<div class="alert alert-${alert_type}">${date}</div>
<div class="alert alert-info background-white">${comment}</div>
`
}
</script>
<div class="styled-select">
Choix de l'UV :
<select id='UV-select' onchange="update_display(this.value);">
</select>
<input id="UV-input-text" type="text">
<input id="UV-input-btn" type="button" class="btn" disabled value="Visualiser" onclick="update_select($('#UV-input-text').val())">
</div>
<br>
<div id="UV-intro">
</div>
<div id="evol">
<h3>
Évolutions temporelles des évaluations d'UVs
</h3>
<div id='evol-graph'>
</div>
<h3>
Et en détails
</h3>
<div id="data">
</div>
</div>
<script>
var questions = [];
for (var i = 1; i <= 10; i++) {
questions.push("Q. " + i);
}
var update_display = function (uv) {
// first update main graph
var traces_summary = [];
var questions_data = Array.from(Array(10), () => new Array(0));
var x = [];
for (semester in evals_data) {
x.push(semester);
if (uv in evals_data[semester]) {
var stats = evals_data[semester][uv]["stats"];
for (q in questions) {
var nb_evals = 0;
var val = 0;
for (ans in POSSIBLE_ANSWERS) {
nb_evals += stats[q][ans];
val += stats[q][ans] * POSSIBLE_ANSWERS_COEFFS[ans];
}
questions_data[q].push((val / nb_evals).toFixed(2));
}
} else {
for (q in questions) {
questions_data[q].push(null);
}
}
}
for (q in questions) {
traces_summary.push({
x: x,
y: questions_data[q],
name: questions[q],
type: 'scatter',
})
}
var layout = {
title: 'Évolutions temporelles des évaluations',
yaxis: { range: SCALE_MARK, title: "Note obtenue pour chaque questions" }
};
Plotly.newPlot('evol-graph', traces_summary, layout);
// Then generate table
var all_semester = []
for (semester in evals_data) {
all_semester.push(semester);
}
all_semester.reverse();
var html = '';
var tmp = '';
for (sem in all_semester) {
var semester = all_semester[sem];
var resp = '';
if (uv in evals_data[semester]) {
document.getElementById('UV-intro').innerHTML = uv_intro(uv, evals_data[semester][uv].name)
resp = '<br><br>' + evals_data[semester][uv].teacher_name;
}
tmp += table_row(semester, resp);
}
html = table_header(tmp);
document.getElementById('data').innerHTML = html;
// Finally : fill table
for (semester in evals_data) {
if (uv in evals_data[semester]) {
// details
document.getElementById('details-' + semester).innerHTML = uv_details(evals_data[semester][uv]);
// graph
var traces_table = [];
var stats = evals_data[semester][uv]["stats"];
for (ans in POSSIBLE_ANSWERS) {
var responses = [];
for (q in questions) {
responses.push(stats[q][ans]);
}
traces_table.push({
x: questions,
y: responses,
name: POSSIBLE_ANSWERS[ans],
type: 'bar',
marker: {
color: POSSIBLE_ANSWERS_COL[ans]
}
})
}
var layout_table = {
title: "Évaluations de l'UV " + uv + " en " + semester,
barmode: 'stack', showlegend: false, yaxis: {
title: "Nombre d'évaluations"
}
};
Plotly.newPlot('graph-' + semester, traces_table, layout_table);
if (uv in evals_data[semester]) {
var teacher_date = evals_data[semester][uv]['date_review_teacher'];
var conseil_date = evals_data[semester][uv]['date_review_conseil'];
var teacher_comment = evals_data[semester][uv]['teacher_comment'];
var conseil_comment = evals_data[semester][uv]['conseil_comment'];
var conseil_alert = 'warning', teacher_alert = 'warning';
if (teacher_date === null) {
teacher_alert = 'danger';
teacher_date = "<i>Les évaluations ne semblent par avoir été vues par le ou la responsable de l'UV ce semestre.</i>";
teacher_comment = '';
} else {
teacher_date = "Évaluations prises en compte par le ou la responsable de l'UV le " + teacher_date + '.';
}
if (conseil_date === null) {
conseil_alert = 'danger';
conseil_date = "<i>Les évaluations ne semblent par avoir été vues par le conseil de perfectionnement ce semestre.</i>";
conseil_comment = '';
} else {
conseil_date = "Évaluations prises en compte par le conseil de perfectionnement le " + teacher_date + '.';
}
if (teacher_comment > '') {
teacher_alert = 'success';
}
if (conseil_comment > '') {
conseil_alert = 'success';
}
if (teacher_comment === '' || teacher_comment === null) {
teacher_comment = "<i>Aucun rapport saisi par le ou la responsable de l'enseignement ce semestre.</i>";
}
if (conseil_comment === '' || conseil_comment === null) {
conseil_comment = "<i>Aucun rapport des conseils de perfectionnement enregistré.</i>";
}
teacher_comment = teacher_comment.replace(/¿/g, "'");
conseil_comment = conseil_comment.replace(/¿/g, "'");
document.getElementById('com-resp-' + semester).innerHTML = comment_html(teacher_date, teacher_comment, teacher_alert);
document.getElementById('com-conseil-' + semester).innerHTML = comment_html(conseil_date, conseil_comment, conseil_alert);
}
}
}
// update link
document.getElementById('uv-details-name').innerHTML = uv;
}
var all_uvs = new Set();
for (semester in evals_data) {
for (uv in evals_data[semester]) {
all_uvs.add(uv);
}
}
all_uvs = Array.from(all_uvs).sort();
var select_uv = document.getElementById('UV-select')
for (ind in all_uvs) {
var uv = all_uvs[ind];
var opt = document.createElement('option');
opt.value = uv;
opt.innerHTML = uv;
select_uv.appendChild(opt);
}
var interval = setInterval(function () {
if (typeof Plotly == 'undefined') return;
clearInterval(interval);
document.getElementById('UV-select').value = all_uvs[0];
update_display(all_uvs[0]);
}, 200);
// js for handling input uv name
var is_uv = function(uv){
for (ind in all_uvs) {
if (all_uvs[ind] == uv) {
return true;
}
}
return false;
}
var update_select = function (uv) {
if (is_uv(uv)){
$("#UV-select").val(uv).change();
}
}
$('#UV-input-text').keyup(function (event) {
var val = $('#UV-input-text').val().toUpperCase();
if (event.keyCode === 13) {
event.preventDefault();
update_select(val);
} else {
var check_input = function (value) {
var valid = false;
for (ind in all_uvs) {
var uv = all_uvs[ind];
var re = new RegExp('^' + value);
if (uv.match(re)) {
return true;
}
}
return false;
}
while (!check_input(val)) {
val = val.substring(0, val.length - 1);
}
$('#UV-input-btn').prop('disabled', !is_uv(val));
}
$('#UV-input-text').val(val);
});
</script>
\ No newline at end of file
import datetime
from datetime import date
import string
import random
import json
import os.path
def generate_data_file(semester):
"""
Function for generating a random data file to
be used in analysis
"""
TODAY = datetime.datetime.today().strftime('%d/%m/%Y')
output = {
'date': TODAY,
'semester': semester,
'data': {}
}
def random_eval(nb_evals):
stats = {}
for i in range(1, 10):
pp = random.randint(0, nb_evals)
p = random.randint(0, nb_evals - pp)
m = random.randint(0, nb_evals - pp - p)
mm = nb_evals - pp