Example #1
0
def acceptPost(request, course, exercise, post_url):
    '''
    Presents a template and accepts post value for grading queue.
    '''
    _requireActions(exercise)
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    fields = copy.deepcopy(exercise.get("fields", []))
    if request.method == "POST":

        # Parse submitted values.
        miss = False
        for entry in fields:
            entry["value"] = request.POST.get(entry["name"], "").strip()
            if "required" in entry and entry["required"] and not entry["value"]:
                entry["missing"] = True
                miss = True
        if miss:
            result = {"fields": fields, "rejected": True}
        else:

            # Store submitted values.
            sdir = create_submission_dir(course, exercise)
            for entry in fields:
                write_submission_file(sdir, entry["name"], entry["value"])
            return _acceptSubmission(request, course, exercise, post_url, sdir)
    else:
        result = {"fields": fields}

    return cache_headers(
        render_configured_template(request, course, exercise, post_url,
                                   'access/accept_post_default.html', result),
        request, exercise)
Example #2
0
def acceptGeneralForm(request, course, exercise, post_url):
    '''
    Presents a template and accepts form containing any input types 
    (text, file, etc) for grading queue.
    '''
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    fields = copy.deepcopy(exercise.get("fields", []))
    result = None
    miss = False
    
    if request.method == "POST":
        # Parse submitted values.
        for entry in fields:        
            entry["value"] = request.POST.get(entry["name"], "").strip()
            if "required" in entry and entry["required"] and not entry["value"]:
                entry["missing"] = True
                miss = True
        
        files_submitted = [] 
        if "files" in exercise:
            # Confirm that all required files were submitted.
            #files_submitted = [] # exercise["files"] entries for the files that were really submitted
            for entry in exercise["files"]:
                # by default, all fields are required
                required = ("required" not in entry or entry["required"])
                if entry["field"] not in request.FILES:
                    if required:
                        result = { "rejected": True, "missing_files": True }
                        break
                else:
                    files_submitted.append(entry)
                   
        if miss:
            result = { "fields": fields, "rejected": True }
        elif result is None:
            # Store submitted values.
            sdir = create_submission_dir(course, exercise)
            for entry in fields:
                write_submission_file(sdir, entry["name"], entry["value"])
                               
            if "files" in exercise:
                if "required_number_of_files" in exercise and \
                        exercise["required_number_of_files"] > len(files_submitted):
                    result = { "rejected": True, "missing_files": True }
                else:
                    # Store submitted files.
                    for entry in files_submitted:
                        save_submitted_file(sdir, entry["name"], request.FILES[entry["field"]])             
            return _acceptSubmission(request, course, exercise, post_url, sdir)
    
    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            "access/accept_general_default.html", result
        ),
        request,
        exercise
    )    
Example #3
0
def acceptAttachedExercise(request, course, exercise, post_url):
    '''
    Accepts attached exercise rules and user files for queue.
    '''
    _requireActions(exercise)
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = None

    # Receive post.
    if request.method == "POST":

        # Search for file contents.
        if "file[]" in request.FILES:
            file_list = request.FILES.getlist("file[]")
        else:
            file_list = []
            i = 0
            while "content_%d" % (i) in request.FILES:
                file_list.append(request.FILES["content_%d" % (i)])
                i += 1

        # Store submitted files.
        if not file_list:
            result = {"rejected": True, "missing_files": True}
        else:
            sdir = create_submission_dir(course, exercise)
            i = 0
            for content in file_list:
                if i > 0:
                    key = "file_%d" % (i)
                    if not key in request.POST or not request.POST[key]:
                        result = {"error": True, "missing_file_name": True}
                        clean_submission_dir(sdir)
                        break
                    save_submitted_file(sdir, request.POST[key], content)
                else:
                    save_submitted_file(sdir, "exercise_attachment", content)
                i += 1
            if result is None:
                return _acceptSubmission(request, course, exercise, post_url,
                                         sdir)

    # Add the attachment as a hint to the default view form.
    if result is None:
        import copy
        exercise = copy.deepcopy(exercise)
        exercise["files"] = [{
            "field": "content_0",
            "name": "exercise_attachment"
        }]

    return cache_headers(
        render_configured_template(request, course, exercise, post_url,
                                   "access/accept_files_default.html", result),
        request, exercise)
