Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Graph marks #60

Merged
merged 6 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 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
Expand Down
8 changes: 8 additions & 0 deletions src/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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):
"""
Expand Down
15 changes: 15 additions & 0 deletions src/static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -392,3 +398,12 @@ footer {
.wsh-table {
min-width: rem-calc(640);
}

.div_pie_chart {
width: 600px;
height: 600px;
}

#toggle_chart {
color: var(--main-text-color);
}
20 changes: 20 additions & 0 deletions src/static/javascript/chart_mark_toggle.js
Original file line number Diff line number Diff line change
@@ -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");
}
}

11 changes: 11 additions & 0 deletions src/static/javascript/load_mark_chart_setting.js
Original file line number Diff line number Diff line change
@@ -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";
}

80 changes: 80 additions & 0 deletions src/static/javascript/pie_graph_setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

/* 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 */
return new Chart(chartElement, {
type: 'pie',
data: chartData,
options: {
plugins: {
legend: {
display: true,
position: 'right',
labels: {
color: "#888888",
font: {
size: 14,
}
},
}
},
radius: 150,
hover: {
onHover: function(e) {
var point = this.getElementAtEvent(e);
e.target.style.cursor = point.length ? 'pointer' : 'default';
},
},
}
});

}

const colorScale = d3.interpolateSpectral;

const colorRangeInfo = {
colorStart: 0.2,
colorEnd: 0.8,
useEndAsStart: false,
};
70 changes: 70 additions & 0 deletions src/templates/chart.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{% if qcm and qcm.count_works() > 0 %}
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.min.js"
integrity="sha256-cHVO4dqZfamRhWD7s4iXyaXWVK10odD+qp4xidFzqTI="
crossorigin="anonymous"
>
</script>
<script src="https://d3js.org/d3-color.v1.min.js"></script>
<script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="{{ url_for('static', filename='javascript/pie_graph_setup.js') }}"></script>
<div class="question-answers">
<h2>
Détail des réponses
</h2>
</div>
{% for question in qcm.flat_questions() %}
<div class="question-answers">
<h3>
{{ question.question|safe }}
</h3>
<p>
{{ question.sub_text|safe }}
</p>
{% if question.is_text_question %}
<ul>
{% for text in question.texts %}
<li>
{{ text.text | safe }}
</li>
{% endfor %}
</ul>
{% else %}
<div class="div_pie_chart center">
<canvas id="question_chart_{{question.id}}"></canvas>
</div>
<script>
const data_{{question.id}} = {
labels: [
{% for answer in question.answers %}
{% if answer.is_valid %}
'✔️ ' +
{% endif %}
htmlDecode(
'{{ answer.answer|safe|striptags }}'
),
{% endfor %}
],
datasets: [{
label: '{{ question.question }}',
data: [
{% for answer in question.answers %}
{{answer.nb_of_selection()}},
{% endfor %}
],
hoverOffset: 0
}]
};

const question_chart_{{question.id}} = new createChart(
'question_chart_{{question.id}}',
data_{{question.id}},
colorScale,
colorRangeInfo
);
</script>
{% endif %}
</div>
{% endfor %}
{% endif %}
82 changes: 0 additions & 82 deletions src/templates/marks.html
Original file line number Diff line number Diff line change
@@ -1,86 +1,4 @@
{% if qcm %}
<script src="{{ url_for('static', filename='javascript/sort_table.js') }}">
</script>
<div class="question-answers">
<table class="center colored-background" style="border: none; outline: none">
<tr>
<td class="no-border">
Numero
</td>
<td class="no-border bold">
{{ qcm.id }}
</td>
</tr>
<tr>
<td class="no-border">
Titre
</td>
<td class="no-border bold">
{{ qcm.title|safe }}
</td>
</tr>
<tr>
<td class="no-border">
Création
</td>
<td class="no-border bold">
{{ qcm.datetime_formated() }}
</td>
</tr>
<tr>
<td class="no-border">
Questions
</td>
<td class="no-border bold">
{{ qcm.question_count }}
</td>
</tr>
<tr>
<td class="no-border">
Réponses
</td>
<td class="no-border bold">
{{ qcm.count_works() }}
</td>
</tr>
</table>
<div class="spacediv">
</div>
<div class="spacediv">
</div>
<div class="spacediv">
</div>
<div class="spacediv">
</div>
<div style="text-align: center;">
<button>
<a href="{{ url_for('preview', id_qcm=qcm.id) }}">
Voir le QCM
</a>
</button>
<button>
<a href="{{ url_for('remove', id_qcm=qcm.id) }}">
Supprimer le QCM
</a>
</button>
{% if qcm.has_works() %}
<button>
<a href="{{ url_for('export', id_qcm=qcm.id) }}">
Exporter en CSV
</a>
</button>
{% endif %}
<button>
<a href="{{ url_for('toggle_open', id_qcm=qcm.id) }}">
{% if qcm.is_open %}
Fermer
{% else %}
Ouvrir
{% endif %}
</a>
</button>
</div>
</div>
{% if qcm.has_works() %}
<div class="question-answers">
<h3>
Expand Down
8 changes: 8 additions & 0 deletions src/templates/view.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,17 @@ <h1 class="big-text" id="qcm-id-title">
</h1>
</div>
</div>
<div class="part" id="qcm-view-menu">
{% include('view_menu.html', qcm) %}
</div>
<div class="part" id="qcm-marks">
{% include('marks.html', qcm) %}
</div>
<div class="part" id="qcm-chart">
{% include('chart.html', qcm) %}
</div>
<script src="{{ url_for('static', filename='javascript/id_qcm_popup.js') }}">
</script>
<script src='{{ url_for('static', filename='javascript/load_mark_chart_setting.js') }}'>
</script>
{% endblock %}
Loading