Пример #1
0
def exercise_template(request, course_key, exercise_key, parameter=None):
    '''
    Presents the exercise template.
    '''
    (course, exercise) = config.exercise_entry(course_key, exercise_key)
    if course is None or exercise is None:
        raise Http404()
    response = None

    path = None
    if 'template_files' in exercise:

        def find_name(paths, name):
            templates = [(path, path.split('/')[-1]) for path in paths]
            for path, name in templates:
                if name == parameter:
                    return path
            return None

        path = find_name(exercise['template_files'], parameter)
    if path:
        with open(os.path.join(course['dir'], path)) as f:
            content = f.read()
        response = HttpResponse(content, content_type='text/plain')
    else:
        try:
            response = import_named(course, exercise['view_type'] +
                                    "Template")(request, course, exercise,
                                                parameter)
        except ImportError:
            pass
    if response:
        return response
    else:
        raise Http404()
Пример #2
0
def exercise_ajax(request, course_key, exercise_key):
    '''
    Receives an AJAX request for an exercise.
    '''
    lang = request.GET.get('lang', None)

    try:
        (course, exercise,
         lang) = _get_course_exercise_lang(course_key, exercise_key, lang)

        if course is None or exercise is None or 'ajax_type' not in exercise:
            raise Http404()

        # jQuery does not send "requested with" on cross domain requests
        #if not request.is_ajax():
        #    return HttpResponse('Method not allowed', status=405)

        response = import_named(course, exercise['ajax_type'])(request, course,
                                                               exercise)
    except (ConfigError, ImportError) as e:
        return _error_response(exc=e)

    # No need to control domain as valid submission_url is required to submit.
    response['Access-Control-Allow-Origin'] = '*'
    return response
Пример #3
0
def exercise_template(request, course_key, exercise_key, parameter=None):
    '''
    Presents the exercise template.
    '''
    lang = request.GET.get('lang', None)
    (course, exercise, lang) = _get_course_exercise_lang(course_key, exercise_key, lang)

    response = None
    path = None

    if 'template_files' in exercise:
        def find_name(paths, name):
            templates = [(path,path.split('/')[-1]) for path in paths]
            for path,name in templates:
                if name == parameter:
                    return path
            return None
        path = find_name(exercise['template_files'], parameter)

    if path:
        with open(os.path.join(course['dir'], path)) as f:
            content = f.read()
        response = HttpResponse(content, content_type='text/plain')
    else:
        try:
            response = import_named(course, exercise['view_type'] + "Template")(request, course, exercise, parameter)
        except ImportError:
            pass

    if response:
        return response
    else:
        raise Http404()
Пример #4
0
def exercise_model(request, course_key, exercise_key, parameter=None):
    '''
    Presents a model answer for an exercise.
    '''
    (course, exercise) = config.exercise_entry(course_key, exercise_key)
    if course is None or exercise is None:
        raise Http404()
    response = None

    try:
        response = import_named(course, exercise['view_type'] + "Model")(
            request, course, exercise, parameter)
    except ImportError:
        pass

    if 'model_files' in exercise:
        def find_name(paths, name):
            models = [(path,path.split('/')[-1]) for path in paths]
            for path,name in models:
                if name == parameter:
                    return path
            return None
        path = find_name(exercise['model_files'], parameter)
        if path:
            with open(os.path.join(course['dir'], path)) as f:
                content = f.read()
            response = HttpResponse(content, content_type='text/plain')

    if response:
        return response
    else:
        raise Http404()
Пример #5
0
def exercise(request, course_key, exercise_key):
    '''
    Presents the exercise and accepts answers to it.
    '''
    post_url = request.GET.get('post_url', None)
    lang = request.GET.get('lang', None)

    # Fetch the corresponding exercise entry from the config.
    (course, exercise) = config.exercise_entry(course_key,
                                               exercise_key,
                                               lang=lang)
    if course is None or exercise is None:
        raise Http404()

    # Exercise language.
    if not lang:
        if "lang" in course:
            lang = course["lang"]
        else:
            lang = "en"
    translation.activate(lang)

    # Try to call the configured view.
    return import_named(course, exercise['view_type'])(request, course,
                                                       exercise, post_url)
