Commit 77dfed2a authored by Julien Palard's avatar Julien Palard
Browse files

Button to warn about unhelpfull corrections (with proper i18n in js).

parent 3df1d89e
......@@ -30,6 +30,7 @@ Templates are translated using django `makemessages` and `compilemessages` comma
```
$ ./manage.py makemessages --locale fr
$ ./manage.py makemessages --locale fr --domain djangojs --extension html
# Edit the .po files
$ ./manage.py compilemessages
```
......
......@@ -17,6 +17,7 @@ from django.contrib import admin
from django.shortcuts import redirect
from django.conf import settings
from django.urls import path, include
from django.views.i18n import JavaScriptCatalog
from website.api import router
from website.views import (
ExerciseListView,
......@@ -34,6 +35,7 @@ from website.views import (
urlpatterns = [
path("", index, name="index"),
path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"),
path("about/", about, name="about"),
path("help/", helppage, name="help"),
path("team/", team, name="team"),
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-11-10 10:54+0100\n"
"POT-Creation-Date: 2020-11-12 23:48+0100\n"
"PO-Revision-Date: 2020-09-28 13:20+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-11-13 00:02+0100\n"
"PO-Revision-Date: 2020-11-13 00:03+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: templates/admin/base_site.html:3
msgid "Django site admin"
msgstr "Admin de Django"
......@@ -22,11 +22,14 @@ logger = logging.getLogger(__name__)
@database_sync_to_async
def db_store_correction(answer, is_valid, message):
answer = Answer.get(answer["id"])
answer.is_valid = is_valid
answer.correction_message = message
def db_flag_as_unhelpfull(user_id: int, answer_id: int):
try:
answer = Answer.objects.get(user_id=user_id, id=answer_id)
except Answer.DoesNotExist:
return False
answer.is_unhelpfull = True
answer.save()
return answer
@database_sync_to_async
......@@ -130,6 +133,8 @@ class ExerciseConsumer(AsyncJsonWebsocketConsumer):
async def receive_json(self, content):
if content["type"] == "answer":
asyncio.create_task(self.answer(content["source_code"]))
elif content["type"] == "is_unhelpfull":
asyncio.create_task(self.flag_as_unhelpfull(content["answer_id"]))
elif content["type"] == "recorrect":
asyncio.create_task(self.recorrect(content))
elif content["type"] == "snippet":
......@@ -139,6 +144,15 @@ class ExerciseConsumer(AsyncJsonWebsocketConsumer):
else:
self.log("Unknown message received", json.dumps(content))
async def flag_as_unhelpfull(self, answer_id: str):
try:
answer_id = int(answer_id)
except ValueError:
return
answer = await db_flag_as_unhelpfull(self.scope["user"].id, answer_id)
if answer:
await self.send_json(answer_message(answer))
async def recorrect(self, answer):
self.log("Restarting correction for an answer")
uncorrected = await db_find_uncorrected(answer["id"], self.scope["user"])
......
This diff is collapsed.
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-11-13 00:02+0100\n"
"PO-Revision-Date: 2020-11-13 00:03+0100\n"
"Last-Translator: Julien Palard <julien@palard.fr>\n"
"Language-Team: fr <julien@palard.fr>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: website/templates/hkis/exercise.html:84
#, javascript-format
msgid "<a href=\"%s\">Login</a> to backup your code and progression."
msgstr ""
"<a href=\"%s\">Connectez-vous</a> pour sauvegarder votre code et votre "
"progression."
#: website/templates/hkis/exercise.html:92
msgid ""
"Flag this answer as particularly unhelpfull,\n"
"It'll be reviewed by a human."
msgstr ""
"Marquer cette correction comme particulièrement innutile,\n"
"un humain passera la relire."
#: website/templates/hkis/exercise.html:95
msgid "Report"
msgstr "Signaler"
#: website/templates/hkis/exercise.html:102
msgid "Reported"
msgstr "Signalé"
#: website/templates/hkis/exercise.html:103
msgid "Thanks for the feedback!"
msgstr "Merci, je regarde ça bientôt !"
#: website/templates/hkis/exercise.html:110
msgid "Waiting for correction..."
msgstr "Correction en cours…"
#: website/templates/hkis/exercise.html:133
msgid "Waiting for test run"
msgstr "Exécution en cours…"
#: website/templates/hkis/exercise.html:156
msgid "Connecting to correction server…"
msgstr "Connexion au serveur de correction en cours…"
#: website/templates/hkis/exercise.html:173
msgid "Connected to correction server."
msgstr "Connecté au serveur de correction!"
#: website/templates/hkis/exercise.html:184
msgid "Connection to correction server lost, will retry in 5s…"
msgstr ""
"La connexion au serveur de correction a échouée. Nouvelle tentative dans 5s…"
#: website/templates/hkis/exercise.html:187
msgid "Cannot connect to correction server, will retry in 5s…"
msgstr ""
"Impossible de se connecter au serveur de correction. Nouvelle tentative dans "
"5s…"
#: website/templates/hkis/exercise.html:188
#, javascript-format
msgid "(See the <a href='%s'#FAQ'>FAQ</a> if it persists.)"
msgstr "(Consultez la <a href='%s#FAQ'>FAQ</a> si le problème persiste.)"
......@@ -8,6 +8,7 @@
<title>HackInScience - {% block title %}{% trans "Welcome!" %}{% endblock %}</title>
<link href="{% static "bootstrap.min.css" %}" rel="stylesheet">
<link href="{% static "css/hkis.css" %}" rel="stylesheet" type="text/css" />
<script src="{% url 'javascript-catalog' %}"></script>
<script>
var HKIS_SETTINGS = {
{% get_current_language as LANGUAGE_CODE %}
......
......@@ -14,62 +14,100 @@
{% block extrahead %}
{% autoescape off %}
{% url 'profile' user.id as profile_url %}
{% url "profile" user.id as profile_url %}
<script>
if (typeof Element.prototype.swapChild === 'undefined') {
Object.defineProperty(Element.prototype, 'swapChild', {
configurable: true,
enumerable: false,
value: function(newChild) {
while (this.firstChild) this.removeChild(this.lastChild);
this.appendChild(newChild);
}
});
}
var hkis = {
createElement: function(tagName, attrs, dataset) {
el = document.createElement(tagName)
for (const property in attrs)
el[property] = attrs[property]
if (typeof dataset != "undefined")
for (const property in dataset)
el.dataset[property] = dataset[property]
return el
}
}
function append_to_answer_table(html) {
if (document.querySelectorAll("#answer-table tr").length)
document.querySelectorAll("#answer-table tr")[0].insertAdjacentHTML('beforebegin', html);
document.querySelectorAll("#answer-table tr")[0].insertAdjacentHTML("beforebegin", html);
else
document.querySelectorAll("#answer-table")[0].insertAdjacentHTML('afterbegin', html);
document.querySelectorAll("#answer-table")[0].insertAdjacentHTML("afterbegin", html);
}
function report_as_unhelpfull(answer_id) {
window.ws.send(JSON.stringify({type: "is_unhelpfull", answer_id: answer_id}));
return false;
}
function fill_answer(answer) {
var result = document.getElementById("answers-result");
if (!result)
return;
var elem = document.getElementById("td-".concat(answer.id));
if (!elem) {
var answerbox = document.getElementById("td-".concat(answer.id));
if (!answerbox) {
var answer_table = document.getElementById("answer-table");
append_to_answer_table('<tr><td id="td-'.concat(answer.id).concat('">…</td> </tr>'));
elem = document.getElementById("td-".concat(answer.id));
answerbox = document.getElementById("td-".concat(answer.id));
}
if (answer.is_corrected) {
var answer_table = document.getElementById("answer-table");
var div = document.createElement("div");
var button_next = document.createElement("a");
button_next.className = 'btn btn-primary btn-sm'
button_next.innerHTML = '{% trans "Next" %}'
button_next.href = "{% url "exercise" next %}"
var button_help = document.createElement("a")
button_help.innerHTML = '{% trans "Need help?" %}'
button_help.href = "{% url 'help' %}"
div.innerHTML = answer.correction_message_html;
var div = hkis.createElement("div", {innerHTML: answer.correction_message_html});
if (answer.is_valid) {
console.log("Answer is valid: old_rank={{current_rank}}, new_rank=", answer.user_rank)
document.getElementById("btn_next").className = "btn btn-primary"
document.getElementById("submit_answer").className = "btn btn-secondary"
answer_table.rows[0].className = "table-success";
if (answer.user && answer.user_rank < {{ current_rank }}) {
{% url 'profile' user.id as profile_url %}
var message = '<div class="alert alert-success">{% blocktrans %}Your new <a href="{{ profile_url }}">rank</a> is: NEW_RANK{% endblocktrans %}</div>'.replace('NEW_RANK', answer.user_rank);
div.insertAdjacentHTML('beforeend', message);
{% url "profile" user.id as profile_url %}
div.appendChild(hkis.createElement("div", {
className: "alert alert-success",
innerHTML: interpolate(gettext('Your new <a href="%(url)s">rank</a> is: %(new_rank)s'),
{url: "{{ profile_url }}", new_rank: answer.user_rank},
true)}));
}
if (!answer.user) {
{% url 'auth_login' as login_url %}
var message = '<div class="alert alert-warning">{% blocktrans %}<a href="{{ login_url }}">Login</a> to backup your code and progression.{% endblocktrans %}</div>';
div.insertAdjacentHTML('beforeend', message);
div.appendChild(hkis.createElement("div", {
className: "alert alert-warning",
innerHTML: interpolate(gettext('<a href="%s">Login</a> to backup your code and progression.'), ["{% url "auth_login" %}"])
}));
}
div.appendChild(button_next);
document.getElementById("solution_link").classList.remove("disabled");
}
else {
answer_table.rows[0].className = "table-danger";
div.appendChild(button_help);
var button_unhelpfull = hkis.createElement("button", {
title: gettext("Flag this answer as particularly unhelpfull,\nIt'll be reviewed by a human."),
className: "btn, btn-secondary btn-sm",
onclick: function() { report_as_unhelpfull(answer.id); return false},
innerHTML: gettext("Report")
}, {
toggle: "tooltip"
});
if (answer.is_unhelpfull) {
button_unhelpfull.disabled = true;
button_unhelpfull.innerHTML = gettext("Reported")
button_unhelpfull.title = gettext("Thanks for the feedback!")
}
div.appendChild(button_unhelpfull);
}
while (elem.firstChild) elem.removeChild(elem.firstChild);
elem.appendChild(div);
unlock_button('submit_answer');
answerbox.swapChild(div)
unlock_button("submit_answer")
} else {
elem.innerHTML = '<img src="{% static "img/loading_anim.gif" %}" height=30> {% trans "Waiting for correction" %}';
answerbox.innerHTML = gettext("Waiting for correction...")
}
}
......@@ -89,11 +127,10 @@
div.appendChild(pre);
var content = document.createTextNode(output);
pre.appendChild(content);
while (elem.firstChild) elem.removeChild(elem.firstChild);
elem.appendChild(div);
unlock_button('submit_answer');
elem.swapChild(div);
unlock_button("submit_answer");
} else {
elem.innerHTML = '<img src="{% static "img/loading_anim.gif" %}" height=30> {% trans "Waiting for test run" %}';
elem.innerHTML = gettext("Waiting for test run");
}
}
......@@ -107,16 +144,16 @@
var first_cell = null;
if (first_row)
first_cell = first_row.getElementsByTagName("td")[0];
var html = '<div class="alert alert-' + type + '">' + message + '</td> </tr>';
var html = '<div class="alert alert-' + type + '">' + message + "</td> </tr>";
if (first_cell && !first_cell.id)
first_cell.innerHTML = html
else
append_to_answer_table('<tr><td>' + html + '</td></tr>');
append_to_answer_table("<tr><td>" + html + "</td></tr>");
}
function websocket_connect(ws_location) {
var connected = false;
fill_message("{% trans "Connecting to correction server" %}", "info");
fill_message(gettext("Connecting to correction server…"), "info");
window.ws = new WebSocket(ws_location);
window.ws.onmessage = function(e) {
var data = JSON.parse(e.data);
......@@ -133,7 +170,7 @@
unlock_button("submit_answer");
unlock_button("submit_snippet");
connected = true;
fill_message("{% trans "Connected to correction server." %}", "success");
fill_message(gettext("Connected to correction server."), "success");
window.ws.send(JSON.stringify({type: "settings", value: HKIS_SETTINGS}));
document.querySelectorAll("td.answer-cell").forEach(function(answer) {
if (answer.dataset.isCorrected == "True") return ;
......@@ -144,11 +181,11 @@
window.ws.onclose = function(event) {
console.log("onclose", event);
if (connected)
fill_message("{% trans "Connection to correction server lost, will retry in 5s" %}", "warning");
fill_message(gettext("Connection to correction server lost, will retry in 5s…"), "warning");
else
{% url 'help' as help_url %}
fill_message("{% trans "Cannot connect to correction server, will retry in 5s" %}" + "<br/>" +
"{% blocktrans %}(See the <a href='{{ help_url }}#FAQ'>FAQ</a> if it persists.){% endblocktrans %}", "warning");
fill_message(gettext("Cannot connect to correction server, will retry in 5s…") + "<br/>" +
interpolate(gettext("(See the <a href='%s'#FAQ'>FAQ</a> if it persists.)"), ["{% url "help" %}"]), "warning");
setTimeout(function(){websocket_connect(ws_location)}, 5000);
lock_button("submit_answer", 0);
lock_button("submit_snippet", 0);
......@@ -164,18 +201,18 @@
}
function unlock_button(button_id) {
document.getElementById(button_id).removeAttribute('disabled');
document.getElementById(button_id).removeAttribute("disabled");
}
function lock_button(button_id, unlock_after_seconds) {
document.getElementById(button_id).setAttribute('disabled', true);
document.getElementById(button_id).setAttribute("disabled", true);
if (unlock_after_seconds)
setTimeout(function(){unlock_button(button_id);}, unlock_after_seconds * 1000);
}
function ws_submit_snippet(form) {
var message = {"type": "snippet", "source_code": form["source_code"].value};
lock_button('submit_snippet', 1);
lock_button("submit_snippet", 1);
console.log("WebSocket send snippet", message);
window.ws.send(JSON.stringify(message));
......@@ -190,9 +227,9 @@
var ws_protocol = window.location.protocol == "http:" ? "ws:" : "wss:";
{% if not is_impersonating %}
window.addEventListener('DOMContentLoaded', function (event) {
window.addEventListener("DOMContentLoaded", function (event) {
websocket_connect(ws_protocol + "//" + window.location.host + "/ws/exercises/" + {{object.id}} + "/");
document.addEventListener('keydown', ctrl_enter_handler);
document.addEventListener("keydown", ctrl_enter_handler);
});
{% endif %}
</script>
......@@ -262,7 +299,7 @@
</a>
{% endif %}
{% if next %}
<a class="btn btn-secondary" href="{% url "exercise" next %}">
<a id="btn_next" class="btn btn-secondary" href="{% url "exercise" next %}">
</a>
{% endif %}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment