summaryrefslogtreecommitdiff
path: root/kpov_judge/web
diff options
context:
space:
mode:
Diffstat (limited to 'kpov_judge/web')
-rw-r--r--kpov_judge/web/kpov_judge/babel.cfg3
-rwxr-xr-xkpov_judge/web/kpov_judge/kpov_judge.py75
-rw-r--r--kpov_judge/web/kpov_judge/static/style.css64
-rw-r--r--kpov_judge/web/kpov_judge/templates/class_tasks.html16
-rw-r--r--kpov_judge/web/kpov_judge/templates/index.html24
-rw-r--r--kpov_judge/web/kpov_judge/templates/task_greeting.html125
-rw-r--r--kpov_judge/web/kpov_judge/translations/en/LC_MESSAGES/messages.po102
-rw-r--r--kpov_judge/web/kpov_judge/translations/sl/LC_MESSAGES/messages.po107
8 files changed, 431 insertions, 85 deletions
diff --git a/kpov_judge/web/kpov_judge/babel.cfg b/kpov_judge/web/kpov_judge/babel.cfg
new file mode 100644
index 0000000..f0234b3
--- /dev/null
+++ b/kpov_judge/web/kpov_judge/babel.cfg
@@ -0,0 +1,3 @@
+[python: **.py]
+[jinja2: **/templates/**.html]
+extensions=jinja2.ext.autoescape,jinja2.ext.with_
diff --git a/kpov_judge/web/kpov_judge/kpov_judge.py b/kpov_judge/web/kpov_judge/kpov_judge.py
index 1085b9b..c0bf885 100755
--- a/kpov_judge/web/kpov_judge/kpov_judge.py
+++ b/kpov_judge/web/kpov_judge/kpov_judge.py
@@ -1,18 +1,33 @@
#!/usr/bin/env python3
-import flask
-from flask import Flask, g, session, redirect, url_for, abort, render_template, flash, app, request, Response
-import pymongo
+import collections
+import datetime
import json
import random
import settings
-import datetime
-import kpov_util
-from kpov_draw_setup import draw_setup
import traceback
+from kpov_draw_setup import draw_setup
+import kpov_util
+
+import pymongo
+import flask
+from flask import Flask, g, session, redirect, url_for, abort, render_template, flash, app, request, Response
+from flask.ext.babel import Babel, gettext, ngettext, format_datetime, _
+import jinja2
+
app = Flask(__name__)
app.config.from_object(settings)
+babel = Babel(app)
+
+@babel.localeselector
+def get_locale():
+ # terrible hack, should store as user preference in the DB
+ if '/en/' in request.path:
+ return 'en'
+ if '/si/' in request.path:
+ return 'sl'
+ return request.accept_languages.best_match(['sl', 'en'])
dummy_task = """
def task(neznano_ime_naloge):
@@ -36,9 +51,9 @@ def before_request():
@app.route('/')
-def root():
+@app.route('/classes/')
+def index():
student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody')
- # env = flask.app.request.environ
classes = g.db.classes.find({}, {'class_id': 1, 'name': 1}).sort('class_id')
return render_template('index.html', student_id=student_id, classes=classes)
@@ -46,7 +61,6 @@ def root():
@app.route('/classes/<class_id>/')
def class_tasks(class_id):
student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody')
- # env = flask.app.request.environ
clas = g.db.classes.find_one({'class_id': class_id})
tasks = g.db.tasks.find({'class_id': class_id}, {'task_id': 1}).sort('task_id')
if tasks is not None:
@@ -56,14 +70,6 @@ def class_tasks(class_id):
return render_template('class_tasks.html', student_id=student_id, tasks=task_list, clas=clas)
-@app.route('/classes/')
-def class_list():
- student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody')
- # env = flask.app.request.environ
- classes = g.db.classes.find({}, {'class_id': 1, 'name': 1,}).sort('name')
- return render_template('class_list.html', student_id=student_id, classes=classes)
-
-
def results_post(class_id, task_id, results):
student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody')
db = g.db
@@ -86,8 +92,7 @@ def results_post(class_id, task_id, results):
task_check_source = db.task_checkers.find_one({'class_id': class_id, 'task_id': task_id})['source']
d = {}
exec(compile(task_check_source, 'checker.py', 'exec'), globals(), d)
- # raise Exception(str(params))
- res, hints = d['task_check'](results, params)
+ res, hints = d['task_check'](collections.defaultdict(str, results), params)
except Exception as e:
hints = ["Checker died: " + str(e)]
res = 0
@@ -240,14 +245,14 @@ def task_greeting(class_id, task_id, lang):
instr_ok = False
if instr_ok:
try:
- public_params = {}
+ public_params = []
for k, v in meta.items():
if v.get('public', False):
- public_params[k] = params.get(k, "???")
- # instructions = instructions.format(**public_params).decode('utf8')
- instructions = instructions.format(**public_params)
-
- # instructions = instructions.format(**public_params).encode('utf8')
+ public_params += [{
+ 'name': k,
+ 'value': params.get(k),
+ 'description': v.get('descriptions', {}).get(lang)
+ }]
except Exception as e:
instructions = str(e)
computer_list = list(db.student_computers.find({'class_id': class_id, 'task_id': task_id, 'student_id': student_id}))
@@ -265,13 +270,29 @@ def task_greeting(class_id, task_id, lang):
openstackCreated = True
else:
openstackCreated = False
+
+ try:
+ result = db.results.find_one(
+ {'$query': {'class_id': class_id, 'task_id': task_id, 'student_id': student_id},
+ '$orderby': collections.OrderedDict([('result', -1), ('time', 1)])},
+ {'result': 1, 'status': 1, 'hints': 1, 'time': True, '_id': 0})
+ result['time'] = format_datetime(result['time'])
+ print(result)
+ except Exception:
+ result = None
+
return render_template('task_greeting.html',
disk_base_url='/'.join([app.config['STUDENT_DISK_URL'], student_id, class_id, task_id, '']),
+ class_id=class_id,
+ task_id=task_id,
computers=computer_list,
backing_images=sorted(backing_images),
- lang=lang,
+ lang='sl' if lang == 'si' else lang, # TODO s/si/sl in all tasks (and maybe elsewhere)
openstack=openstackCreated,
- instructions=instructions)
+ instructions=jinja2.Template(instructions),
+ params=public_params,
+ result=result,
+ **{p['name']: p['value'] for p in public_params})
@app.route('/tasks/<class_id>/<task_id>/params.json', methods=['GET', 'POST'])
diff --git a/kpov_judge/web/kpov_judge/static/style.css b/kpov_judge/web/kpov_judge/static/style.css
index 9be13f8..4511302 100644
--- a/kpov_judge/web/kpov_judge/static/style.css
+++ b/kpov_judge/web/kpov_judge/static/style.css
@@ -1,15 +1,55 @@
-.hover {
- position:relative;
+a.back {
+ text-decoration: none;
+}
+
+body {
+ margin: 0 auto;
+ max-width: 60em;
+ padding: 1em 2em;
+}
+
+code {
+ background-color: #d6d6d6;
+ color: black;
+ font-size: 1.2em;
+ padding: 0.20em 0.25em 0;
+}
+
+dl {
+ margin-top: 0.5em;
+}
+dt {
+ margin-top: 0.5em;
+}
+dd {
+ margin-left: 1em;
+}
+
+h1 {
+ margin-bottom: 0.5em;
+}
+
+p {
+ hyphens: auto;
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ padding-top: 0;
+ text-align: justify;
+}
+
+pre {
+ margin-left: 1em;
+ overflow: auto;
}
.tooltip { /* hide and position tooltip */
- top:1em;
- left:1em;
- background-color:black;
- color:white;
- border-radius:5px;
- opacity:0;
- position:absolute;
+ top: 1em;
+ left: 1em;
+ background-color: black;
+ color: white;
+ border-radius: 5px;
+ opacity: 0;
+ position: absolute;
-webkit-transition: opacity 0.5s;
-moz-transition: opacity 0.5s;
-ms-transition: opacity 0.5s;
@@ -17,6 +57,10 @@
transition: opacity 0.5s;
}
+.hover {
+ position: relative;
+}
+
.hover:hover .tooltip { /* display tooltip on hover */
- opacity:1;
+ opacity: 1;
}
diff --git a/kpov_judge/web/kpov_judge/templates/class_tasks.html b/kpov_judge/web/kpov_judge/templates/class_tasks.html
new file mode 100644
index 0000000..4b6a523
--- /dev/null
+++ b/kpov_judge/web/kpov_judge/templates/class_tasks.html
@@ -0,0 +1,16 @@
+<html>
+<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
+
+<h1><a href="{{ url_for('index') }}" class="back">↩</a> {{clas.name}}</h1>
+
+<p>
+{{ _('Zdravo, %(student)s.', student=student_id) }}
+
+<section>
+<h1>{{ _('Naloge') }}</h1>
+
+<ul>
+{% for t in tasks %}
+ <li><a href="{{url_for('task_lang_redirect', class_id=clas.class_id, task_id=t)}}">{{t}}</a>
+{% endfor %}
+</ul>
diff --git a/kpov_judge/web/kpov_judge/templates/index.html b/kpov_judge/web/kpov_judge/templates/index.html
index b5149ab..4c2ba47 100644
--- a/kpov_judge/web/kpov_judge/templates/index.html
+++ b/kpov_judge/web/kpov_judge/templates/index.html
@@ -1,20 +1,16 @@
<html>
-<body>
+<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
+
+<h1>KPOV Judge</h1>
+
<p>
-Zdravo, {{student_id}}
-</p>
-<p>
-{{env}}
-</p>
-<p>
-Trenutno so na voljo predmeti:
+{{ _('Zdravo, %(student)s.', student=student_id) }}
+
+<section>
+<h1>{{ _('Predmeti') }}</h1>
+
<ul>
{% for c in classes %}
-<li><a href="{{url_for('class_tasks', class_id=c.class_id)}}">{{c.name}}</a></li>
+ <li><a href="{{url_for('class_tasks', class_id=c.class_id)}}">{{c.name}}</a>
{% endfor %}
</ul>
-</li>
-</p>
-<p>Navodila lahko najdete <a href="http://lusy.fri.uni-lj.si/en/kpov-vaje">na strani LUSY</a>.
-</body>
-</html>
diff --git a/kpov_judge/web/kpov_judge/templates/task_greeting.html b/kpov_judge/web/kpov_judge/templates/task_greeting.html
index 422f14b..49288e3 100644
--- a/kpov_judge/web/kpov_judge/templates/task_greeting.html
+++ b/kpov_judge/web/kpov_judge/templates/task_greeting.html
@@ -1,65 +1,122 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
- "http://www.w3.org/TR/html4/strict.dtd">
-<html>
-<body>
-<h1>{{task_id}}</h1>
-<h2>Navodila</h2>
-<pre>
-{{instructions}}
-</pre>
-<p>
-<p>
-<a href='howto/'>Podrobna navodila (HOWTO)</a>
-</p>
+<html lang="{{lang}}">
-<section class="images">
-<h2>Računalniki v vaji</h2>
+<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
+<style>
+body > h1 > span {
+ font-size: small;
+ font-weight: normal;
+}
+img.setup {
+ border: 1px solid black;
+ border-radius: 0.2em;
+ float: right;
+ margin-left: 1.5em;
+ max-width: 20em;
+}
+section.data {
+ clear: both;
+ columns: 2;
+}
+section.data > section {
+ display: inline-block;
+ margin: 0 1em;
+}
+section.disks > section > h1 {
+ margin-bottom: 0;
+}
+section ul {
+ list-style-position: inside;
+ margin: 0;
+ margin-left: 1em;
+ padding-left: 0;
+}
+section > ul {
+ margin-top: 0.5em;
+}
+</style>
+
+<h1>
+<a href="{{ url_for('class_tasks', class_id=class_id) }}" class="back">↩</a> {{task_id}}
+ <span>[
+{% for lang in ('en', 'si') %}
+<a href="{{ url_for('task_greeting', class_id=class_id, task_id=task_id, lang=lang) }}">{{lang}}</a>
+{% endfor %}
+]</span>
+</h1>
<section>
+<a href="setup.png"><img src="setup.png" class="setup"></a>
+
+{% include instructions %}
+
+<p>
+<a href="howto/">{{ _('Podrobna navodila.') }}</a>
+</section>
+
+<section class="data">
+<section class="disks">
+<h1>{{ _('Računalniki') }}</h1>
+<p>
+{{ _('Slike diskov za to nalogo:') }}
+
{% for c in computers %}
- <h3>{{c['name']}}</h3>
+<ul>
+ <li>{{c['name']}}
{% if 'disk_urls' in c %}
<ul>
{% for u in c['disk_urls'] %}
- <li><a href={{disk_base_url+u['file']}}>{{u['name']}}</a></li>
+ <li><a href="{{disk_base_url+u['file']}}">{{u['name']}}</a>
{% endfor %}
</ul>
{% else %}
- Slike navideznih diskov so v izdelavi in bodo kmalu na voljo.
+ <br>{{ _('Slike navideznih diskov so v izdelavi in bodo kmalu na voljo.') }}
{% endif %}
+ </ul>
{% endfor %}
-<p>
-<img src="setup.png">
-</p>
-</section>
-
<section>
{% if backing_images %}
-<h3>Osnovne slike</h3>
+<h1>{{ _('Osnovne slike') }}</h1>
+<p>
+{{ _('Te slike so enake za vse naloge. Prenesite samo tiste, ki jih še nimate.') }}
+
<ul>
{% for b in backing_images %}
- <li><a href={{disk_base_url+b}}>{{b}}</a></li>
+ <li><a href="{{disk_base_url+b}}">{{b}}</a></li>
{% endfor %}
</ul>
{% endif %}
</section>
</section>
+<section>
+<h1>{{ _('Rezultat') }}</h1>
<p>
-Lahko si ogledate surove:
-<ul>
-<li><a href='results.html'>rezultate</a> (<a href='../results.json'>json</a>)</li>
-<li><a href='params.html'>parametre</a> (<a href='../params.json'>json</a>, <a href='../params_meta.json'>opisi parametrov</a>)</li>
-<li><a href='../task.html'>preverjalni program</a> (<a href='../task.py'>source</a>)</li>
-</ul>
-</p>
+{% if result is none %}
+{{ _('Naloga nima še nobenega poskusa.') }}
+{% elif result['result'] == 10 %}
+{{ _('Naloga je uspešno opravljena dne %(time)s.', time=result['time']) }}
+{% else %}
+{{ _('Naloga še ni opravljena, najvišji rezultat je %(score)s.', score=result['result']) }}
+{% endif %}
+
+<p>
+<a href="../task.html">{{ _('Program za preverjanje.') }}</a>
+
+<h1>{{ _('Parametri') }}</h1>
+<dl>
+{% for p in params if p['value'] %}
+ <dt><em>{{ p['name'] }}</em>{% if p['description'] %} … <small>{{ p['description'] }}</small>{% endif %}</dt>
+ <dd><code>{{ p['value'] }}</code></dd>
+{% endfor %}
+</dl>
+</section>
+{#
<p>
{% if openstack %}
Openstack projekt za to vajo je že ustvarjen ali v izdelavi (funkcionalnost še ne deluje).
{% else %}
<a href='?narediStack=true'>Ustvari</a> Openstack projekt za to vajo (funkcionalnost še ne deluje).
{% endif %}
-</body>
-</html>
+#}
diff --git a/kpov_judge/web/kpov_judge/translations/en/LC_MESSAGES/messages.po b/kpov_judge/web/kpov_judge/translations/en/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..ef9a7bb
--- /dev/null
+++ b/kpov_judge/web/kpov_judge/translations/en/LC_MESSAGES/messages.po
@@ -0,0 +1,102 @@
+# English translations for PROJECT.
+# Copyright (C) 2018 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2018-10-13 19:25+0200\n"
+"PO-Revision-Date: 2018-10-13 20:57+0200\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: en <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 1.3\n"
+
+#: templates/class_tasks.html:7 templates/index.html:7
+#, python-format
+msgid "Zdravo, %(student)s."
+msgstr "Hi, %(student)s."
+
+#: templates/class_tasks.html:10
+msgid "Naloge"
+msgstr "Tasks"
+
+#: templates/index.html:10
+msgid "Predmeti"
+msgstr "Classes"
+
+#: templates/task_greeting.html:52
+msgid "Podrobna navodila."
+msgstr "Detailed instructions."
+
+#: templates/task_greeting.html:57
+msgid "Računalniki"
+msgstr "Computers"
+
+#: templates/task_greeting.html:59
+msgid "Slike diskov za to nalogo:"
+msgstr "Disk images for this task:"
+
+#: templates/task_greeting.html:71
+msgid "Slike navideznih diskov so v izdelavi in bodo kmalu na voljo."
+msgstr "Disk images are being generated, please wait."
+
+#: templates/task_greeting.html:78
+msgid "Osnovne slike"
+msgstr "Backing images"
+
+#: templates/task_greeting.html:80
+msgid "Te slike so enake za vse naloge. Prenesite samo tiste, ki jih še nimate."
+msgstr ""
+"These disk images are the same for all exercises. Download only those you"
+" don’t have yet."
+
+#: templates/task_greeting.html:92
+msgid "Rezultat"
+msgstr "Result"
+
+#: templates/task_greeting.html:95
+msgid "Naloga nima še nobenega poskusa."
+msgstr "This task has not been attempted yet."
+
+#: templates/task_greeting.html:97
+#, python-format
+msgid "Naloga je uspešno opravljena dne %(time)s."
+msgstr "This task was solved on %(time)s."
+
+#: templates/task_greeting.html:99
+#, python-format
+msgid "Naloga še ni opravljena, najvišji rezultat je %(score)s."
+msgstr "This task has not been solved yet, the highest score is %(score)s."
+
+#: templates/task_greeting.html:103
+msgid "Program za preverjanje."
+msgstr "Testing program."
+
+#: templates/task_greeting.html:105
+msgid "Parametri"
+msgstr "Parameters"
+
+#~ msgid "Trenutno so na voljo naloge:"
+#~ msgstr "Available tasks:"
+
+#~ msgid "Trenutno so na voljo predmeti:"
+#~ msgstr "Available classes"
+
+#~ msgid "Rezultati"
+#~ msgstr "Results"
+
+#~ msgid "Ogledate si lahko:"
+#~ msgstr "You can view:"
+
+#~ msgid "opisi parametrov"
+#~ msgstr "parameter descriptions"
+
+#~ msgid "preverjalni program"
+#~ msgstr "checking program"
+
diff --git a/kpov_judge/web/kpov_judge/translations/sl/LC_MESSAGES/messages.po b/kpov_judge/web/kpov_judge/translations/sl/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..18458f9
--- /dev/null
+++ b/kpov_judge/web/kpov_judge/translations/sl/LC_MESSAGES/messages.po
@@ -0,0 +1,107 @@
+# Slovenian translations for PROJECT.
+# Copyright (C) 2018 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2018-10-13 19:25+0200\n"
+"PO-Revision-Date: 2018-10-13 02:29+0200\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: sl <LL@li.org>\n"
+"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 "
+"|| n%100==4 ? 2 : 3)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 1.3\n"
+
+#: templates/class_tasks.html:7 templates/index.html:7
+#, python-format
+msgid "Zdravo, %(student)s."
+msgstr ""
+
+#: templates/class_tasks.html:10
+msgid "Naloge"
+msgstr ""
+
+#: templates/index.html:10
+msgid "Predmeti"
+msgstr ""
+
+#: templates/task_greeting.html:52
+msgid "Podrobna navodila."
+msgstr ""
+
+#: templates/task_greeting.html:57
+msgid "Računalniki"
+msgstr ""
+
+#: templates/task_greeting.html:59
+msgid "Slike diskov za to nalogo:"
+msgstr ""
+
+#: templates/task_greeting.html:71
+msgid "Slike navideznih diskov so v izdelavi in bodo kmalu na voljo."
+msgstr ""
+
+#: templates/task_greeting.html:78
+msgid "Osnovne slike"
+msgstr ""
+
+#: templates/task_greeting.html:80
+msgid "Te slike so enake za vse naloge. Prenesite samo tiste, ki jih še nimate."
+msgstr ""
+
+#: templates/task_greeting.html:92
+msgid "Rezultat"
+msgstr ""
+
+#: templates/task_greeting.html:95
+msgid "Naloga nima še nobenega poskusa."
+msgstr ""
+
+#: templates/task_greeting.html:97
+#, python-format
+msgid "Naloga je uspešno opravljena dne %(time)s."
+msgstr ""
+
+#: templates/task_greeting.html:99
+#, python-format
+msgid "Naloga še ni opravljena, najvišji rezultat je %(score)s."
+msgstr ""
+
+#: templates/task_greeting.html:103
+msgid "Program za preverjanje."
+msgstr ""
+
+#: templates/task_greeting.html:105
+msgid "Parametri"
+msgstr ""
+
+#~ msgid "Trenutno so na voljo naloge:"
+#~ msgstr ""
+
+#~ msgid "Trenutno so na voljo predmeti:"
+#~ msgstr ""
+
+#~ msgid "Rezultati"
+#~ msgstr ""
+
+#~ msgid "Ogledate si lahko:"
+#~ msgstr ""
+
+#~ msgid "rezultate"
+#~ msgstr ""
+
+#~ msgid "parametre"
+#~ msgstr ""
+
+#~ msgid "opisi parametrov"
+#~ msgstr ""
+
+#~ msgid "preverjalni program"
+#~ msgstr ""
+