Example #4
0
def acceptAttachedExercise(request, course, exercise, post_url):
    '''
    Accepts attached exercise rules and user files for queue.
    '''
    _requireActions(exercise)
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = None

    # Receive post.
    if request.method == "POST":

        # Search for file contents.
        if "file[]" in request.FILES:
            file_list = request.FILES.getlist("file[]")
        else:
            file_list = []
            i = 0
            while "content_%d" % (i) in request.FILES:
                file_list.append(request.FILES["content_%d" % (i)])
                i += 1

        # Store submitted files.
        if not file_list:
            result = { "error":True, "missing_files":True }
        else:
            sdir = create_submission_dir(course, exercise)
            i = 0
            for content in file_list:
                if i > 0:
                    key = "file_%d" % (i)
                    if not key in request.POST or not request.POST[key]:
                        result = { "error": True, "missing_file_name": True }
                        clean_submission_dir(sdir)
                        break
                    save_submitted_file(sdir, request.POST[key], content)
                else:
                    save_submitted_file(sdir, "exercise_attachment", content)
                i += 1
            if result is None:
                return _acceptSubmission(request, course, exercise, post_url, sdir)

    # Add the attachment as a hint to the default view form.
    if result is None:
        import copy
        exercise = copy.deepcopy(exercise)
        exercise["files"] = [ { "field": "content_0", "name": "exercise_attachment" } ]

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            "access/accept_files_default.html", result
        ),
        request,
        exercise
    )
Example #5
0
def noGrading(request, course, exercise, post_url):
    '''
    Presents a template and does no grading.
    '''
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)
    return cache_headers(
        render_configured_template(request, course, exercise, post_url, None,
                                   None), request, exercise)
Example #6
0
def createForm(request, course, exercise, post_url):
    '''
    Creates form by configuration and grades answers.
    '''
    if "max_points" not in exercise:
        raise ConfigError("Missing required \"max_points\" in exercise configuration")
    try:
        acceptNonce(request)
    except PermissionDenied:
        return render_template(request, course, exercise, post_url,
            'access/exercise_frame.html', { "error":True, "nonce_used":True })

    last = False
    if request.method == 'POST':
        try:
            n = int(request.GET.get('ordinal_number', '0'))
            max_n = int(request.GET.get('max_submissions', '0'))
        except ValueError:
            pass
        last = max_n > 0 and n >= max_n

    form = GradedForm(request.POST or None, request.FILES or None,
        exercise=exercise, show_correct_once=last)

    # Support caching of non personalized forms.
    if not form.randomized and not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = { "form": form, "rejected": True }

    # Grade valid form posts.
    if form.is_valid():
        (points, error_groups, error_fields) = form.grade()
        points = pointsInRange(points, exercise["max_points"])

        # Allow passing to asynchronous grading.
        if "actions" in exercise or "container" in exercise:
            from .stdasync import _saveForm
            return _saveForm(request, course, exercise, post_url, form)

        # If points are not granted by form fields.
        if points == 0 and not error_fields:
            points = exercise["max_points"]

        result = { "form": form, "accepted": True, "points": points,
            "error_groups": error_groups, "error_fields": error_fields }

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            'access/create_form_default.html', result
        ),
        request,
        exercise,
        form.randomized,
    )
Example #7
0
def noGrading(request, course, exercise, post_url):
    '''
    Presents a template and does no grading.
    '''
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)
    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url, None, None
        ),
        request,
        exercise
    )
Example #8
0
def comparePostValues(request, course, exercise, post_url):
    '''
    Presents a template and grades configured POST values.
    '''
    if "max_points" not in exercise:
        raise ConfigError(
            "Missing required \"max_points\" in exercise configuration")

    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = None
    if request.method == "POST":

        if "values" in exercise:
            received = {}
            points = 0
            failed = []

            # Check each POST value against the rule.
            for (name, rule) in exercise["values"].items():
                received[name] = request.POST.get(name, False)
                if "accept" in rule:
                    if (received[name] in rule["accept"]) or \
                        (True in rule["accept"] and received[name] is not False):
                        if "points" in rule and isinstance(
                                rule["points"], int):
                            points += rule["points"]
                    else:
                        failed.append(name)

            # If points are not granted by rules.
            if points == 0 and not failed:
                points = exercise["max_points"]

            points = pointsInRange(points, exercise["max_points"])
            result = {
                "accepted": True,
                "received": received,
                "points": points,
                "failed": failed
            }
        else:
            result = {"accepted": True, "points": 0}

    return cache_headers(
        render_configured_template(request, course, exercise, post_url, None,
                                   result), request, exercise)
