#!/usr/bin/env python3 import collections import datetime import json import random import settings 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 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): print "Ta naloga ne obstaja!" return {'odgovor': 'neznana naloga'} def task_check(results, params): raise Exception("neznana naloga?") return "Manjka preverjalnik naloge (neznana naloga?)" def gen_params(user_id, meta): return dict() params_meta = {'neznano_ime_naloge': {'opis':'NEZNANA NALOGA', 'w': False}} """ @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) def results_post(class_id, task_id, results): student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody') db = g.db params = db.task_params.find_one({'class_id': class_id, 'task_id': task_id, 'student_id': student_id})['params'] if params is None: # params = {} #else: # params = params['params'] return {'result': 0, 'hints': ['task not found'], status: 'NOT OK'} # no such task 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] 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': student_id, 'response': results, 'time': datetime.datetime.now()}) return {'result': res, 'hints': hints, 'status': res_status} def results_dict(class_id, task_id): student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody') db = g.db try: entry = db.results.find_one( {'$query': {'class_id': class_id, 'task_id': task_id, 'student_id': student_id}, # vsi uporabniki brez nastavljenega REMOTE_USER (i.e. Apache basic auth) imajo skupne rezultate, napaka? '$orderby': {'time': -1}}, {'result': 1, 'status': 1, 'hints': 1, '_id': 0}) if entry is None: return {'result': 'Naloga ni bila nikdar ocenjena', 'status': 'NOT OK'} return entry except Exception as e: return {'Error': str(e)} @app.route('/tasks///results.json', methods=['GET', 'POST']) def results_json(class_id, task_id): if flask.app.request.method == 'POST': return json.dumps(results_post(class_id, task_id, json.loads(flask.app.request.form['results']))) return json.dumps(results_dict(class_id, task_id)) @app.route('/tasks////results.html') def results_html(class_id, task_id, lang): return render_template('results.html', results=results_dict(class_id, task_id)) @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) def public_meta(db, class_id, task_id): shown = {} try: meta = db.task_params_meta.find_one({'class_id': class_id, 'task_id': task_id})['params'] for k, v in meta.items(): try: if v['public']: shown[k] = v except Exception: pass except Exception: shown = dummy_meta return shown @app.route('/tasks///task.py') def task_source(class_id, task_id): db = g.db try: task_str = db.tasks.find_one({'class_id': class_id, 'task_id': task_id})['source'] except Exception: return dummy_task return task_str @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: 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[k] = params.get(k, "???") 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_images = set() for computer in computer_list: for disk_url in computer.get('disk_urls', []): backing_images |= set(disk_url['backing']) 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 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='sl' if lang == 'si' else lang, # TODO s/si/sl in all tasks (and maybe elsewhere) openstack=openstackCreated, instructions=jinja2.Template(instructions), **public_params, ) @app.route('/tasks///params.json', methods=['GET', 'POST']) def params_json(class_id, task_id): student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody') db = g.db params, meta = get_params(class_id, task_id, student_id, db) shown_params = {} if flask.app.request.method == 'POST': try: new_params = json.loads(flask.app.request.form['params']) except Exception: new_params = {} for name in params.items(): if meta.get(name, {'w': False}).get('w', False) and k in new_params: params[name] = new_params[name] if meta.get(name, {'public': False})['public']: shown_params[name] = params[name] db.task_params.update({'class_id': class_id, 'task_id': task_id, 'student_id': student_id}, {'$set': {'params': params}}) else: 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////params.html', methods=['GET', 'POST']) def params_html(class_id, task_id, lang): student_id = flask.app.request.environ.get('REMOTE_USER', 'Nobody') db = g.db params, meta = get_params(class_id, task_id, student_id, db) shown_params = {} for name, meta_param in meta.items(): if meta_param.get('public', False): shown_params[name] = params.get(name, '') if flask.app.request.method == 'POST': form = flask.app.request.form new_params = {} for k, v in meta.items(): if v.get('w', False): params[k] = flask.app.request.form[k] if v.get('public', False): shown_params[name] = params.get(name, '') db.task_params.update({'class_id': class_id, 'task_id': task_id, 'student_id': student_id}, {'$set': {'params': params}}) return render_template('params.html', params=shown_params, params_meta=meta) @app.route('/tasks///params_meta.json') def params_meta(class_id, task_id): db = g.db return json.dumps(public_meta(db, class_id, task_id)) @app.route('/static/') def send_foo(filename): return send_from_directory(app.config.STATIC_FILES, filename) if __name__ == '__main__': app.run(host='0.0.0.0')