Пример #6
0
def exercise(request, course_key, exercise_key):
    '''
    Presents the exercise and accepts answers to it.
    '''
    post_url = request.GET.get('post_url', None)
    lang = request.POST.get('__grader_lang', None) or request.GET.get('lang', None)
    (course, exercise, lang) = _get_course_exercise_lang(course_key, exercise_key, lang)

    # Try to call the configured view.
    return import_named(course, exercise['view_type'])(request, course, exercise, post_url)
Пример #7
0
def exercise(request, course_key, exercise_key):
    '''
    Presents the exercise and accepts answers to it.
    '''
    post_url = request.GET.get('post_url', None)
    lang = request.GET.get('lang', None)
    (course, exercise,
     lang) = _get_course_exercise_lang(course_key, exercise_key, lang)

    # Try to call the configured view.
    return import_named(course, exercise['view_type'])(request, course,
                                                       exercise, post_url)
Пример #8
0
def exercise_ajax(request, course_key, exercise_key):
    '''
    Receives an AJAX request for an exercise.
    '''
    (course, exercise) = config.exercise_entry(course_key, exercise_key)
    if course is None or exercise is None or 'ajax_type' not in exercise:
        raise Http404()
    # jQuery does not send "requested with" on cross domain requests
    #if not request.is_ajax():
    #    return HttpResponse('Method not allowed', status=405)

    response = import_named(course, exercise['ajax_type'])(request, course,
                                                           exercise)

    # No need to control domain as valid submission_url is required to submit.
    response['Access-Control-Allow-Origin'] = '*'
    return response
Пример #9
0
def exercise_ajax(request, course_key, exercise_key):
    '''
    Receives an AJAX request for an exercise.
    '''
    (course, exercise) = config.exercise_entry(course_key, exercise_key)
    if course is None or exercise is None or 'ajax_type' not in exercise:
        raise Http404()
    # jQuery does not send "requested with" on cross domain requests
    #if not request.is_ajax():
    #    return HttpResponse('Method not allowed', status=405)

    response = import_named(course, exercise['ajax_type'])(
        request, course, exercise)

    # No need to control domain as valid submission_url is required to submit.
    response['Access-Control-Allow-Origin'] = '*'
    return response
Пример #10
0
def exercise_model(request, course_key, exercise_key, parameter=None):
    '''
    Presents a model answer for an exercise.
    '''
    lang = request.GET.get('lang', None)
    try:
        (course, exercise,
         lang) = _get_course_exercise_lang(course_key, exercise_key, lang)
    except ConfigError as e:
        return HttpResponse(str(e), content_type='text/plain')

    response = None
    path = None

    if 'model_files' in exercise and parameter:
        path = _find_file(exercise['model_files'], parameter)

    if path:
        try:
            with open(os.path.join(course['dir'], path)) as f:
                content = f.read()
        except FileNotFoundError as error:
            raise Http404(f'Model file "{parameter}" missing') from error
        except OSError as error:
            LOGGER.error(f'Error in reading the exercise model file "{path}".',
                         exc_info=error)
            content = str(error)
        response = HttpResponse(content, content_type='text/plain')
    else:
        try:
            response = import_named(course,
                                    exercise['view_type'] + "Model")(request,
                                                                     course,
                                                                     exercise,
                                                                     parameter)
        except ImportError:
            pass
        except ConfigError as e:
            response = HttpResponse(str(e), content_type='text/plain')

    if response:
        return response
    else:
        raise Http404()
Пример #11
0
def exercise_model(request, course_key, exercise_key, parameter=None):
    '''
    Presents a model answer for an exercise.
    '''
    lang = request.GET.get('lang', None)
    (course, exercise,
     lang) = _get_course_exercise_lang(course_key, exercise_key, lang)

    response = None
    path = None

    if 'model_files' in exercise:

        def find_name(paths, name):
            models = [(path, path.split('/')[-1]) for path in paths]
            for path, name in models:
                if name == parameter:
                    return path
            return None

        path = find_name(exercise['model_files'], parameter)

    if path:
        try:
            with open(os.path.join(course['dir'], path)) as f:
                content = f.read()
        except FileNotFoundError:
            pass
        else:
            response = HttpResponse(content, content_type='text/plain')
    else:
        try:
            response = import_named(course,
                                    exercise['view_type'] + "Model")(request,
                                                                     course,
                                                                     exercise,
                                                                     parameter)
        except ImportError:
            pass

    if response:
        return response
    else:
        raise Http404()
