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 _acceptSubmission(request, course, exercise, post_url, sdir): ''' Queues the submission for grading. ''' # Backup synchronous grading. if not settings.CELERY_BROKER: LOGGER.warning("No queue configured") from grader.runactions import runactions r = runactions(course, exercise, sdir, get_uid(request), int(request.GET.get("ordinal_number", 1))) html = template_to_str(course, exercise, "", r["template"], r["result"]) return render_template(request, course, exercise, post_url, "access/async_accepted.html", { "synchronous": True, "accepted": True, "max_points": r["result"].get("max_points", 1), "points": r["result"].get("points", 0), "feedback": html, }) if "submission_url" in request.GET: surl = request.GET["submission_url"] surl_missing = False else: LOGGER.warning("submission_url missing from a request") surl = request.build_absolute_uri(reverse('test-result')) surl_missing = True # Queue grader. tasks.grade.delay(course["key"], exercise["key"], translation.get_language(), surl, sdir, get_uid(request), int(request.GET.get("ordinal_number", 1))) _acceptSubmission.counter += 1 qlen = queue_length() LOGGER.debug("Submission of %s/%s, queue counter %d, queue length %d", course["key"], exercise["key"], _acceptSubmission.counter, qlen) if qlen >= settings.QUEUE_ALERT_LENGTH: LOGGER.error("Queue alert, length: %d", qlen) return render_template(request, course, exercise, post_url, "access/async_accepted.html", { "accepted": True, "wait": True, "missing_url": surl_missing, "queue": qlen })
def createFormModel(request, course, exercise, parameter): form = GradedForm(None, exercise=exercise, show_correct=True) form.bind_initial() points,error_groups,error_fields = form.grade() result = { "form": form, "accepted": True, "points": points, "error_groups": error_groups, "error_fields": error_fields } return render_template(request, course, exercise, None, 'access/graded_form.html', result)
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 createFormModel(request, course, exercise, parameter): form = GradedForm(None, exercise=exercise, show_correct=True) form.bind_initial() points, error_groups, error_fields = form.grade() result = { "form": form, "accepted": True, "points": points, "error_groups": error_groups, "error_fields": error_fields } return render_template(request, course, exercise, None, 'access/graded_form.html', result)
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 _acceptSubmission(request, course, exercise, post_url, sdir): ''' Queues the submission for grading. ''' uids = get_uid(request) attempt = int(request.GET.get("ordinal_number", 1)) container_flag = settings.CONTAINER_MODE and "container" in exercise # Backup synchronous grading. if not container_flag and not settings.CELERY_BROKER: LOGGER.warning("No queue configured") from grader.runactions import runactions r = runactions(course, exercise, sdir, uids, attempt) html = template_to_str(course, exercise, "", r["template"], r["result"]) return render_template( request, course, exercise, post_url, "access/async_accepted.html", { "synchronous": True, "accepted": True, "max_points": r["result"].get("max_points", 1), "points": r["result"].get("points", 0), "feedback": html, }) if "submission_url" in request.GET: surl = request.GET["submission_url"] surl_missing = False else: LOGGER.warning("submission_url missing from a request") surl = request.build_absolute_uri(reverse('test-result')) surl_missing = True _acceptSubmission.counter += 1 # Order container for grading. if container_flag: c = _requireContainer(exercise) course_extra = { "key": course["key"], "name": course["name"], } exercise_extra = { "key": exercise["key"], "title": exercise.get("title", None), } if exercise.get("personalized", False): exercise_extra["personalized_exercise"] \ = select_generated_exercise_instance(course, exercise, uids, attempt) if settings.ENABLE_PERSONAL_DIRECTORIES: exercise_extra["personal_directory"] \ = user_personal_directory_path(course, exercise, uids) sid = os.path.basename(sdir) write_submission_meta(sid, { "url": surl, "dir": sdir, }) r = invoke([ settings.CONTAINER_SCRIPT, sid, request.scheme + "://" + request.get_host(), c["image"], os.path.join(DIR, course["key"], c["mount"]), sdir, c["cmd"], json.dumps(course_extra), json.dumps(exercise_extra), ]) LOGGER.debug("Container order exit=%d out=%s err=%s", r["code"], r["out"], r["err"]) qlen = 1 # Queue in celery & rabbitmq for chroot sandbox actions. else: tasks.grade.delay(course["key"], exercise["key"], translation.get_language(), surl, sdir, uids, attempt) qlen = queue_length() LOGGER.debug("Submission of %s/%s, queue counter %d, queue length %d", course["key"], exercise["key"], _acceptSubmission.counter, qlen) if qlen >= settings.QUEUE_ALERT_LENGTH: LOGGER.error("Queue alert, length: %d", qlen) return render_template(request, course, exercise, post_url, "access/async_accepted.html", { "accepted": True, "wait": True, "missing_url": surl_missing, "queue": qlen })
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, )
def _acceptSubmission(request, course, exercise, post_url, sdir: SubmissionDir): ''' Queues the submission for grading. ''' uids = get_uid(request) attempt = int(request.GET.get("ordinal_number", 1)) if "submission_url" in request.GET: surl = request.GET["submission_url"] surl_missing = False else: LOGGER.warning("submission_url missing from a request") surl = f'http://{request.META["HOSTNAME"]}:{request.META["SERVER_PORT"]}{reverse("test-result")}' surl_missing = True # Order container for grading. c = _requireContainer(exercise) ro_mounts = c.get("mounts", {}).copy() if not isinstance(ro_mounts, dict): raise ConfigError( "'container'->'mounts' must be a <path on course>-<mount path> dictionary or omitted altogether" ) for k, v in ro_mounts.items(): if not os.path.isabs(v): raise ConfigError(f"Mount path must be absolute: {v}") if any(v == p or v.startswith(p + "/") for p in ("/exercise", "/submission", "/personalized_exercise")): raise ConfigError( "/exercise, /submission and /personalized_exercise are reserved mounts, you cannot mount to them in 'mounts'" ) if os.path.isabs(k) or os.path.normpath(k).startswith(".."): raise ConfigError( f"Mounted path on course must be a relative subpath: {k}") ro_mounts[c["mount"]] = "/exercise" ro_mounts = { os.path.join(settings.COURSES_PATH, course["key"], k): v for k, v in ro_mounts.items() } if len(set(ro_mounts.values())) != len(ro_mounts): raise ConfigError("Mount paths must be distinct") if exercise.get("personalized", False): personalized_dir = select_generated_exercise_instance( course, exercise, uids, attempt) ro_mounts[personalized_dir] = "/personalized_exercise" write_submission_meta( sdir.sid, { "url": surl, "dir": str(sdir.dir()), "course_key": course["key"], "exercise_key": exercise["key"], "lang": translation.get_language(), }) return_code, out, err = runner_func( course=course, exercise=exercise, container_config=c, submission_id=sdir.sid, host_url=request.scheme + "://" + request.get_host(), readwrite_mounts={str(sdir.dir()): "/submission"}, readonly_mounts=ro_mounts, image=c["image"], cmd=c["cmd"], settings=settings.RUNNER_MODULE_SETTINGS, ) LOGGER.debug(f"Container order exit={return_code} out={out} err={err}") qlen = 1 return render_template( request, course, exercise, post_url, "access/async_accepted.html", { "error": return_code != 0, "accepted": True, "wait": True, "missing_url": surl_missing, "queue": qlen })
def _acceptSubmission(request, course, exercise, post_url, sdir): ''' Queues the submission for grading. ''' uids = get_uid(request) attempt = int(request.GET.get("ordinal_number", 1)) if "submission_url" in request.GET: surl = request.GET["submission_url"] surl_missing = False else: LOGGER.warning("submission_url missing from a request") surl = request.build_absolute_uri(reverse('test-result')) surl_missing = True _acceptSubmission.counter += 1 # Order container for grading. c = _requireContainer(exercise) course_extra = { "key": course["key"], "name": course["name"], } exercise_extra = { "key": exercise["key"], "title": exercise.get("title", None), "resources": c.get("resources", {}), # Unofficial param, implemented differently later "require_constant_environment": c.get("require_constant_environment", False) # Unofficial param, implemented differently later } if exercise.get("personalized", False): exercise_extra["personalized_exercise"] \ = select_generated_exercise_instance(course, exercise, uids, attempt) sid = os.path.basename(sdir) write_submission_meta( sid, { "url": surl, "dir": sdir, "course_key": course["key"], "exercise_key": exercise["key"], "lang": translation.get_language(), }) r = invoke([ settings.CONTAINER_SCRIPT, sid, request.scheme + "://" + request.get_host(), c["image"], os.path.join(DIR, course["key"], c["mount"]), sdir, c["cmd"], json.dumps(course_extra), json.dumps(exercise_extra), ]) LOGGER.debug("Container order exit=%d out=%s err=%s", r["code"], r["out"], r["err"]) qlen = 1 return render_template(request, course, exercise, post_url, "access/async_accepted.html", { "accepted": True, "wait": True, "missing_url": surl_missing, "queue": qlen })
def _acceptSubmission(request, course, exercise, post_url, sdir): ''' Queues the submission for grading. ''' uids = get_uid(request) attempt = int(request.GET.get("ordinal_number", 1)) container_flag = settings.CONTAINER_MODE and "container" in exercise # Backup synchronous grading. if not container_flag and not settings.CELERY_BROKER: LOGGER.warning("No queue configured") from grader.runactions import runactions r = runactions(course, exercise, sdir, uids, attempt) html = template_to_str(course, exercise, "", r["template"], r["result"]) return render_template(request, course, exercise, post_url, "access/async_accepted.html", { "synchronous": True, "accepted": True, "max_points": r["result"].get("max_points", 1), "points": r["result"].get("points", 0), "feedback": html, }) if "submission_url" in request.GET: surl = request.GET["submission_url"] surl_missing = False else: LOGGER.warning("submission_url missing from a request") surl = request.build_absolute_uri(reverse('test-result')) surl_missing = True _acceptSubmission.counter += 1 # Order container for grading. if container_flag: c = _requireContainer(exercise) course_extra = { "key": course["key"], "name": course["name"], } exercise_extra = { "key": exercise["key"], "title": exercise.get("title", None), "resources": c.get("resources", {}), # Unofficial param, implemented differently later "require_constant_environment": c.get("require_constant_environment", False) # Unofficial param, implemented differently later } if exercise.get("personalized", False): exercise_extra["personalized_exercise"] \ = select_generated_exercise_instance(course, exercise, uids, attempt) if settings.ENABLE_PERSONAL_DIRECTORIES: exercise_extra["personal_directory"] \ = user_personal_directory_path(course, exercise, uids) sid = os.path.basename(sdir) write_submission_meta(sid, { "url": surl, "dir": sdir, "course_key": course["key"], "exercise_key": exercise["key"], "lang": translation.get_language(), }) r = invoke([ settings.CONTAINER_SCRIPT, sid, request.scheme + "://" + request.get_host(), c["image"], os.path.join(DIR, course["key"], c["mount"]), sdir, c["cmd"], json.dumps(course_extra), json.dumps(exercise_extra), ]) LOGGER.debug("Container order exit=%d out=%s err=%s", r["code"], r["out"], r["err"]) qlen = 1 # Queue in celery & rabbitmq for chroot sandbox actions. else: tasks.grade.delay(course["key"], exercise["key"], translation.get_language(), surl, sdir, uids, attempt) qlen = queue_length() LOGGER.debug("Submission of %s/%s, queue counter %d, queue length %d", course["key"], exercise["key"], _acceptSubmission.counter, qlen) if qlen >= settings.QUEUE_ALERT_LENGTH: LOGGER.error("Queue alert, length: %d", qlen) return render_template(request, course, exercise, post_url, "access/async_accepted.html", { "accepted": True, "wait": True, "missing_url": surl_missing, "queue": qlen })