From 3ce0594a4565e70102faa92308bd119d8c5dc3c7 Mon Sep 17 00:00:00 2001 From: qkzk Date: Thu, 2 Jun 2022 22:05:07 +0200 Subject: [PATCH 1/6] Charts, first try. --- src/app.py | 2 +- src/model.py | 8 +++ src/static/css/style.css | 6 ++ src/templates/graphs.html | 132 ++++++++++++++++++++++++++++++++++++++ src/templates/view.html | 3 + 5 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 src/templates/graphs.html diff --git a/src/app.py b/src/app.py index 13e256a..3e621fa 100644 --- a/src/app.py +++ b/src/app.py @@ -254,7 +254,7 @@ def fix_missing_csrf_token(): def add_security_headers(resp): resp.headers[ "Content-Security-Policy" - ] = "script-src 'self' 'unsafe-inline'; img-src * data:; default-src 'self'; style-src 'self' 'unsafe-inline'; frame-src www.youtube-nocookie.com www.youtube.com" + ] = "script-src 'self' 'unsafe-inline' cdn.jsdelivr.net; img-src * data:; default-src 'self'; style-src 'self' 'unsafe-inline'; frame-src www.youtube-nocookie.com www.youtube.com" return resp @app.context_processor diff --git a/src/model.py b/src/model.py index 216deff..664a68f 100644 --- a/src/model.py +++ b/src/model.py @@ -117,6 +117,10 @@ def flat_questions_formatted(self) -> list[str]: """Returns a flat list of formatted `Question`s of this QCM.""" return [question.format() for part in self.part for question in part.questions] + def flat_questions(self) -> list["QcmPartQuestion"]: + """Returns a flat list of formatted `Question`s of this QCM.""" + return [question for part in self.part for question in part.questions] + def remove_and_commit(self): """ Remove the `self` QCM and commit. @@ -274,6 +278,10 @@ def from_parser(cls, parsed_answer: QCM_Answer) -> "QcmPartQuestionAnswer": """Creates an answer record from a parsed answer.""" return cls(answer=parsed_answer.text, is_valid=parsed_answer.is_valid) + def nb_of_selection(self) -> int: + """Returns the number of time this answer has been selected by as `Student`.""" + return Choice.query.filter_by(id_answer=self.id).count() + class Student(db.Model): """ diff --git a/src/static/css/style.css b/src/static/css/style.css index 2e1aeb0..f7a7d68 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -229,6 +229,12 @@ img { height: auto; } +.center { + margin-left: auto; + margin-right: auto; + text-align: center; +} + table.center { margin-left: auto; margin-right: auto; diff --git a/src/templates/graphs.html b/src/templates/graphs.html new file mode 100644 index 0000000..59e22e8 --- /dev/null +++ b/src/templates/graphs.html @@ -0,0 +1,132 @@ +{% if qcm %} + {% if qcm.count_works() > 0 %} +

+ Détail des réponses +

+ + {% for question in qcm.flat_questions() %} +
+
+

+ {{ question.question|safe }} +

+

+ {{ question.sub_text|safe }} +

+ {% if question.is_text_question %} +
    + {% for text in question.texts %} +
  • + {{ text.text | safe }} +
  • + {% endfor %} +
+ {% else %} +
+ +
+ + {% endif %} +
+
+ {% endfor %} + {% endif %} +{% endif %} diff --git a/src/templates/view.html b/src/templates/view.html index f1567da..8ec4ba4 100644 --- a/src/templates/view.html +++ b/src/templates/view.html @@ -33,6 +33,9 @@

{% include('marks.html', qcm) %}
+
+ {% include('graphs.html', qcm) %} +
{% endblock %} From 2a6bd3bcf65fc65f922fa2a7761b15d022ca2bcd Mon Sep 17 00:00:00 2001 From: qkzk Date: Thu, 2 Jun 2022 22:51:08 +0200 Subject: [PATCH 2/6] Charts. Simple random colors, unescape answers, text questions. --- src/templates/graphs.html | 230 +++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 125 deletions(-) diff --git a/src/templates/graphs.html b/src/templates/graphs.html index 59e22e8..e2cc2ff 100644 --- a/src/templates/graphs.html +++ b/src/templates/graphs.html @@ -1,132 +1,112 @@ -{% if qcm %} - {% if qcm.count_works() > 0 %} +{% if qcm and qcm.count_works() > 0 %} + +

Détail des réponses

- - {% for question in qcm.flat_questions() %} -
-
-

- {{ question.question|safe }} -

-

- {{ question.sub_text|safe }} -

- {% if question.is_text_question %} -
    - {% for text in question.texts %} -
  • - {{ text.text | safe }} -
  • + {% for question in qcm.flat_questions() %} +
    +
    +

    + {{ question.question|safe }} +

    +

    + {{ question.sub_text|safe }} +

    + {% if question.is_text_question %} +
      + {% for text in question.texts %} +
    • + {{ text.text | safe }} +
    • + {% endfor %} +
    + {% else %} +
    + + +
    + - {% endif %} -
    -
    - {% endfor %} - {% endif %} + const question_chart_{{question.id}} = new Chart( + document.getElementById('question_chart_{{question.id}}'), + config_{{question.id}} + ); + + {% endif %} +
+
+ {% endfor %} {% endif %} From 105124d7cdbae1548aa4bc2d6fcd15f7ca00c8b5 Mon Sep 17 00:00:00 2001 From: qkzk Date: Fri, 3 Jun 2022 13:59:03 +0200 Subject: [PATCH 3/6] Fix chart radius to 150px. Use d3 for color gradients. --- src/app.py | 2 +- src/static/css/style.css | 5 + src/static/javascript/pie_graph_setup.js | 75 ++++++++++++ src/templates/graphs.html | 150 ++++++++--------------- src/templates/marks.html | 6 +- 5 files changed, 138 insertions(+), 100 deletions(-) create mode 100644 src/static/javascript/pie_graph_setup.js diff --git a/src/app.py b/src/app.py index 3e621fa..b7068ef 100644 --- a/src/app.py +++ b/src/app.py @@ -254,7 +254,7 @@ def fix_missing_csrf_token(): def add_security_headers(resp): resp.headers[ "Content-Security-Policy" - ] = "script-src 'self' 'unsafe-inline' cdn.jsdelivr.net; img-src * data:; default-src 'self'; style-src 'self' 'unsafe-inline'; frame-src www.youtube-nocookie.com www.youtube.com" + ] = "script-src 'self' 'unsafe-inline' cdn.jsdelivr.net d3js.org; img-src * data:; default-src 'self'; style-src 'self' 'unsafe-inline'; frame-src www.youtube-nocookie.com www.youtube.com" return resp @app.context_processor diff --git a/src/static/css/style.css b/src/static/css/style.css index f7a7d68..148949b 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -398,3 +398,8 @@ footer { .wsh-table { min-width: rem-calc(640); } + +.div_pie_chart { + width: 600px; + height: 600px; +} diff --git a/src/static/javascript/pie_graph_setup.js b/src/static/javascript/pie_graph_setup.js new file mode 100644 index 0000000..6df0fca --- /dev/null +++ b/src/static/javascript/pie_graph_setup.js @@ -0,0 +1,75 @@ + +/* Unescape an HTML escaped string. */ +function htmlDecode(input) { + var doc = new DOMParser().parseFromString(input, "text/html"); + return doc.documentElement.textContent; +} + +/* Calculates an interpolation point */ +function calculatePoint(i, intervalSize, colorRangeInfo) { + var { colorStart, colorEnd, useEndAsStart } = colorRangeInfo; + return (useEndAsStart + ? (colorEnd - (i * intervalSize)) + : (colorStart + (i * intervalSize))); +} + +/* Create a d3 interpolation colors array. */ +function interpolateColors(dataLength, colorScale, colorRangeInfo) { + var { colorStart, colorEnd } = colorRangeInfo; + var colorRange = colorEnd - colorStart; + var intervalSize = colorRange / dataLength; + var i, colorPoint; + var colorArray = []; + + for (i = 0; i < dataLength; i++) { + colorPoint = calculatePoint(i, intervalSize, colorRangeInfo); + colorArray.push(colorScale(colorPoint)); + } + + return colorArray; +} + +/* Set up Chart.js Pie Chart */ +function createChart(chartId, chartData, colorScale, colorRangeInfo) { + /* Grab chart element by id */ + const chartElement = document.getElementById(chartId); + + const dataLength = chartData.labels.length; + + /* Create color array */ + var COLORS = interpolateColors(dataLength, colorScale, colorRangeInfo); + chartData.datasets[0].backgroundColor = COLORS; + chartData.datasets[0].hoverBackgroundColor = COLORS; + + + /* Create chart */ + const myChart = new Chart(chartElement, { + type: 'pie', + data: chartData, + options: { + plugins: { + legend: { + display: true, + position: 'right' + } + }, + radius: 150, + hover: { + onHover: function(e) { + var point = this.getElementAtEvent(e); + e.target.style.cursor = point.length ? 'pointer' : 'default'; + }, + }, + } + }); + + return myChart; +} + +const colorScale = d3.interpolateSpectral; + +const colorRangeInfo = { + colorStart: 0.2, + colorEnd: 0.8, + useEndAsStart: false, +}; diff --git a/src/templates/graphs.html b/src/templates/graphs.html index e2cc2ff..6efd1ed 100644 --- a/src/templates/graphs.html +++ b/src/templates/graphs.html @@ -5,108 +5,66 @@ crossorigin="anonymous" > - + + + +

Détail des réponses

{% for question in qcm.flat_questions() %} -
-
-

- {{ question.question|safe }} -

-

- {{ question.sub_text|safe }} -

- {% if question.is_text_question %} -
    - {% for text in question.texts %} -
  • - {{ text.text | safe }} -
  • - {% endfor %} -
- {% else %} -
- - -
- - {% endif %} + const question_chart_{{question.id}} = new createChart( + 'question_chart_{{question.id}}', + data_{{question.id}}, + colorScale, + colorRangeInfo + ); + + {% endif %} +
- {% endfor %} {% endif %} diff --git a/src/templates/marks.html b/src/templates/marks.html index 8d1cddf..2e6d7e0 100644 --- a/src/templates/marks.html +++ b/src/templates/marks.html @@ -55,18 +55,18 @@
{% if qcm.has_works() %} {% endif %} From e8aa3418c555a5b77ec17e9d34d6dd42ae3f4a0a Mon Sep 17 00:00:00 2001 From: qkzk Date: Fri, 3 Jun 2022 14:20:02 +0200 Subject: [PATCH 4/6] Separate marks and view control. toggle marks & charts. --- src/static/css/style.css | 4 ++ src/templates/chart.html | 70 +++++++++++++++++++++++ src/templates/graphs.html | 70 ----------------------- src/templates/marks.html | 82 --------------------------- src/templates/view.html | 7 ++- src/templates/view_menu.html | 106 +++++++++++++++++++++++++++++++++++ 6 files changed, 185 insertions(+), 154 deletions(-) create mode 100644 src/templates/chart.html delete mode 100644 src/templates/graphs.html create mode 100644 src/templates/view_menu.html diff --git a/src/static/css/style.css b/src/static/css/style.css index 148949b..8e1f2d7 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -403,3 +403,7 @@ footer { width: 600px; height: 600px; } + +#toggle_chart { + color: var(--main-text-color); +} diff --git a/src/templates/chart.html b/src/templates/chart.html new file mode 100644 index 0000000..434d2ca --- /dev/null +++ b/src/templates/chart.html @@ -0,0 +1,70 @@ +{% if qcm and qcm.count_works() > 0 %} + + + + + +
+

+ Détail des réponses +

+
+ {% for question in qcm.flat_questions() %} +
+

+ {{ question.question|safe }} +

+

+ {{ question.sub_text|safe }} +

+ {% if question.is_text_question %} +
    + {% for text in question.texts %} +
  • + {{ text.text | safe }} +
  • + {% endfor %} +
+ {% else %} +
+ +
+ + {% endif %} +
+ {% endfor %} +{% endif %} diff --git a/src/templates/graphs.html b/src/templates/graphs.html deleted file mode 100644 index 6efd1ed..0000000 --- a/src/templates/graphs.html +++ /dev/null @@ -1,70 +0,0 @@ -{% if qcm and qcm.count_works() > 0 %} - - - - - -

- Détail des réponses -

- {% for question in qcm.flat_questions() %} -
-
-

- {{ question.question|safe }} -

-

- {{ question.sub_text|safe }} -

- {% if question.is_text_question %} -
    - {% for text in question.texts %} -
  • - {{ text.text | safe }} -
  • - {% endfor %} -
- {% else %} -
- -
- - {% endif %} -
-
- {% endfor %} -{% endif %} diff --git a/src/templates/marks.html b/src/templates/marks.html index 2e6d7e0..d100642 100644 --- a/src/templates/marks.html +++ b/src/templates/marks.html @@ -1,86 +1,4 @@ {% if qcm %} - -
- - - - - - - - - - - - - - - - - - - - - -
- Numero - - {{ qcm.id }} -
- Titre - - {{ qcm.title|safe }} -
- Création - - {{ qcm.datetime_formated() }} -
- Questions - - {{ qcm.question_count }} -
- Réponses - - {{ qcm.count_works() }} -
-
-
-
-
-
-
-
-
- -
{% if qcm.has_works() %}

diff --git a/src/templates/view.html b/src/templates/view.html index 8ec4ba4..7956321 100644 --- a/src/templates/view.html +++ b/src/templates/view.html @@ -30,11 +30,14 @@

+
+ {% include('view_menu.html', qcm) %} +
{% include('marks.html', qcm) %}
-
- {% include('graphs.html', qcm) %} + diff --git a/src/templates/view_menu.html b/src/templates/view_menu.html new file mode 100644 index 0000000..7f51ca0 --- /dev/null +++ b/src/templates/view_menu.html @@ -0,0 +1,106 @@ +{% if qcm %} + +
+ + + + + + + + + + + + + + + + + + + + + +
+ Numero + + {{ qcm.id }} +
+ Titre + + {{ qcm.title|safe }} +
+ Création + + {{ qcm.datetime_formated() }} +
+ Questions + + {{ qcm.question_count }} +
+ Réponses + + {{ qcm.count_works() }} +
+
+
+
+
+
+
+
+
+
+ + + {% if qcm.has_works() %} + + {% endif %} + + +
+
+ +{% endif %} From c1166744485c9494c2f84fa308c781d7eab44a2b Mon Sep 17 00:00:00 2001 From: qkzk Date: Fri, 3 Jun 2022 15:04:12 +0200 Subject: [PATCH 5/6] Use local storage for chart/mark setting. --- src/static/javascript/chart_mark_toggle.js | 20 +++++++++++++++++++ .../javascript/load_mark_chart_setting.js | 11 ++++++++++ src/templates/view.html | 4 +++- src/templates/view_menu.html | 19 +----------------- 4 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 src/static/javascript/chart_mark_toggle.js create mode 100644 src/static/javascript/load_mark_chart_setting.js diff --git a/src/static/javascript/chart_mark_toggle.js b/src/static/javascript/chart_mark_toggle.js new file mode 100644 index 0000000..268e432 --- /dev/null +++ b/src/static/javascript/chart_mark_toggle.js @@ -0,0 +1,20 @@ +const toggle_chart = document.getElementById("toggle_chart"); +toggle_chart.addEventListener("click", toogle_details); + +function toogle_details() { + const marks_elm = document.getElementById("qcm-marks"); + const chart_elm = document.getElementById("qcm-chart"); + if (marks_elm.style.display != 'none') { + marks_elm.style.display = 'none'; + chart_elm.style.display = 'block'; + toggle_chart.innerText = "Notes"; + localStorage.setItem("mark_chart", "chart"); + } + else { + marks_elm.style.display = 'block'; + chart_elm.style.display = 'none'; + toggle_chart.innerText = "Graphes"; + localStorage.setItem("mark_chart", "mark"); + } +} + diff --git a/src/static/javascript/load_mark_chart_setting.js b/src/static/javascript/load_mark_chart_setting.js new file mode 100644 index 0000000..df675a5 --- /dev/null +++ b/src/static/javascript/load_mark_chart_setting.js @@ -0,0 +1,11 @@ +let curr_chart_mark = localStorage.getItem("mark_chart"); +const qcm_chart_elm = document.getElementById("qcm-chart"); +const qcm_mark_elm = document.getElementById("qcm-marks"); +if (curr_chart_mark === "chart") { + qcm_chart_elm.style.display = "block"; + qcm_mark_elm.style.display = "none"; +} else { + qcm_chart_elm.style.display = "none"; + qcm_mark_elm.style.display = "block"; +} + diff --git a/src/templates/view.html b/src/templates/view.html index 7956321..713f07a 100644 --- a/src/templates/view.html +++ b/src/templates/view.html @@ -36,9 +36,11 @@

{% include('marks.html', qcm) %}
-

- {% endif %} From eb3836469804be96bb13f910786178a5d540e4cc Mon Sep 17 00:00:00 2001 From: qkzk Date: Fri, 3 Jun 2022 15:20:00 +0200 Subject: [PATCH 6/6] Chart labels: font color & size. --- src/static/javascript/pie_graph_setup.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/static/javascript/pie_graph_setup.js b/src/static/javascript/pie_graph_setup.js index 6df0fca..f81bc42 100644 --- a/src/static/javascript/pie_graph_setup.js +++ b/src/static/javascript/pie_graph_setup.js @@ -43,14 +43,20 @@ function createChart(chartId, chartData, colorScale, colorRangeInfo) { /* Create chart */ - const myChart = new Chart(chartElement, { + return new Chart(chartElement, { type: 'pie', data: chartData, options: { plugins: { legend: { display: true, - position: 'right' + position: 'right', + labels: { + color: "#888888", + font: { + size: 14, + } + }, } }, radius: 150, @@ -63,7 +69,6 @@ function createChart(chartId, chartData, colorScale, colorRangeInfo) { } }); - return myChart; } const colorScale = d3.interpolateSpectral;