From 8081a5520a441b43a8a7a73f3a90c7aacfaa8e10 Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Sun, 24 Feb 2019 21:05:27 +0100 Subject: Move everything one level up --- web/kpov.wsgi | 1 + web/kpov_judge/babel.cfg | 3 + web/kpov_judge/icons/computer.svg | 1877 +++++++++++ web/kpov_judge/icons/drive-harddisk.svg | 491 +++ web/kpov_judge/icons/internet.svg | 3398 ++++++++++++++++++++ web/kpov_judge/kpov_draw_setup.py | 43 + web/kpov_judge/kpov_judge.py | 289 ++ web/kpov_judge/kpov_util.py | 1 + web/kpov_judge/random_data | 1 + web/kpov_judge/settings.py | 1 + web/kpov_judge/static/icons/computer.png | Bin 0 -> 15600 bytes web/kpov_judge/static/icons/computer.svg | 1877 +++++++++++ web/kpov_judge/static/icons/drive-harddisk.png | Bin 0 -> 1940 bytes web/kpov_judge/static/icons/drive-harddisk.svg | 491 +++ web/kpov_judge/static/icons/internet.png | Bin 0 -> 9438 bytes web/kpov_judge/static/icons/internet.svg | 3398 ++++++++++++++++++++ web/kpov_judge/static/style.css | 72 + web/kpov_judge/templates/class_tasks.html | 16 + web/kpov_judge/templates/index.html | 16 + web/kpov_judge/templates/params.html | 24 + web/kpov_judge/templates/results.html | 15 + web/kpov_judge/templates/task.html | 12 + web/kpov_judge/templates/task_greeting.html | 141 + .../translations/en/LC_MESSAGES/messages.po | 144 + .../translations/sl/LC_MESSAGES/messages.po | 145 + 25 files changed, 12456 insertions(+) create mode 100644 web/kpov.wsgi create mode 100644 web/kpov_judge/babel.cfg create mode 100644 web/kpov_judge/icons/computer.svg create mode 100644 web/kpov_judge/icons/drive-harddisk.svg create mode 100644 web/kpov_judge/icons/internet.svg create mode 100644 web/kpov_judge/kpov_draw_setup.py create mode 100755 web/kpov_judge/kpov_judge.py create mode 120000 web/kpov_judge/kpov_util.py create mode 120000 web/kpov_judge/random_data create mode 120000 web/kpov_judge/settings.py create mode 100644 web/kpov_judge/static/icons/computer.png create mode 100644 web/kpov_judge/static/icons/computer.svg create mode 100644 web/kpov_judge/static/icons/drive-harddisk.png create mode 100644 web/kpov_judge/static/icons/drive-harddisk.svg create mode 100644 web/kpov_judge/static/icons/internet.png create mode 100644 web/kpov_judge/static/icons/internet.svg create mode 100644 web/kpov_judge/static/style.css create mode 100644 web/kpov_judge/templates/class_tasks.html create mode 100644 web/kpov_judge/templates/index.html create mode 100644 web/kpov_judge/templates/params.html create mode 100644 web/kpov_judge/templates/results.html create mode 100644 web/kpov_judge/templates/task.html create mode 100644 web/kpov_judge/templates/task_greeting.html create mode 100644 web/kpov_judge/translations/en/LC_MESSAGES/messages.po create mode 100644 web/kpov_judge/translations/sl/LC_MESSAGES/messages.po (limited to 'web') diff --git a/web/kpov.wsgi b/web/kpov.wsgi new file mode 100644 index 0000000..b55e6ac --- /dev/null +++ b/web/kpov.wsgi @@ -0,0 +1 @@ +from kpov_judge import app as kpovjudge diff --git a/web/kpov_judge/babel.cfg b/web/kpov_judge/babel.cfg new file mode 100644 index 0000000..f0234b3 --- /dev/null +++ b/web/kpov_judge/babel.cfg @@ -0,0 +1,3 @@ +[python: **.py] +[jinja2: **/templates/**.html] +extensions=jinja2.ext.autoescape,jinja2.ext.with_ diff --git a/web/kpov_judge/icons/computer.svg b/web/kpov_judge/icons/computer.svg new file mode 100644 index 0000000..6da674d --- /dev/null +++ b/web/kpov_judge/icons/computer.svg @@ -0,0 +1,1877 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/kpov_judge/icons/drive-harddisk.svg b/web/kpov_judge/icons/drive-harddisk.svg new file mode 100644 index 0000000..5f36f85 --- /dev/null +++ b/web/kpov_judge/icons/drive-harddisk.svg @@ -0,0 +1,491 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Drive - Hard Disk + + + Jakub Steiner + + + + + hdd + hard drive + fixed + media + solid + + + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/kpov_judge/icons/internet.svg b/web/kpov_judge/icons/internet.svg new file mode 100644 index 0000000..5efbd24 --- /dev/null +++ b/web/kpov_judge/icons/internet.svg @@ -0,0 +1,3398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/kpov_judge/kpov_draw_setup.py b/web/kpov_judge/kpov_draw_setup.py new file mode 100644 index 0000000..41f5e01 --- /dev/null +++ b/web/kpov_judge/kpov_draw_setup.py @@ -0,0 +1,43 @@ +import pygraphviz as pgv + +def draw_setup(computers, networks, destination=None, + icon_prefix = '../../../static/icons/', + format='svg', icon_path = '', icon_suffix = None): + if icon_suffix is None: + icon_suffix = format + icon_suffix = '.' + icon_suffix + G = pgv.AGraph(imagepath=icon_path + '/') + print(G.graph_attr) + have_internet = [] + for net in networks: + net_name = net.get('name', 'net') + if net.get('public', False): + have_internet.append(net_name) + G.add_node('net-' + net_name, label=net_name, shape='rectangle') + if len(have_internet): + G.add_node('net-' + 'internet', + label='internet', + labelloc='b', + image=icon_prefix + 'internet' + icon_suffix, + shape='none') + for n in have_internet: + G.add_edge('net-' + n, 'net-internet') + for properties in computers: + c = properties.get('name', '') + label_str = '< ' + label = label_str.format(c, icon_prefix + 'computer' + icon_suffix) + for hdd in properties.get('disks', []): + icon = icon_prefix + 'drive-harddisk' + icon_suffix + label += ''.format(icon, hdd['name']) + label += '
{}
{}
>' + G.add_node('comp-' + c, + label = label, + shape='box', labelloc='b') + for iface in properties.get('network_interfaces', []): + G.add_edge('comp-' + c, 'net-' + iface['network']) + return G.draw(path=destination, format=format, prog='dot') + +if __name__ == '__main__': + import sample_task as task + print(draw_setup(task.computers, task.networks)) + diff --git a/web/kpov_judge/kpov_judge.py b/web/kpov_judge/kpov_judge.py new file mode 100755 index 0000000..47e413e --- /dev/null +++ b/web/kpov_judge/kpov_judge.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python3 + +import collections +import datetime +import json +import random +import settings +import traceback +import uuid + +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']) + + +@app.before_request +def before_request(): + g.db = pymongo.MongoClient(app.config['DB_URI']).get_default_database() + + +@app.route('/') +@app.route('/classes/') +def index(): + student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody') + classes = g.db.classes.find({}, {'class_id': 1, 'name': 1}).sort('class_id') + return render_template('index.html', student_id=student_id, classes=classes) + + +@app.route('/classes//') +def class_tasks(class_id): + student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody') + 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: + task_list = [i['task_id'] for i in tasks] + else: + task_list = [] + return render_template('class_tasks.html', student_id=student_id, tasks=task_list, clas=clas) + + +@app.route('/tasks////setup.', methods=['GET']) +def setup_svg(class_id, task_id, lang, ending): + db = g.db + fmt, mimetype = { + 'svg':('svg', 'image/svg+xml'), + 'png':('png', 'image/png'), + }[ending] + networks = list(db.networks.find({'class_id': class_id, 'task_id': task_id})) + computers = list(db.computers_meta.find({'class_id': class_id, 'task_id': task_id})) + return Response(draw_setup(computers, networks, format=fmt, + icon_path=app.config['STATIC_DIR']), + mimetype=mimetype) + + +@app.route('/tasks///task.py') +def task_source(class_id, task_id): + db = g.db + try: + return db.tasks.find_one({'class_id': class_id, 'task_id': task_id})['source'] + except: + return '' + + +@app.route('/tasks///task.html') +def task_html(class_id, task_id): + return render_template('task.html', task=task_source(class_id, task_id)) + + +def get_params(class_id, task_id, student_id, db): + try: + meta = db.task_params_meta.find_one({'class_id': class_id, 'task_id': task_id})['params'] + except Exception: + return {'mama': 'ZAKVAJ?'}, {'mama': {'public': True}} + + params = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'student_id': student_id}) + if params is None or 'params' not in params: # TODO try with $exists: params or smth. + try: + gen_params_source = db.gen_params.find_one({'class_id': class_id, 'task_id': task_id})['source'] + gen_params_code = compile(gen_params_source, 'generator.py', 'exec') + d = {} + exec(gen_params_code, globals(), d) + params = d['gen_params'](student_id, meta) + db.task_params.update({'class_id': class_id, 'task_id': task_id, 'student_id': student_id}, + {'$set': {'params': params}}, upsert=True) + params = d['gen_params'](student_id, meta) # TODO this is repeated, is it necessary? + for computer in db.computers_meta.find({'class_id': class_id, 'task_id': task_id}): + try: + name = computer.pop('name') + del computer['_id'] + del computer['task_id'] + except Exception: + pass + db.student_computers.update({'class_id': class_id, 'task_id': task_id, 'student_id': student_id, 'name': name}, + {'$set': computer}, upsert=True) + except Exception as e: + meta = {'crash': {'public': True}} + params = {'crash': "Parameter creator crashed or missing:\n{}".format( + traceback.format_exc())} + else: + params = params['params'] + return params, meta + + +@app.route('/tasks///') +def task_lang_redirect(class_id, task_id): + return redirect(url_for('task_greeting', class_id=class_id, task_id=task_id, lang=app.config['DEFAULT_LANG'])) + + +@app.route('/tasks////howto/') +def task_howto(class_id, task_id, lang): + db = g.db + return db.howtos.find_one({'class_id': class_id, 'task_id': task_id, 'lang': lang}).get('text', '') + + +@app.route('/tasks////images/') +def task_image(class_id, task_id, lang, fname): + db = g.db + return db.howto_images.find_one({'class_id': class_id, 'task_id': task_id, 'fname': fname}).get('data', '') + + +@app.route('/tasks////') +def task_greeting(class_id, task_id, lang): + student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody') + db = g.db + # generate the parameters as soon as the student visits + params, meta = get_params(class_id, task_id, student_id, db) + instr_ok = True + try: + instructions = db.task_instructions.find_one({'class_id': class_id, 'task_id': task_id}) + instructions = instructions.get(lang, instructions[app.config['DEFAULT_LANG']]) + except Exception: + try: + instructions = list(instructions.values())[0] + except Exception as e: + instructions = str(e) + instr_ok = False + if instr_ok: + try: + public_params = [] + for k, v in meta.items(): + if v.get('public', False): + 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})) + + backing_files = collections.defaultdict(set) + for computer in computer_list: + if 'disk_urls' not in computer: + continue + for name, disk in computer['disk_urls'].items(): + for fmt in disk['formats']: + backing_files[fmt] |= set(disk[fmt][1:]) + + if request.args.get('narediStack', 'false') == 'true': + #db.student_tasks.update({'task_id': task_id, 'student_id': student_id}, {'$set': {'create_openstack': True}}, upsert = True) + openstackCreated = False # Spremeni na True, ko odkomentiras zgornjo vrstico. + else: + if db.student_tasks.find({'class_id': class_id, 'task_id': task_id, 'student_id': student_id, 'openstack_created': True}).count() > 0: + openstackCreated = True + elif db.student_tasks.find({'class_id': class_id, 'task_id': task_id, 'student_id': student_id, 'create_openstack': True}).count() > 0: + 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=sorted((c for c in computer_list if 'disk_urls' in c), key=lambda c: c['name']), + backing_files={fmt: sorted(images) for fmt, images in backing_files.items()}, + lang='sl' if lang == 'si' else lang, # TODO s/si/sl in all tasks (and maybe elsewhere) + openstack=openstackCreated, + instructions=jinja2.Template(instructions), + params=public_params, + result=result, + **{p['name']: p['value'] for p in public_params}) + + +@app.route('/tasks///token.json') +def get_token(class_id, task_id): + db = g.db + student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody') + token = str(uuid.uuid4()) + db.task_params.update({'class_id': class_id, 'task_id': task_id, 'student_id': student_id}, + {'$set': {'token': token}}, upsert=True) + return json.dumps({'token': token}) + + +@app.route('/tasks///params.json', methods=['POST']) +def params_json(class_id, task_id): + db = g.db + token = flask.app.request.form['token'] + record = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'token': token}) + if not record: + return json.dumps({}) + params, meta = get_params(record['class_id'], record['task_id'], record['student_id'], db) + shown_params = {} + for name, param in params.items(): + if meta.get(name, {'public': False})['public']: + shown_params[name] = param + return json.dumps(shown_params) + + +@app.route('/tasks///results.json', methods=['POST']) +def results_json(class_id, task_id): + db = g.db + token = flask.app.request.form.get('token', '') + task = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'token': token}) + if not task: + return json.dumps({'result': 0, 'hints': ['invalid token'], 'status': 'NOT OK'}) + + params = task['params'] + if params is None: + return json.dumps({'result': 0, 'hints': ['no parameters found for task'], 'status': 'NOT OK'}) # no such task + + results = json.loads(flask.app.request.form['results']) + user_params = json.loads(flask.app.request.form['params']) + + meta = db.task_params_meta.find_one({'task_id': task_id}) + if meta is None: + meta = {} + else: + meta = meta['params'] + for param_name, param_meta in meta.items(): + if param_meta.get('w', False) and param_name in user_params: + params[param_name] = user_params[param_name] + + # hack to get token into task_check function + # TODO rethink the API + params['token'] = token + try: + 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) + res, hints = d['task_check'](collections.defaultdict(str, results), params) + except Exception as e: + hints = ["Checker died: " + str(e)] + res = 0 + if (isinstance(res, int) or isinstance(res, float)) and res > 0: + res_status = 'OK' + else: + res_status = 'NOT OK' + + db.results.insert({ + 'class_id': class_id, 'task_id': task_id, + 'result': res, 'hints': hints, 'status': res_status, + 'student_id': task['student_id'], + 'response': results, + 'time': datetime.datetime.now() + }) + return json.dumps({'result': res, 'hints': hints, 'status': res_status}) + + +if __name__ == '__main__': + app.run(host='0.0.0.0') diff --git a/web/kpov_judge/kpov_util.py b/web/kpov_judge/kpov_util.py new file mode 120000 index 0000000..20fecf4 --- /dev/null +++ b/web/kpov_judge/kpov_util.py @@ -0,0 +1 @@ +../../kpov_util.py \ No newline at end of file diff --git a/web/kpov_judge/random_data b/web/kpov_judge/random_data new file mode 120000 index 0000000..6a83e61 --- /dev/null +++ b/web/kpov_judge/random_data @@ -0,0 +1 @@ +../../random_data \ No newline at end of file diff --git a/web/kpov_judge/settings.py b/web/kpov_judge/settings.py new file mode 120000 index 0000000..8889b19 --- /dev/null +++ b/web/kpov_judge/settings.py @@ -0,0 +1 @@ +../../settings.py \ No newline at end of file diff --git a/web/kpov_judge/static/icons/computer.png b/web/kpov_judge/static/icons/computer.png new file mode 100644 index 0000000..aebd82e Binary files /dev/null and b/web/kpov_judge/static/icons/computer.png differ diff --git a/web/kpov_judge/static/icons/computer.svg b/web/kpov_judge/static/icons/computer.svg new file mode 100644 index 0000000..6da674d --- /dev/null +++ b/web/kpov_judge/static/icons/computer.svg @@ -0,0 +1,1877 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/kpov_judge/static/icons/drive-harddisk.png b/web/kpov_judge/static/icons/drive-harddisk.png new file mode 100644 index 0000000..ce482f2 Binary files /dev/null and b/web/kpov_judge/static/icons/drive-harddisk.png differ diff --git a/web/kpov_judge/static/icons/drive-harddisk.svg b/web/kpov_judge/static/icons/drive-harddisk.svg new file mode 100644 index 0000000..5f36f85 --- /dev/null +++ b/web/kpov_judge/static/icons/drive-harddisk.svg @@ -0,0 +1,491 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Drive - Hard Disk + + + Jakub Steiner + + + + + hdd + hard drive + fixed + media + solid + + + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/kpov_judge/static/icons/internet.png b/web/kpov_judge/static/icons/internet.png new file mode 100644 index 0000000..2f70cbf Binary files /dev/null and b/web/kpov_judge/static/icons/internet.png differ diff --git a/web/kpov_judge/static/icons/internet.svg b/web/kpov_judge/static/icons/internet.svg new file mode 100644 index 0000000..5efbd24 --- /dev/null +++ b/web/kpov_judge/static/icons/internet.svg @@ -0,0 +1,3398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/kpov_judge/static/style.css b/web/kpov_judge/static/style.css new file mode 100644 index 0000000..363fbc4 --- /dev/null +++ b/web/kpov_judge/static/style.css @@ -0,0 +1,72 @@ +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, h2, h3 { + margin-bottom: 0.5em; +} +h2 { + font-size: 110%; +} +h3 { + font-size: 105%; +} + +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; + -webkit-transition: opacity 0.5s; + -moz-transition: opacity 0.5s; + -ms-transition: opacity 0.5s; + -o-transition: opacity 0.5s; + transition: opacity 0.5s; +} + +.hover { + position: relative; +} + +.hover:hover .tooltip { /* display tooltip on hover */ + opacity: 1; +} diff --git a/web/kpov_judge/templates/class_tasks.html b/web/kpov_judge/templates/class_tasks.html new file mode 100644 index 0000000..4b6a523 --- /dev/null +++ b/web/kpov_judge/templates/class_tasks.html @@ -0,0 +1,16 @@ + + + +