Example #9
0
def acceptGitAddress(request, course, exercise, post_url):
    '''
    Presents a template and accepts Git URL for grading.
    '''
    _requireActions(exercise)
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = None

    # Receive post.
    if request.method == "POST" and "git" in request.POST and request.POST["git"].strip():
        source = request.POST["git"]

        # Safe gitlab addresses.
        if "require_gitlab" in exercise:
            if not source.startswith("git@%s:" % (exercise["require_gitlab"])):
                url_start = "https://%s/" % (exercise["require_gitlab"])
                if source.startswith(url_start):
                    url_start_len = len(url_start)
                    url_parts = source[url_start_len:].split("/")
                    if len(url_parts) > 1:
                        source = "git@%s:%s/%s" % (exercise["require_gitlab"], url_parts[0], url_parts[1])
                        if not source.endswith(".git"):
                            source += ".git"
                    else:
                        result = { "error": True, "invalid_address": True }
                else:
                    result = { "error": True, "invalid_address": True }

        # Try to prevent shell injections.
        elif "\"" in source or ";" in source:
            result = { "error": True, "invalid_address": True }

        if result is None:
            sdir = create_submission_dir(course, exercise)
            write_submission_file(sdir, "gitsource", source)
            return _acceptSubmission(request, course, exercise, post_url, sdir)

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            "access/accept_git_default.html", result
        ),
        request,
        exercise
    )
Example #10
0
def acceptGitAddress(request, course, exercise, post_url):
    '''
    Presents a template and accepts Git URL for grading.
    '''
    _requireActions(exercise)
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = None

    # Receive post.
    if request.method == "POST" and "git" in request.POST and request.POST["git"].strip():
        source = request.POST["git"]

        # Safe gitlab addresses.
        if "require_gitlab" in exercise:
            if not source.startswith("git@%s:" % (exercise["require_gitlab"])):
                url_start = "https://%s/" % (exercise["require_gitlab"])
                if source.startswith(url_start):
                    url_start_len = len(url_start)
                    url_parts = source[url_start_len:].split("/")
                    if len(url_parts) > 1:
                        source = "git@%s:%s/%s" % (exercise["require_gitlab"], url_parts[-2], url_parts[-1])
                        if not source.endswith(".git"):
                            source += ".git"
                    else:
                        result = { "error": True, "invalid_address": True }
                else:
                    result = { "error": True, "invalid_address": True }

        # Try to prevent shell injections.
        elif "\"" in source or ";" in source:
            result = { "error": True, "invalid_address": True }

        if result is None:
            sdir = create_submission_dir(course, exercise)
            write_submission_file(sdir, "gitsource", source)
            return _acceptSubmission(request, course, exercise, post_url, sdir)

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            "access/accept_git_default.html", result
        ),
        request,
        exercise
    )
Example #11
0
def comparePostValues(request, course, exercise, post_url):
    '''
    Presents a template and grades configured POST values.
    '''
    if "max_points" not in exercise:
        raise ConfigError("Missing required \"max_points\" in exercise configuration")

    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = None
    if request.method == "POST":

        if "values" in exercise:
            received = {}
            points = 0
            failed = []

            # Check each POST value against the rule.
            for (name, rule) in exercise["values"].items():
                received[name] = request.POST.get(name, False)
                if "accept" in rule:
                    if (received[name] in rule["accept"]) or \
                        (True in rule["accept"] and received[name] is not False):
                        if "points" in rule and isinstance(rule["points"], int):
                            points += rule["points"]
                    else:
                        failed.append(name)

            # If points are not granted by rules.
            if points == 0 and not failed:
                points = exercise["max_points"]

            points = pointsInRange(points, exercise["max_points"])
            result = { "accepted": True, "received": received,
                "points": points, "failed": failed }
        else:
            result = { "accepted": True, "points": 0 }

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url, None, result
        ),
        request,
        exercise
    )
