def admin_init(admins=ADMINS, permitted_user=None): """ Check if the user is an admin. If the user is not an admin, and is not a permitted user, print an error message. Args: admins ([str], optional): The list of admins to check against. permitted_user (str, optional): One additional user who is permitted to access the page in question. This is intended to allow users to see their own data. Returns: Whether the user is authorised. """ try: user = uqauth.get_user() if user not in admins and user != permitted_user: print UNAUTHORISED.format(user) return False except uqauth.Redirected: return False else: return True
def submit_answer(tutorial_hash, code): """ Submit the student's answer for the given tutorial. When we store the answer, we make use of the student's own local naming scheme (eg, they could call the tutorial package 'Bob' if they wanted). This obviously won't work for submissions; we need consistency. Instead, what we do is we take a hash of the entire tutorial package and use that to uniquely identify the package. We ignore the possiblity of hash collisions (come on, what are the odds...those totally aren't famous last words). First, we check that the given hash actually exists. If it does, we can go ahead and add the submission. Implementation details are hidden in the support file (so that we can switch backends if needed). Args: tutorial_hash (str): The sha512 hash of the tutorial folder, encoded as a base32 string. code (str): The student's code. Returns: 'OK' if submitted on time. 'LATE' if submitted late. Raises: ActionError: If tutorial hash does not match a known tutorial package, or if attempting to add the submission fails. NullResponse: If the student has already submitted this tutorial. """ # authenticate the user user = uqauth.get_user() # check that the tutorial actually exists hashes = support.parse_tutorial_hashes() try: tutorial_info = next(ti for ti in hashes if ti.hash == tutorial_hash) except StopIteration: raise ActionError('Invalid tutorial: {}'.format(tutorial_hash)) # check if the student has already submitted this tutorial submissions = support.parse_submission_log(user) try: next(si for si in submissions if si.hash == tutorial_hash) raise NullResponse( 'Tutorial already submitted: {}'.format(tutorial_hash)) except StopIteration: pass # we want this -- no such tutorial has been submitted # write out the submission submission = support.add_submission(user, tutorial_hash, code) if submission is None: raise ActionError('Could not add submission: {}'.format(tutorial_hash)) # return either 'OK' or 'LATE' return 'OK' if submission.date <= tutorial_info.due else 'LATE'
def show_submit(): """ Return the submissions for the current user. Returns: A list of two-element tuples. Each tuple represents a single tutorial. The first element in the tuple is the hash of the tutorial package (in the same format as usual, ie base32 encoded sha512 hash). The second element in the tuple is one of 'MISSING', 'OK', and 'LATE'. """ # authenticate the user user = uqauth.get_user() # get our data hashes = support.parse_tutorial_hashes() submissions = {sub.hash: sub for sub in support.parse_submission_log(user)} # check if our submissions are late or not results = [] for tutorial_info in hashes: status = 'MISSING' submission = submissions.get(tutorial_info.hash) if submission is not None: status = 'OK' if submission.date <= tutorial_info.due else 'LATE' results.append((tutorial_info.hash, status)) return json.dumps(results)
def download_code(tutorial_package_name, problem_set_name, tutorial_name): """ Retrieve the given code on the server for the student's account. Args: code (str): The student's code. tutorial_package_name (str): The name of the tutorial package (eg, for UQ students, this will be something like 'CSSE1001Tutorials'). problem_set_name (str): The name of the problem set (eg, 'Introduction'). tutorial_name (str): The name of the tutorial problem (note that this will be, eg, 'Using Functions', not 'fun1.tut'). Returns: The student code, as a string. Raises: NullResponse: If there is no code to download. """ # authenticate the user user = uqauth.get_user() # read the answer code = support.read_answer(user, tutorial_package_name, problem_set_name, tutorial_name) if code is None: raise NullResponse('No code to download') return code
def upload_code(code, tutorial_package_name, problem_set_name, tutorial_name): """ Store the given code on the server for the student's account. Args: code (str): The student's code. tutorial_package_name (str): The name of the tutorial package (eg, for UQ students, this will be something like 'CSSE1001Tutorials'). problem_set_name (str): The name of the problem set (eg, 'Introduction'). tutorial_name (str): The name of the tutorial problem (note that this will be, eg, 'Using Functions', not 'fun1.tut'). Returns: 'OK' if successful. Raises: ActionError: If the code is too large. """ # authenticate the user user = uqauth.get_user() # immediately fail if the student is trying to send us too much junk # (so that we can't easily be DOSed) if len(code) > 5 * 1024: raise ActionError('Code exceeds maximum length') # write the answer support.write_answer(user, tutorial_package_name, problem_set_name, tutorial_name, code) return "OK"
def provide_feedback(subject, feedback, code=''): """ Register the given feedback for the given user. """ # authenticate the user user = uqauth.get_user() # delegate adding the feedback to the support file support.add_feedback(user, subject, feedback, code) return 'OK' # this can't fail
def wrapped(form): # Check privileges if admin and uqauth.get_user() not in ADMINS: raise ActionError("Forbidden: insufficient privileges") # Get the arguments out of the form args = {} for i, arg in enumerate(argspec.args): # If the arg doesn't have a default value and isn't given, fail if arg in form: args[arg] = form[arg].value elif i < num_required: raise ActionError("Required parameter {!r} not given.\n" "Report to maintainer.".format(arg)) return func(**args)
def answer_info(tutorial_package_name, problem_set_name, tutorial_name): """ Return information on the server copy of the student's answer for the given tutorial. Args: tutorial_package_name (str): The name of the tutorial package (eg, for UQ students, this will be something like 'CSSE1001Tutorials'). problem_set_name (str): The name of the problem set (eg, 'Introduction'). tutorial_name (str): The name of the tutorial problem (note that this will be, eg, 'Using Functions', not 'fun1.tut'). Returns: A two-element tuple. The first element of the tuple is a base32 encoding of the sha512 hash of the server copy of the student's answer. The second element is the last-modified time of the answer, as a unix timestamp. Raises: NullResponse: If no answer can be found to this problem. """ # authenticate the user user = uqauth.get_user() # grab our data answer_hash = support.get_answer_hash(user, tutorial_package_name, problem_set_name, tutorial_name) timestamp = support.get_answer_modification_time(user, tutorial_package_name, problem_set_name, tutorial_name) if answer_hash is None or timestamp is None: raise NullResponse('No code exists for this problem') # build our response response_dict = { 'hash': answer_hash, 'timestamp': timestamp, } return json.dumps(response_dict)
def main(): try: user = uqauth.get_user() except uqauth.Redirected: return # two sets of possibilities # (1) the help list (need to be a tutor) # (a) viewing the list # (b) submitting the form (eg, deleting an entry) # (2) asking for help (as a student) # (a) for the first time (no message query param) # (b) submitting the form form = cgi.FieldStorage(keep_blank_values=True) view_list = user in TUTORS and 'noadmin' not in form if view_list and user not in TUTORS: print UNAUTHORISED.format(user) return print "Content-Type: text/html\n" data = {} if view_list: template = Template(filename="./templates/help_list.html") # make our changes if the form was submitted if os.environ.get('REQUEST_METHOD') == 'POST' and 'mark_as' in form: user = form.getvalue('username') status = form.getvalue('mark_as') support.set_help_request_status(user, status) help_list = sorted(support.get_help_list(), key=lambda hi: hi.timestamp) data['date_offset'] = TZ_DELTA # hacky workaround data['help_list'] = help_list data['open_index'] = -1 else: template = Template(filename='./templates/help_request.html') data['user'] = user message = form.getvalue('message') traceback = form.getvalue('traceback') if os.environ.get('REQUEST_METHOD') == 'POST' \ and 'message' is not None: info = uqauth.get_user_info() name = info.get('name', '') support.log_help_request(user, name, message, traceback) data['alert_message'] = ('alert-success', 'Your help request has been logged') pending_request = support.get_pending_help_request(user) data['has_pending'] = pending_request is not None data['is_ignored'] = False data['queue_position'] = None if pending_request is not None: message = pending_request.message traceback = pending_request.traceback data['is_ignored'] = \ pending_request.status == support.HELP_STATUS_IGNORED data['queue_position'] = support.get_position_in_help_queue(user) data['message'] = message data['traceback'] = traceback try: print(template.render(**data)) except: print(exceptions.html_error_template().render())
def main(): try: this_user = uqauth.get_user() except uqauth.Redirected: return form = cgi.FieldStorage(keep_blank_values=True) is_admin = (this_user in ADMINS and 'noadmin' not in form) user = form.getvalue('user', this_user) if user != this_user and not this_user in ADMINS: print UNAUTHORISED.format(this_user) return message = None if (is_admin and os.environ.get('REQUEST_METHOD') == 'POST' and 'action' in form): before_submissions = get_submissions(user) if form.getvalue('action') == 'allow_late': action = (lambda tutorial: not is_submitted_on_time(tutorial, before_submissions) and support.set_allow_late(user, tutorial, this_user, True)) elif form.getvalue('action') == 'disallow_late': action = (lambda tutorial: not is_submitted_on_time(tutorial, before_submissions) and support.set_allow_late(user, tutorial, this_user, False)) else: action = None message = ('alert-danger', 'Action unknown or not specified.') problems = form.getlist('problem') if action and problems: count = sum(map(action, problems)) if count == 0: message = ('alert-warning', '0 entries modified.') elif count < len(problems): message = ('alert-success', '{} entries modified, {} unchanged.' .format(count, len(problems)-count)) else: message = ('alert-success', '{} entries modified.' .format(count)) elif action and not problems: message = ('alert-warning', 'No problems specified.') user_info = (support.get_user(user) or support.User(user, 'UNKNOWN', 'UNKNOWN', 'not_enrolled')) submissions = get_submissions(user) print "Content-Type: text/html\n" data = { 'name': 'Change Me', 'id': 'changeme', 'user': user_info, 'openIndex': -1, 'is_admin': is_admin, 'message': message, }; # Count the number of on-time or accepted-late submissions the student has made. data['mark'] = 0 data['late'] = 0 data['groups'] = [] group = None for tutorial, submission in submissions: if not group or tutorial.problem_set_name != group['slug']: group = { 'slug': tutorial.problem_set_name, 'title': tutorial.problem_set_name.replace('_', ' '), 'due': (tutorial.due + TZ_DELTA).strftime(DATE_FORMAT), 'problems': [], 'mark': 0, 'late': 0, } data['groups'].append(group) tut = process_tutorial(tutorial, submission) group['problems'].append(tut) if (submission is not None and submission.date is not None and (submission.date <= tutorial.due or submission.allow_late)): group['mark'] += 1 data['mark'] += 1 elif submission is not None and submission.date is not None: group['late'] += 1 data['late'] += 1 data['total'] = len(submissions) try: print(Template(filename="./templates/progress.html").render(**data)) except: print(exceptions.html_error_template().render())
def main(): try: user = uqauth.get_user() except uqauth.Redirected: return # two sets of possibilities # (1) the help list (need to be a tutor) # (a) viewing the list # (b) submitting the form (eg, deleting an entry) # (2) asking for help (as a student) # (a) for the first time (no message query param) # (b) submitting the form form = cgi.FieldStorage(keep_blank_values=True) view_list = user in TUTORS and 'noadmin' not in form if view_list and user not in TUTORS: print UNAUTHORISED.format(user) return print "Content-Type: text/html\n" data = {} if view_list: template = Template(filename="./templates/help_list.html") # make our changes if the form was submitted if os.environ.get('REQUEST_METHOD') == 'POST' and 'mark_as' in form: user = form.getvalue('username') status = form.getvalue('mark_as') support.set_help_request_status(user, status) help_list = sorted( support.get_help_list(), key=lambda hi: hi.timestamp ) data['date_offset'] = TZ_DELTA # hacky workaround data['help_list'] = help_list data['open_index'] = -1 else: template = Template(filename='./templates/help_request.html') data['user'] = user message = form.getvalue('message') traceback = form.getvalue('traceback') if os.environ.get('REQUEST_METHOD') == 'POST' \ and 'message' is not None: info = uqauth.get_user_info() name = info.get('name', '') support.log_help_request(user, name, message, traceback) data['alert_message'] = ( 'alert-success', 'Your help request has been logged' ) pending_request = support.get_pending_help_request(user) data['has_pending'] = pending_request is not None data['is_ignored'] = False data['queue_position'] = None if pending_request is not None: message = pending_request.message traceback = pending_request.traceback data['is_ignored'] = \ pending_request.status == support.HELP_STATUS_IGNORED data['queue_position'] = support.get_position_in_help_queue(user) data['message'] = message data['traceback'] = traceback try: print(template.render(**data)) except: print(exceptions.html_error_template().render())