diff options
Diffstat (limited to 'kpov_judge/web')
-rw-r--r-- | kpov_judge/web/kpov_judge/babel.cfg | 3 | ||||
-rwxr-xr-x | kpov_judge/web/kpov_judge/kpov_judge.py | 75 | ||||
-rw-r--r-- | kpov_judge/web/kpov_judge/static/style.css | 64 | ||||
-rw-r--r-- | kpov_judge/web/kpov_judge/templates/class_tasks.html | 16 | ||||
-rw-r--r-- | kpov_judge/web/kpov_judge/templates/index.html | 24 | ||||
-rw-r--r-- | kpov_judge/web/kpov_judge/templates/task_greeting.html | 125 | ||||
-rw-r--r-- | kpov_judge/web/kpov_judge/translations/en/LC_MESSAGES/messages.po | 102 | ||||
-rw-r--r-- | kpov_judge/web/kpov_judge/translations/sl/LC_MESSAGES/messages.po | 107 |
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 "" + |