Пример #12
0
def exercise(request, course_key, exercise_key):
    '''
    Presents the exercise and accepts answers to it.
    '''
    post_url = request.GET.get('post_url', None)
    lang = request.POST.get('__grader_lang', None) or request.GET.get('lang', None)
    (course, exercise, lang) = _get_course_exercise_lang(course_key, exercise_key, lang)

    # Try to call the configured view.
    try:
        return import_named(course, exercise['view_type'])(request, course, exercise, post_url)
    except ConfigError as error:
        return render(request, 'access/exercise_config_error.html', {
            'course': course,
            'exercise': exercise,
            'config_error': str(error),
            'result': {
                'error': True,
            },
        })
Пример #13
0
def exercise(request, course_key, exercise_key):
    '''
    Presents the exercise and accepts answers to it.
    '''
    try:
        if request.method == "GET":
            access_read_check_if_number(request, course_key)
        else:
            access_write_check_if_number(request, course_key)
    except PermissionDenied as e:
        SecurityLog.reject(request, f"EXERCISE-{request.method}",
                           f"course_id={course_key}: {e}")
        raise

    SecurityLog.accept(request, f"EXERCISE-{request.method}",
                       f"course_id={course_key}")

    post_url = request.GET.get('post_url', None)
    lang = request.POST.get('__grader_lang', None) or request.GET.get(
        'lang', None)
    course = exercise = None

    try:
        (course, exercise,
         lang) = _get_course_exercise_lang(course_key, exercise_key, lang)
        # Try to call the configured view.
        return import_named(course, exercise['view_type'])(request, course,
                                                           exercise, post_url)
    except (ConfigError, ImportError) as error:
        return render(
            request, 'access/exercise_config_error.html', {
                'course': course,
                'exercise': exercise,
                'config_error': str(error),
                'result': {
                    'error': True,
                },
            })
Пример #14
0
def exercise(request, course_key, exercise_key):
    '''
    Presents the exercise and accepts answers to it.
    '''
    post_url = request.GET.get('post_url', None)
    lang = request.GET.get('lang', None)

    # Fetch the corresponding exercise entry from the config.
    (course, exercise) = config.exercise_entry(course_key, exercise_key, lang=lang)
    if course is None or exercise is None:
        raise Http404()

    # Exercise language.
    if not lang:
        if "lang" in course:
            lang = course["lang"]
        else:
            lang = "en"
    translation.activate(lang)

    # Try to call the configured view.
    return import_named(course, exercise['view_type'])(
        request, course, exercise, post_url)
Пример #15
0
    def _course_root(self, course_key):
        '''
        Gets course dictionary root (meta and data).

        @type course_key: C{str}
        @param course_key: a course key
        @rtype: C{dict}
        @return: course root or None
        '''

        # Try cached version.
        if course_key in self._courses:
            course_root = self._courses[course_key]
            try:
                if course_root["mtime"] >= os.path.getmtime(course_root["file"]):
                    return course_root
            except OSError:
                pass

        LOGGER.debug('Loading course "%s"' % (course_key))
        meta = read_meta(os.path.join(DIR, course_key, META))
        try:
            f = self._get_config(os.path.join(self._conf_dir(DIR, course_key, meta), INDEX))
        except ConfigError:
            return None

        t = os.path.getmtime(f)
        data = self._parse(f)
        if data is None:
            raise ConfigError('Failed to parse configuration file "%s"' % (f))

        self._check_fields(f, data, ["name"])
        data["key"] = course_key
        data["mtime"] = t
        data["dir"] = self._conf_dir(DIR, course_key, {})

        if "language" in data:
            data["lang"] = data["language"]

        if "modules" in data:
            keys = []
            config = {}
            def recurse_exercises(parent):
                if "children" in parent:
                    for exercise_vars in parent["children"]:
                        if "key" in exercise_vars:
                            cfg = None
                            if "config" in exercise_vars:
                                cfg = exercise_vars["config"]
                            elif "type" in exercise_vars and "exercise_types" in data \
                                    and exercise_vars["type"] in data["exercise_types"] \
                                    and "config" in data["exercise_types"][exercise_vars["type"]]:
                                cfg = data["exercise_types"][exercise_vars["type"]]["config"]
                            if cfg:
                                keys.append(exercise_vars["key"])
                                config[exercise_vars["key"]] = cfg
                        recurse_exercises(exercise_vars)
            for module in data["modules"]:
                recurse_exercises(module)
            data["exercises"] = keys
            data["config_files"] = config

        # Enable course configurable ecercise_loader function.
        exercise_loader = self._default_exercise_loader
        if "exercise_loader" in data:
            exercise_loader = import_named(data, data["exercise_loader"])

        self._courses[course_key] = course_root = {
            "meta": meta,
            "file": f,
            "mtime": t,
            "ptime": time.time(),
            "data": data,
            "lang": data.get('lang', DEFAULT_LANG),
            "exercise_loader": exercise_loader,
            "exercises": {}
        }
        return course_root
