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 }) form = GradedForm(request.POST or None, exercise=exercise) result = { "form": form } # Grade valid form posts. if form.is_valid(): (points, error_groups, error_fields) = form.grade() points = pointsInRange(points, exercise["max_points"]) # 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 render_configured_template(request, course, exercise, post_url, 'access/create_form_default.html', result)
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 )
def acceptGitUser(request, course, exercise, post_url): ''' Presents a template and expects a user id to create Git URL for grading. ''' auth_secret = "*AYVhD'b5,hKzf/6" _requireActions(exercise) if not "git_address" in exercise: raise ConfigError("Missing \"git_address\" in exercise configuration.") user = detect_user(request) if request.method == "POST": if user is None and "user" in request.POST and "hash" in request.POST: user = request.POST["user"] if make_hash(auth_secret, user) != request.POST["hash"]: raise PermissionDenied() source = exercise["git_address"].replace("$USER", user) sdir = create_submission_dir(course, exercise) write_submission_file(sdir, "gitsource", source) return _acceptSubmission(request, course, exercise, post_url, sdir) return render_configured_template(request, course, exercise, post_url, "access/accept_git_user.html", { "user": user, "hash": make_hash(auth_secret, user) })
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)
def acceptGitUser(request, course, exercise, post_url): ''' Presents a template and expects a user id to create Git URL for grading. ''' auth_secret = "*AYVhD'b5,hKzf/6" _requireActions(exercise) if not "git_address" in exercise: raise ConfigError("Missing \"git_address\" in exercise configuration.") user = get_uid(request) if request.method == "POST": if "user" in request.POST and "hash" in request.POST: user = request.POST["user"] if make_hash(auth_secret, user) != request.POST["hash"]: raise PermissionDenied() source = exercise["git_address"].replace("$USER", user) sdir = create_submission_dir(course, exercise) write_submission_file(sdir, "gitsource", source) return _acceptSubmission(request, course, exercise, post_url, sdir) return render_configured_template(request, course, exercise, post_url, "access/accept_git_user.html", { "user": user, "hash": make_hash(auth_secret, user) })
def md5Authentication(request, course, exercise, post_url): ''' Creates an md5 hash for user authentication. ''' user = get_uid(request) key = make_hash(exercise["auth_secret"], user) return render_configured_template(request, course, exercise, post_url, None, { "user": user, "hash": key })
def md5Authentication(request, course, exercise, post_url): ''' Creates an md5 hash for user authentication. ''' user = detect_user(request) key = make_hash(exercise["auth_secret"], user) return render_configured_template(request, course, exercise, post_url, None, { "user": user, "hash": key })
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)
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)
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 )
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, )
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 )
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)
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 )
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 )
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 )
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 )
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 )
def view(request, course, exercise, post_url): ''' Renders an iframe to hold javascript based exercise from static files. ''' url = '/static/' + course['key'] + exercise['iframe_src'] result = { 'src': update_url_params( url, { 'submission_url': request.GET.get('submission_url'), 'submit_url': request.build_absolute_uri( reverse('access.views.exercise_ajax', args=[course['key'], exercise['key']])), }), } return render_configured_template(request, course, exercise, post_url, './iframe.html', result)
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 )
def noGrading(request, course, exercise, post_url): ''' Presents a template and does no grading. ''' return render_configured_template(request, course, exercise, post_url, None, None);
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, )
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 )
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, )