{{clas.name}}

+ +

+{{ _('Zdravo, %(student)s.', student=student_id) }} + +

+

{{ _('Naloge') }}

+ +
    +{% for t in tasks %} +
  • {{t}} +{% endfor %} +
diff --git a/web/kpov_judge/templates/index.html b/web/kpov_judge/templates/index.html new file mode 100644 index 0000000..4c2ba47 --- /dev/null +++ b/web/kpov_judge/templates/index.html @@ -0,0 +1,16 @@ + + + +

KPOV Judge

+ +

+{{ _('Zdravo, %(student)s.', student=student_id) }} + +

+

{{ _('Predmeti') }}

+ +
    +{% for c in classes %} +
  • {{c.name}} +{% endfor %} +
diff --git a/web/kpov_judge/templates/params.html b/web/kpov_judge/templates/params.html new file mode 100644 index 0000000..0648f66 --- /dev/null +++ b/web/kpov_judge/templates/params.html @@ -0,0 +1,24 @@ + + + + + +

+{{task_id}} +

+
+

Parametri

+
+{% for k, v in params.items() %} +
{{k}}:
{{params_meta[k]['descriptions']['si']}}
+{% if params_meta[k]["w"] %} + +{% else %} +{{v}} +{% endif %}
+{% endfor %} +