Пример #16
0
def runactions(course, exercise, submission_dir):
    '''
    Runs configured grading actions for an exercise submission.

    @type course: C{dict}
    @param course: a course configuration
    @type exercise: C{dict}
    @param exercise: an exercise configuration
    @type submission_dir: C{str}
    @param submission_dir: a submission directory where submitted files are stored
    @rtype: C{dict}
    @return: template = template name, result = points, max_points, tests
    '''
    total_points = 0
    max_points = 0
    total_result = []
    error = False
    has_appendixes = False

    # Try to run the grading actions.
    try:
        for action in exercise["actions"]:
            exgrader = None
            try:
                exgrader = import_named(course, action["type"])
            except ImproperlyConfigured as e:
                raise ConfigError("Invalid action \"type\" in exercise configuration.", e)

            # Run the exercise grader action
            LOGGER.debug("Running action \"%s\"", action["type"])
            r = exgrader(course, exercise, action, submission_dir)
            has_appendixes = has_appendixes or \
                ("appendix" in r and r["appendix"])

            # Configured template values.
            if "title" in action:
                r["title"] = action["title"]
            if "html" in action and action["html"]:
                r["html"] = True

            # Override with configured points.
            if "points" in action:
                r["max_points"] = action["points"]
                if r["stop"]:
                    r["points"] = 0
                else:
                    r["points"] = action["points"]
            elif "max_points" in action:
                r["max_points"] = action["max_points"]
                if r["points"] > action["max_points"]:
                    r["points"] = action["max_points"]

            # Sum total numbers.
            total_result.append(r)
            total_points += r["points"]
            if "max_points" in r:
                max_points += r["max_points"]
            if r["stop"]:
                if "expect_success" in action:
                    error = action["expect_success"]
                break

        # Override with configured max points.
        if "max_points" in exercise:
            max_points = exercise["max_points"]

        # Check the points are in range.
        if total_points > max_points:
            total_points = max_points
        elif total_points < 0:
            total_points = 0

        # Determine template.
        template = None
        if "feedback_template" in exercise:
            template = exercise["feedback_template"]
        else:
            template = "access/task_success.html"

        return {
            "template": template,
            "result": {
                "points": total_points,
                "max_points": max_points,
                "tests": total_result,
                "error": error,
                "has_appendixes": has_appendixes,
            }
        }

    finally:
        clean_submission_dir(submission_dir)
