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

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
This diff is collapsed.
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 - p - m
stats[str(i)] = {
'++': pp,
'+': p,
'-': m,
'--': mm
}
return stats
def random_date():
start_date = date.today().toordinal() - 60
end_date = date.today().toordinal()
random_day = date.fromordinal(random.randint(start_date, end_date))
return random_day.strftime('%d/%m/%Y')
def random_string(l=40):
return ''.join(random.choices(string.ascii_uppercase, k=l))
NB_UVS = 200
for uv in range(1, NB_UVS):
uv_code = 'UV' + str(uv)
nb_etu_registered = random.randint(10, 100)
nb_etu_abs = random.randint(1, 9)
nb_etu_passed = random.randint(1, nb_etu_registered - nb_etu_abs)
nb_evals = random.randint(1, nb_etu_registered - nb_etu_abs)
date_review_teacher = random.choice([None, random_date()])
date_review_conseil = random.choice([None, random_date()])
teacher_comment = None
conseil_comment = None
if date_review_teacher:
teacher_comment = random_string(random.randint(0, 500))
if date_review_conseil:
conseil_comment = random_string(random.randint(0, 500))
output['data'][uv_code] = {
'name': random_string(),
'nb_etu_registered': nb_etu_registered,
'nb_etu_abs': nb_etu_abs,
'nb_etu_passed': nb_etu_passed,
'nb_evals': nb_evals,
'teacher_name': random_string(10),
'stats': random_eval(nb_evals),
'date_review_teacher': date_review_teacher,
'teacher_comment': teacher_comment,
'date_review_conseil': date_review_conseil,
'conseil_comment': conseil_comment
}
fp = os.path.realpath(__file__) + '/../../data/' + semester + '.json'
with open(os.path.abspath(fp), 'w') as outfile:
json.dump(output, outfile)
<style>
a.got-to-uv {
display: inline;
padding-left: 20px;
padding-right: 20px;
padding-top: 10px;
padding-bottom: 10px;
position: fixed;
z-index: 999;
right: 20px;
bottom: 20px;
-webkit-border-radius: 30px;
-moz-border-radius: 30px;
background: aliceblue;
border-radius: 30px;
color: darkblue !important;
font-size: 19px;
}
</style>
<script>
// Button to go to the uv detail section
$(document).ready(function () {
if (document.getElementById('uv-details-name') === null) {
$('body').prepend("<a class='got-to-uv'><center>Voir en détail l'UV<br><span id='uv-details-name' style='font-weight:bold;'></span></center></a>");
}
$('a.got-to-uv').fadeIn('slow');
$('a.got-to-uv').click(function () {
document.getElementById('UV-select').scrollIntoView();
});
});
</script>
\ No newline at end of file
body {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
}
ol, ul {
padding-left: 30px;
}
.board-row:after {
clear: both;
content: "";
display: table;
}
.status {
margin-bottom: 10px;
}
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
.square:focus {
outline: none;
}
.kbd-navigation .square:focus {
background: #ddd;
}
.game {
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
}
// 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.
import React from 'react';
import ReactDOM from 'react-dom';
// import './index.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import ExtractionDate from './js/comp_extractionDate';
import CoeffsSummary from './js/comp_coeffs';
import ResponseRate from './js/comp_responseRate';
class EvalsAnalysis extends React.Component {
// constructor(props) {
// super(props);
// // this.state = {
// // all_data: all_data
// // };
// }
render() {
return (
<div className="EvalsAnalysis">
<h1>Introduction</h1>
<h2>Présentation des données</h2>
<h3>Âge des données</h3>
<ExtractionDate />
<h3>Remarque</h3>
<CoeffsSummary />
<h2>Informations Générales</h2>
<h3>Taux de réponse</h3>
<ResponseRate />
</div>
);
}
}
// ========================================
ReactDOM.render(<EvalsAnalysis />, document.getElementById("root"));
// 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.
import React from 'react';
import coeffs from "../config/coeffs.json";
import { Table } from 'reactstrap';
class CoeffsSummary extends React.Component {
render() {
return (
<div>
<p>
Lorsque les statistiques des évaluations d'UVs (le nombre de <code>++</code>, de <code>+</code>, etc.) seront analysées de manière quantitative la table de conversion symétrique suivante est employée :
</p>
<Table striped bordered>
<thead>
<tr>
<th>Sigle</th>
<th>Valeur associée</th>
</tr>
</thead>
<tbody>
{
Object.keys(coeffs).map((sigle, i) =>
<tr key={i}>
<td key={1}><code>{sigle}</code></td>
<td key={2}>{coeffs[sigle]}</td>
</tr>
)
}
</tbody>
</Table>
</div>
);
}
}
export default CoeffsSummary;
\ No newline at end of file
import React from 'react';
import all_data from "../data/all_data.json";
import { Table } from 'reactstrap';
class ExtractionDate extends React.Component {
render() {
return (
<div>
<p>
Pour les semestres sélectionnés, les données associées ont été extraites aux dates :
</p>
<Table striped bordered>
<thead>
<tr>
<th>Semestre</th>
<th>Date d'extraction</th>
</tr>
</thead>
<tbody>
{Object.keys(all_data).map((semester, i) =>
<tr key={i}>
<td key={1}>{semester}</td>
<td key={2}>{all_data[semester]['date']}</td>
</tr>
)}
</tbody>
</Table>
</div>
);
}
}
export default ExtractionDate;
\ 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.
import React from 'react';
import all_data from "../data/all_data.json";
import ResponseRateTab from './comp_responseRateTab';
import ResponseRateGraph from './comp_responseRateGraph';
class ResponseRate extends React.Component {
calculateStats() {
function toPercent(n) {
return Math.round(n * 10000) / 100;
}
var data = {};
for (var sem in all_data) {
data[sem] = {
nb_uvs: 0,
nb_inscription: 0,
participation: 0,
resp_view: 0,
resp_report: 0
}
var d = all_data[sem]['data'];
for (var uv in d) {
data[sem]['nb_uvs'] += 1;
data[sem]['nb_inscription'] += d[uv]['nb_etu_registered'];
if (d[uv]['nb_etu_registered'] !== 0) {
data[sem]['participation'] += d[uv]['nb_evals'] / d[uv]['nb_etu_registered'];
}
if (d[uv]['date_review_teacher'] !== null) {
data[sem]['resp_view'] += 1;
}
if (d[uv]['teacher_comment'] !== '' && d[uv]['teacher_comment'] !== null) {
data[sem]['resp_report'] += 1;
}
}
var arr = ['participation', 'resp_view', 'resp_report'];
for (var i in arr) {
var k = arr[i];
data[sem][k] /= data[sem]['nb_uvs'];
data[sem][k] = toPercent(data[sem][k]);
}
}
return data;
}
render() {
var data = this.calculateStats();
return (
<div>
<ResponseRateTab data={data} />
<ResponseRateGraph data={data} />
</div>
);
}
}
export default ResponseRate;
\ 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.
import React from 'react';
import Plot from 'react-plotly.js';
import ranges from '../config/ranges.json';
class ResponseRateGraph extends React.Component {
render() {
var data = this.props.data;
var plot_data = [];
for (var sem in data) {
var d = { type: 'bar', name: sem };
d['x'] = ["Participation moyenne des étudiants",
"UVs où le/la resps a <i>vu</i> les éval.",
"UVs où le/la resp. a posté un commentaire"
]
d['y'] = [data[sem]['participation'], data[sem]['resp_view'], data[sem]['resp_report']];
plot_data.push(d);
}
return (
<div>
<Plot
data={plot_data}
layout={
{
barmode: 'group',
title: "Analyse temporelle de l'engagement dans les évaluations d'UVs",
yaxis: { title: 'Taux', range: ranges['SCALE_PERCENTAGE'] }
}}
/>
</div>
);
}
}
export default ResponseRateGraph;
\ 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