+
+
+ + diff --git a/web/kpov_judge/templates/results.html b/web/kpov_judge/templates/results.html new file mode 100644 index 0000000..7f05208 --- /dev/null +++ b/web/kpov_judge/templates/results.html @@ -0,0 +1,15 @@ + + +

+{{task_id}} +

+

+

Rezultati

+
    +{% for k, v in results.items() %} +
  • {{k}}: {{v}}
  • +{% endfor %} +
+

+ + diff --git a/web/kpov_judge/templates/task.html b/web/kpov_judge/templates/task.html new file mode 100644 index 0000000..ddad5d8 --- /dev/null +++ b/web/kpov_judge/templates/task.html @@ -0,0 +1,12 @@ + + + +

+{{task_id}} +

+

+

{{task}}
+

+ + diff --git a/web/kpov_judge/templates/task_greeting.html b/web/kpov_judge/templates/task_greeting.html new file mode 100644 index 0000000..b10a49d --- /dev/null +++ b/web/kpov_judge/templates/task_greeting.html @@ -0,0 +1,141 @@ + + + +KPOV Judge + + + + +

+ {{task_id}} + [ +{% for lang in ('en', 'si') %} +{{lang}} +{% endfor %} +] +

+ +
+{{ _('Shema omrežja za nalogo.') }} +