Пример #17
0
def runactions(course,
               exercise,
               submission_dir,
               user_ids="",
               submission_number=1):
    '''
    Runs configured grading actions for an exercise submission.

    @type course: C{dict}
    @param course: a course configuration
    @type exercise: C{dict}
    @param exercise: an exercise configuration
    @type submission_dir: C{str}
    @param submission_dir: a submission directory where submitted files are stored
    @type user_ids: C{str}
    @param user_ids: user id(s) of the submitter(s) for personalized exercises
    @type submission_number: C{int}
    @param submission_number: ordinal number of the submission (parameter in the grader protocol)
    @rtype: C{dict}
    @return: template = template name, result = points, max_points, tests
    '''
    total_points = 0
    max_points = 0
    total_result = []
    error = False
    has_appendixes = False

    # Try to run the grading actions.
    try:
        for action in exercise["actions"]:
            exgrader = None
            try:
                exgrader = import_named(course, action["type"])
            except ImproperlyConfigured as e:
                raise ConfigError(
                    "Invalid action \"type\" in exercise configuration.", e)

            # Run the exercise grader action
            LOGGER.debug("Running action \"%s\"", action["type"])
            if action["type"] == "grader.actions.prepare" or \
                    action["type"] == "grader.actions.store_user_files":
                r = exgrader(course, exercise, action, submission_dir,
                             user_ids, submission_number)
            else:
                r = exgrader(course, exercise, action, submission_dir)
            has_appendixes = has_appendixes or \
                ("appendix" in r and r["appendix"])

            # Configured template values.
            if "title" in action:
                r["title"] = action["title"]
            if "html" in action and action["html"]:
                r["html"] = True

            # Override with configured points.
            if "points" in action:
                r["max_points"] = action["points"]
                if r["stop"]:
                    r["points"] = 0
                else:
                    r["points"] = action["points"]
            elif "max_points" in action:
                r["max_points"] = action["max_points"]
                if r["points"] > action["max_points"]:
                    r["points"] = action["max_points"]

            # Sum total numbers.
            total_result.append(r)
            total_points += r["points"]
            if "max_points" in r:
                max_points += r["max_points"]
            if r["stop"]:
                if "expect_success" in action:
                    error = action["expect_success"]
                if not ("continue_after_error" in action
                        and action["continue_after_error"]):
                    break  # skip the subsequent actions

        # Override with configured max points.
        if "max_points" in exercise:
            max_points = exercise["max_points"]

        # Check the points are in range.
        if total_points > max_points:
            total_points = max_points
        elif total_points < 0:
            total_points = 0

        # Determine template.
        template = None
        if "feedback_template" in exercise:
            template = exercise["feedback_template"]
        else:
            template = "access/task_success.html"

        return {
            "template": template,
            "result": {
                "points": total_points,
                "max_points": max_points,
                "tests": total_result,
                "error": error,
                "has_appendixes": has_appendixes,
            }
        }

    finally:
        clean_submission_dir(submission_dir)
Пример #18
0
    def _course_root(self, course_key):
        '''
        Gets course dictionary root (meta and data).

        @type course_key: C{str}
        @param course_key: a course key
        @rtype: C{dict}
        @return: course root or None
        '''

        # Try cached version.
        if course_key in self._courses:
            course_root = self._courses[course_key]
            try:
                if course_root["mtime"] >= os.path.getmtime(course_root["file"]):
                    return course_root
            except OSError:
                pass

        LOGGER.debug('Loading course "%s"' % (course_key))
        meta = read_meta(os.path.join(DIR, course_key, META))
        try:
            f = self._get_config(os.path.join(self._conf_dir(DIR, course_key, meta), INDEX))
        except ConfigError:
            return None

        t = os.path.getmtime(f)
        data = self._parse(f)
        if data is None:
            raise ConfigError('Failed to parse configuration file "%s"' % (f))

        self._check_fields(f, data, ["name"])
        data["key"] = course_key
        data["mtime"] = t
        data["dir"] = self._conf_dir(DIR, course_key, {})

        if "static_url" not in data:
            data["static_url"] = "{}{}{}/".format(
                settings.STATIC_URL_HOST_INJECT,
                settings.STATIC_URL,
                course_key
            )

        if "modules" in data:
            keys = []
            config = {}
            def recurse_exercises(parent):
                if "children" in parent:
                    for exercise_vars in parent["children"]:
                        if "key" in exercise_vars:
                            exercise_key = str(exercise_vars["key"])
                            cfg = None
                            if "config" in exercise_vars:
                                cfg = exercise_vars["config"]
                            elif "type" in exercise_vars and "exercise_types" in data \
                                    and exercise_vars["type"] in data["exercise_types"] \
                                    and "config" in data["exercise_types"][exercise_vars["type"]]:
                                cfg = data["exercise_types"][exercise_vars["type"]]["config"]
                            if cfg:
                                keys.append(exercise_key)
                                config[exercise_key] = cfg
                        recurse_exercises(exercise_vars)
            for module in data["modules"]:
                recurse_exercises(module)
            data["exercises"] = keys
            data["config_files"] = config

        # Enable course configurable ecercise_loader function.
        exercise_loader = self._default_exercise_loader
        if "exercise_loader" in data:
            exercise_loader = import_named(data, data["exercise_loader"])

        self._courses[course_key] = course_root = {
            "meta": meta,
            "file": f,
            "mtime": t,
            "ptime": time.time(),
            "data": data,
            "lang": self._default_lang(data),
            "exercise_loader": exercise_loader,
            "exercises": {}
        }
        symbolic_link(DIR, data)
        return course_root