# -*- coding: utf-8 -*-
#
# This file is part of INGInious. See the LICENSE and the COPYRIGHTS files for
# more information about the licensing of this file.
import json
import os
import traceback
from jinja2 import Template
from inginious_container_api.input import get_lang
import inginious_container_api.lang
_feedback_dir = '/.__output' if not inginious_container_api.DEBUG else './'
_feedback_file = os.path.join(_feedback_dir, '__feedback.json')
def _load_feedback():
""" Open existing feedback file """
result = {}
if os.path.exists(_feedback_file):
f = open(_feedback_file, 'r')
cont = f.read()
f.close()
else:
cont = '{}'
try:
result = json.loads(cont) if cont else {}
except ValueError as e:
result = {"result":"crash", "text":"Feedback file has been modified by user !"}
return result
[docs]def save_feedback(rdict):
""" Save feedback file """
# Check for output folder
if not os.path.exists(_feedback_dir):
os.makedirs(_feedback_dir)
jcont = json.dumps(rdict)
f = open(_feedback_file, 'w')
f.write(jcont)
f.close()
# Doing the real stuff
[docs]def set_global_result(result):
""" Set global result value """
rdict = _load_feedback()
rdict['result'] = result
save_feedback(rdict)
[docs]def set_problem_result(result, problem_id):
""" Set problem specific result value """
rdict = _load_feedback()
if not 'problems' in rdict:
rdict['problems'] = {}
cur_val = rdict['problems'].get(problem_id, '')
rdict['problems'][problem_id] = [result, cur_val] if type(cur_val) == str else [result, cur_val[1]]
save_feedback(rdict)
[docs]def set_grade(grade):
""" Set global grade of this job """
rdict = _load_feedback()
rdict['grade'] = float(grade)
save_feedback(rdict)
[docs]def set_global_feedback(feedback, append=False):
""" Set global feedback in case of error """
rdict = _load_feedback()
rdict['text'] = rdict.get('text', '') + feedback if append else feedback
save_feedback(rdict)
[docs]def set_problem_feedback(feedback, problem_id, append=False):
""" Set problem specific feedback """
rdict = _load_feedback()
if not 'problems' in rdict:
rdict['problems'] = {}
cur_val = rdict['problems'].get(problem_id, '')
rdict['problems'][problem_id] = (cur_val + feedback if append else feedback) if type(cur_val) == str else [cur_val[0], (cur_val[1] + feedback if append else feedback)]
save_feedback(rdict)
[docs]def set_state(state):
""" Set the task state """
rdict = _load_feedback()
rdict['state'] = state
save_feedback(rdict)
[docs]def set_tag(tag, value):
"""
Set the tag 'tag' to the value True or False.
:param value: should be a boolean
:param tag: should be the id of the tag. Can not starts with '*auto-tag-'
"""
if not tag.startswith("*auto-tag-"):
rdict = _load_feedback()
tests = rdict.setdefault("tests", {})
tests[tag] = (value == True)
save_feedback(rdict)
[docs]def tag(value):
"""
Add a tag with generated id.
:param value: everything working with the str() function
"""
rdict = _load_feedback()
tests = rdict.setdefault("tests", {})
tests["*auto-tag-" + str(hash(str(value)))] = str(value)
save_feedback(rdict)
[docs]def set_custom_value(custom_name, custom_val):
"""
Set a custom value to be given back in the feedback
:param custom_name: name/key of the entry to be placed in the custom dict
:param custom_val: content of the entry to be placed in the custom dict
"""
rdict = _load_feedback()
if not "custom" in rdict:
rdict["custom"] = {}
rdict["custom"][custom_name] = custom_val
save_feedback(rdict)
[docs]def get_feedback():
""" Returns the dictionary containing the feedback """
rdict = _load_feedback()
return rdict
[docs]def set_feedback_from_tpl(tpl_name, parameters, problem_id=None, append=False):
""" Parse a template, using the given parameters, and set it as the feedback message.
tpl_name must indicate a file. Given that XX_XX is the lang code of the current user ('en_US' or 'fr_FR', for example),
this function will search template file in different locations, in the following order:
- [current_dir]/tpl_name.XX_XX.tpl
- [task_dir]/lang/XX_XX/tpl_name.tpl (this is the preferred way, as it contributes to store all translations in the same folder)
- [current_dir]/tpl_name.tpl
Note that you can indicate "../otherdir/mytpl" to force the function to search in the "../otherdir" directory. Simply omit the final ".tpl".
If no file is found or a parsing exception occured, an error is displayed as feedback message, and False is returned.
If everything went well, True is returned.
The parsing uses Jinja2.
Parameters is a dictionnary that will be given to the Jinja template.
"""
inginious_container_api.lang.init()
lang = get_lang()
tpl_location = None
possible_locations = [".".join([tpl_name, lang, "tpl"]),
os.path.join(inginious_container_api.lang.get_lang_dir_path(), lang, tpl_name) + ".tpl",
".".join([tpl_name, "tpl"])]
for path in possible_locations:
if os.path.exists(path):
tpl_location = path
break
if tpl_location is None:
output = """
.. error::
Unable to find template named %s. Please contact your administrator.
""" % tpl_name
if problem_id is None:
set_global_feedback(output, append)
else:
set_problem_feedback(output, problem_id, append)
return False
try:
template = Template(open(tpl_location, 'r').read())
parameters.update({"_": _})
output = template.render(parameters)
valid = True
except Exception:
output = """
.. error::
An error occured while parsing the feedback template. Here is the full error:
::
"""
output += "\n".join(["\t\t"+line for line in traceback.format_exc().split("\n")])
output += "\n\tPlease contact your administrator.\n"
valid = False
if problem_id is None:
set_global_feedback(output, append)
else:
set_problem_feedback(output, problem_id, append)
return valid