{{ _('Naloga') }}

+ +{% include instructions %} + +

+{{ _('Podrobna navodila.') }} +
+

+ +
+
+

{{ _('Računalniki') }}

+{% if computers %} +
+ {% for c in computers %} +
{{c['name']}} +
    + {% for name, disk in c['disk_urls'].items() %} +
  • {{name}} [ {% for fmt in disk['formats'] %}{{fmt}} {% endfor %}] + {% else %} + {% endfor %} +
+{% endfor %} +
+ +

+ +{{ _('Za GNS3 uporabite slike v formatu qcow2, za katere rabite še zaledne datoteke (angl. backing files). Za VirtualBox uporabite slike VMDK, ki ne podpirajo zalednih datotek in so zato precej večje. Namesto prenosa lahko slike VMDK pretvorite iz formata qcow2.') }} + +{% if backing_files %} +

+

{{ _('Zaledne datoteke') }}

+

+{{ _('Vsako od spodnjih slik prenesete samo pri prvi nalogi, v kateri se pojavi. Vse datoteke morajo biti v istem imeniku.') }} + +

+{% for fmt, images in backing_files.items() %} + {% if images %} +
{{fmt}} +
    + {% for image in images %} +
  • {{image}}
  • + {% endfor %} +
+ {% endif %} +{% endfor %} +
+
+{% endif %} + +{% else %} +