Example #12
0
def acceptFiles(request, course, exercise, post_url):
    '''
    Presents a template and accepts files for grading queue.
    '''
    _requireActions(exercise)
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = None

    # Receive post.
    if request.method == "POST" and "files" in exercise:

        # Confirm that all required files were submitted.
        files_submitted = [] # exercise["files"] entries for the files that were really submitted
        for entry in exercise["files"]:
            # by default, all fields are required
            required = ("required" not in entry or entry["required"])
            if entry["field"] not in request.FILES:
                if required:
                    result = { "error": True, "missing_files": True }
                    break
            else:
                files_submitted.append(entry)

        if result is None:
            if "required_number_of_files" in exercise and \
                    exercise["required_number_of_files"] > len(files_submitted):
                result = { "error": True, "missing_files": True }
            else:
                # Store submitted files.
                sdir = create_submission_dir(course, exercise)
                for entry in files_submitted:
                    save_submitted_file(sdir, entry["name"], request.FILES[entry["field"]])
                return _acceptSubmission(request, course, exercise, post_url, sdir)

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            "access/accept_files_default.html", result
        ),
        request,
        exercise
    )
Example #13
0
def acceptFiles(request, course, exercise, post_url):
    '''
    Presents a template and accepts files for grading queue.
    '''
    _requireActions(exercise)
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = None

    # Receive post.
    if request.method == "POST" and "files" in exercise:

        # Confirm that all required files were submitted.
        files_submitted = [] # exercise["files"] entries for the files that were really submitted
        for entry in exercise["files"]:
            # by default, all fields are required
            required = ("required" not in entry or entry["required"])
            if entry["field"] not in request.FILES:
                if required:
                    result = { "rejected": True, "missing_files": True }
                    break
            else:
                files_submitted.append(entry)

        if result is None:
            if "required_number_of_files" in exercise and \
                    exercise["required_number_of_files"] > len(files_submitted):
                result = { "rejected": True, "missing_files": True }
            else:
                # Store submitted files.
                sdir = create_submission_dir(course, exercise)
                for entry in files_submitted:
                    save_submitted_file(sdir, entry["name"], request.FILES[entry["field"]])
                return _acceptSubmission(request, course, exercise, post_url, sdir)

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            "access/accept_files_default.html", result
        ),
        request,
        exercise
    )
Example #14
0
def acceptPost(request, course, exercise, post_url):
    '''
    Presents a template and accepts post value for grading queue.
    '''
    _requireActions(exercise)
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    fields = copy.deepcopy(exercise.get("fields", []))
    if request.method == "POST":

        # Parse submitted values.
        miss = False
        for entry in fields:
            entry["value"] = request.POST.get(entry["name"], "").strip()
            if "required" in entry and entry["required"] and not entry["value"]:
                entry["missing"] = True
                miss = True
        if miss:
            result = { "fields": fields, "error": True }
        else:

            # Store submitted values.
            sdir = create_submission_dir(course, exercise)
            for entry in fields:
                write_submission_file(sdir, entry["name"], entry["value"])
            return _acceptSubmission(request, course, exercise, post_url, sdir)
    else:
        result = { "fields": fields }

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            'access/accept_post_default.html', result
        ),
        request,
        exercise
    )
Example #15
0
def createForm(request, course, exercise, post_url):
    '''
    Creates form by configuration and grades answers.
    '''
    if "max_points" not in exercise:
        raise ConfigError(
            "Missing required \"max_points\" in exercise configuration")
    try:
        acceptNonce(request)
    except PermissionDenied:
        return render_template(request, course, exercise, post_url,
                               'access/exercise_frame.html', {
                                   "error": True,
                                   "nonce_used": True
                               })

    last = False
    if request.method == 'POST':
        try:
            n = int(request.GET.get('ordinal_number', '0'))
            max_n = int(request.GET.get('max_submissions', '0'))
        except ValueError:
            pass
        last = max_n > 0 and n >= max_n

    form = GradedForm(request.POST or None,
                      request.FILES or None,
                      exercise=exercise,
                      show_correct_once=last)

    # Support caching of non personalized forms.
    if not form.randomized and not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = {"form": form, "rejected": True}

    # Grade valid form posts.
    if form.is_valid():
        (points, error_groups, error_fields) = form.grade()
        points = pointsInRange(points, exercise["max_points"])

        # Allow passing to asynchronous grading.
        if "actions" in exercise or "container" in exercise:
            from .stdasync import _saveForm
            return _saveForm(request, course, exercise, post_url, form)

        # If points are not granted by form fields.
        if points == 0 and not error_fields:
            points = exercise["max_points"]

        result = {
            "form": form,
            "accepted": True,
            "points": points,
            "error_groups": error_groups,
            "error_fields": error_fields
        }

    return cache_headers(
        render_configured_template(request, course, exercise, post_url,
                                   'access/create_form_default.html', result),
        request,
        exercise,
        form.randomized,
    )
