Beispiel #1
0
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
Beispiel #2
0
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'
Beispiel #3
0
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
Beispiel #4
0
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)
Beispiel #5
0
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
Beispiel #6
0
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"
Beispiel #7
0
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
Beispiel #8
0
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
Beispiel #9
0
        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)
Beispiel #10
0
        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)
Beispiel #11
0
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)
Beispiel #12
0
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())
Beispiel #13
0
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())
Beispiel #14
0
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())