{{ _('Slike navideznih diskov so v izdelavi in bodo kmalu na voljo.') }} +{% endif %} +

+ +
+

{{ _('Status') }}

+

+{% 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 %} + +

+{{ _('Program za preverjanje.') }} + +

{{ _('Parametri') }}

+
+{% for p in params if p['value'] %} +
{{ p['name'] }}{% if p['description'] %} … {{ p['description'] }}{% endif %}
+
{{ p['value'] }}
+{% endfor %} +
+
+
+ +{# +

+{% if openstack %} +Openstack projekt za to vajo je že ustvarjen ali v izdelavi (funkcionalnost še ne deluje). +{% else %} +Ustvari Openstack projekt za to vajo (funkcionalnost še ne deluje). +{% endif %} +#} diff --git a/web/kpov_judge/translations/en/LC_MESSAGES/messages.po b/web/kpov_judge/translations/en/LC_MESSAGES/messages.po new file mode 100644 index 0000000..c8cac2d --- /dev/null +++ b/web/kpov_judge/translations/en/LC_MESSAGES/messages.po @@ -0,0 +1,144 @@ +# English translations for PROJECT. +# Copyright (C) 2018 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2018. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2019-02-20 19:31+0100\n" +"PO-Revision-Date: 2018-10-13 20:57+0200\n" +"Last-Translator: FULL NAME \n" +"Language-Team: en \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:54 +msgid "Shema omrežja za nalogo." +msgstr "Network graph for this task." + +#: templates/task_greeting.html:55 +msgid "Naloga" +msgstr "Task" + +#: templates/task_greeting.html:60 +msgid "Podrobna navodila." +msgstr "Detailed instructions." + +#: templates/task_greeting.html:66 +msgid "Računalniki" +msgstr "Computers" + +#: templates/task_greeting.html:82 +msgid "" +"Za GNS3 uporabite slike v formatu qcow2, za katere rabite še zaledne " +"datoteke (angl. backing files). Za VirtualBox " +"uporabite slike VMDK, ki ne podpirajo zalednih datotek in so zato precej " +"večje. Namesto prenosa lahko slike VMDK pretvorite iz formata qcow2." +msgstr "" +"For GNS3 use qcow2 images, which require the backing files below. " +"For VirtualBox use VMDK images, which do not support backing files and " +"are thus much larger. Alternatively you can " +"convert qcow2 images yourself." + +#: templates/task_greeting.html:86 +msgid "Zaledne datoteke" +msgstr "Backing files" + +#: templates/task_greeting.html:88 +msgid "" +"Vsako od spodnjih slik prenesete samo pri prvi nalogi, v kateri se " +"pojavi. Vse datoteke morajo biti v istem imeniku." +msgstr "" +"Download each of the images below only if you do not have it yet. " +"All files must be placed in the same directory." + +#: templates/task_greeting.html:106 +msgid "Slike navideznih diskov so v izdelavi. Stran osvežite čez nekaj minut." +msgstr "Disk images are being generated. Please refresh the page after a few minutes." + +#: templates/task_greeting.html:111 +msgid "Status" +msgstr "Status" + +#: templates/task_greeting.html:114 +msgid "Naloga nima še nobenega poskusa." +msgstr "This task has not been attempted yet." + +#: templates/task_greeting.html:116 +#, python-format +msgid "Naloga je uspešno opravljena dne %(time)s." +msgstr "This task was solved on %(time)s." + +#: templates/task_greeting.html:118 +#, 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:122 +msgid "Program za preverjanje." +msgstr "Testing program." + +#: templates/task_greeting.html:124 +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" + +#~ msgid "Slike diskov za to nalogo:" +#~ msgstr "Disk images for this task:" + +#~ msgid "Osnovne slike" +#~ msgstr "Backing images" + +#~ msgid "" +#~ msgstr "" + +#~ msgid "Rezultat" +#~ msgstr "Result" + +#~ msgid "" +#~ "Za GNS3 uporabite slike v formatu " +#~ "qcow2, za katere rabite še zaledne " +#~ "datoteke (angl. backing " +#~ "files). VirtualBox ne podpira zalednih" +#~ " datotek, zato so slike VDI precej" +#~ " večje. Namesto prenosa lahko slike " +#~ "VDI dobite " +#~ "iz formata qcow2." +#~ msgstr "" + diff --git a/web/kpov_judge/translations/sl/LC_MESSAGES/messages.po b/web/kpov_judge/translations/sl/LC_MESSAGES/messages.po new file mode 100644 index 0000000..2382073 --- /dev/null +++ b/web/kpov_judge/translations/sl/LC_MESSAGES/messages.po @@ -0,0 +1,145 @@ +# Slovenian translations for PROJECT. +# Copyright (C) 2018 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2018. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2019-02-20 19:31+0100\n" +"PO-Revision-Date: 2018-10-13 02:29+0200\n" +"Last-Translator: FULL NAME \n" +"Language-Team: sl \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:54 +msgid "Shema omrežja za nalogo." +msgstr "" + +#: templates/task_greeting.html:55 +msgid "Naloga" +msgstr "" + +#: templates/task_greeting.html:60 +msgid "Podrobna navodila." +msgstr "" + +#: templates/task_greeting.html:66 +msgid "Računalniki" +msgstr "" + +#: templates/task_greeting.html:82 +msgid "" +"Za GNS3 uporabite slike v formatu qcow2, za katere rabite še zaledne " +"datoteke (angl. backing files). Za VirtualBox " +"uporabite slike VMDK, ki ne podpirajo zalednih datotek in so zato precej " +"večje. Namesto prenosa lahko slike VMDK pretvorite iz formata qcow2." +msgstr "" + +#: templates/task_greeting.html:86 +msgid "Zaledne datoteke" +msgstr "" + +#: templates/task_greeting.html:88 +msgid "" +"Vsako od spodnjih slik prenesete samo pri prvi nalogi, v kateri se " +"pojavi. Vse datoteke morajo biti v istem imeniku." +msgstr "" + +#: templates/task_greeting.html:106 +msgid "Slike navideznih diskov so v izdelavi in bodo kmalu na voljo." +msgstr "" + +#: templates/task_greeting.html:111 +msgid "Status" +msgstr "" + +#: templates/task_greeting.html:114 +msgid "Naloga nima še nobenega poskusa." +msgstr "" + +#: templates/task_greeting.html:116 +#, python-format +msgid "Naloga je uspešno opravljena dne %(time)s." +msgstr "" + +#: templates/task_greeting.html:118 +#, python-format +msgid "Naloga še ni opravljena, najvišji rezultat je %(score)s." +msgstr "" + +#: templates/task_greeting.html:122 +msgid "Program za preverjanje." +msgstr "" + +#: templates/task_greeting.html:124 +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 "" + +#~ msgid "Slike diskov za to nalogo:" +#~ msgstr "" + +#~ msgid "Osnovne slike" +#~ msgstr "" + +#~ msgid "" +#~ msgstr "" + +#~ msgid "Rezultat" +#~ msgstr "" + +#~ msgid "" +#~ "Za GNS3 uporabite slike v formatu " +#~ "qcow2, za katere rabite še zaledne " +#~ "datoteke (angl. backing " +#~ "files). VirtualBox ne podpira zalednih" +#~ " datotek, zato so slike VDI precej" +#~ " večje. Namesto prenosa lahko slike " +#~ "VDI dobite " +#~ "iz formata qcow2." +#~ msgstr "" + -- cgit v1.2.1