Example #16
0
def acceptGeneralForm(request, course, exercise, post_url):
    '''
    Presents a template and accepts form containing any input types 
    (text, file, etc) for grading queue.
    '''

    _requireActions(exercise)
    if not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    fields = copy.deepcopy(exercise.get("fields", []))
    result = None
    miss = False
    
    if request.method == "POST":
        # Parse submitted values.
        for entry in fields:        
            entry["value"] = request.POST.get(entry["name"], "").strip()
            if "required" in entry and entry["required"] and not entry["value"]:
                entry["missing"] = True
                miss = True
        
        files_submitted = [] 
        if "files" in exercise:
            # Confirm that all required files were submitted.
            #files_submitted = [] # exercise["files"] entries for the files that were really submitted
            for entry in exercise["files"]:
                # by default, all fields are required
                required = ("required" not in entry or entry["required"])
                if entry["field"] not in request.FILES:
                    if required:
                        result = { "rejected": True, "missing_files": True }
                        break
                else:
                    files_submitted.append(entry)
                   
        if miss:
            result = { "fields": fields, "rejected": True }
        elif result is None:
            # Store submitted values.
            sdir = create_submission_dir(course, exercise)
            for entry in fields:
                write_submission_file(sdir, entry["name"], entry["value"])
                               
            if "files" in exercise:
                if "required_number_of_files" in exercise and \
                        exercise["required_number_of_files"] > len(files_submitted):
                    result = { "rejected": True, "missing_files": True }
                else:
                    # Store submitted files.
                    for entry in files_submitted:
                        save_submitted_file(sdir, entry["name"], request.FILES[entry["field"]])             
            return _acceptSubmission(request, course, exercise, post_url, sdir)
    
    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            "access/accept_general_default.html", result
        ),
        request,
        exercise
    )    
Example #17
0
def createForm(request, course, exercise, post_url):
    '''
    Creates form by configuration and grades answers.
    '''
    if "max_points" not in exercise:
        raise ConfigError("Missing required \"max_points\" in exercise configuration")

    last = False
    if request.method == 'POST':
        try:
            n = int(request.GET.get('ordinal_number', '0'))
            max_n = int(request.GET.get('max_submissions', '0'))
        except ValueError:
            pass
        last = max_n > 0 and n >= max_n

    try:
        form = GradedForm(request.POST or None, request.FILES or None,
            exercise=exercise, reveal_correct=last, request=request)
    except PermissionDenied:
        # Randomized forms raise PermissionDenied when the POST data contains
        # forged checksums or samples. It could be cleaner to check those
        # in the form validation, but the old code raises an exception like this.
        return render_template(request, course, exercise, post_url,
            'access/exercise_frame.html', { "rejected": True, "invalid_checksum": True })

    # Support caching of non personalized forms.
    if not form.randomized and not_modified_since(request, exercise):
        return not_modified_response(request, exercise)

    result = { "form": form, "rejected": True }

    # Grade valid form posts.
    if form.is_valid():
        (points, error_groups, error_fields) = form.grade()
        points = pointsInRange(points, exercise["max_points"])

        # Allow passing to asynchronous grading.
        if "container" in exercise:
            from .stdasync import _saveForm
            return _saveForm(request, course, exercise, post_url, form)

        # If points are not granted by form fields.
        if points == 0 and not error_fields:
            points = exercise["max_points"]

        result = { "form": form, "accepted": True, "points": points,
            "error_groups": error_groups, "error_fields": error_fields }
    else:
        # Don't reveal the correct answers if the form was rejected, and a
        # submission was not consumed.
        form.reveal_correct = False

    return cache_headers(
        render_configured_template(
            request, course, exercise, post_url,
            'access/create_form_default.html', result
        ),
        request,
        exercise,
        form.randomized,
    )