Beispiel #1
0
def test_cache_offer(model):
    """Test that forks don't render content when content exists in cache.
    """
    repo = arca.get_repo(model.courses["test-course"].repo,
                         model.courses["test-course"].branch)

    content_key = page_content_cache_key(repo, "beginners/cmdline", "index",
                                         None,
                                         model.courses["test-course"].vars)

    result = model.courses["test-course"].render_page(
        "beginners/cmdline",
        "index",
        None,
        content_key=content_key,
        request_url="/course/test-course/beginners/cmdline/")

    assert result["content"] is None

    # Also test that if provided a key which is gonna be rejected,
    # content is rendered

    result = model.courses["test-course"].render_page(
        "beginners/cmdline",
        "index",
        None,
        content_key=content_key + "asdfasdf",
        request_url="/course/test-course/beginners/cmdline/")

    assert result["content"] is not None
def course_page(course, lesson, page, solution=None):
    lesson_slug = lesson
    page_slug = page

    try:
        lesson = model.get_lesson(lesson_slug)
        canonical_url = url_for('lesson', lesson=lesson, _external=True)
    except LookupError:
        lesson = canonical_url = None

    kwargs = {}
    prev_link = session_link = next_link = session = None

    if course.is_link():
        naucse.utils.views.forks_raise_if_disabled()

        fork_kwargs = {"request_url": request.path}

        try:
            # Checks if the rendered page content is in cache locally
            # to offer it to the fork.
            # ``course.vars`` calls ``course_info`` so it has to be in
            # the try block.
            # The function can also raise FileNotFoundError if the
            # lesson doesn't exist in repo.
            content_key = page_content_cache_key(
                arca.get_repo(course.repo, course.branch), lesson_slug, page,
                solution, course.vars)
            content_offer = arca.region.get(content_key)

            # We've got the fragment in cache, let's offer it to the fork.
            if content_offer:
                fork_kwargs["content_key"] = content_key

            data_from_fork = course.render_page(lesson_slug, page, solution,
                                                **fork_kwargs)
            record_content_urls(data_from_fork, f"/{course.slug}/")

            content = data_from_fork["content"]

            if content is None:
                # the offer was accepted
                content = content_offer["content"]
                for x in content_offer["urls"]:
                    record_url(urljoin(request.path, x))
            else:
                # the offer was rejected or the the fragment was not in cache
                arca.region.set(content_key, {
                    "content": content,
                    "urls": data_from_fork["content_urls"]
                })
                for x in data_from_fork["content_urls"]:
                    record_url(urljoin(request.path, x))

            # compatibility
            page = process_page_data(data_from_fork.get("page"))
            course = process_course_data(data_from_fork.get("course"),
                                         slug=course.slug)
            session = process_session_data(data_from_fork.get("session"))
            kwargs["edit_info"] = links.process_edit_info(
                data_from_fork.get("edit_info"))
            prev_link, session_link, next_link = process_footer_data(
                data_from_fork.get("footer"))

            title = '{}: {}'.format(course["title"], page["title"])
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            rendered_replacement = False

            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            if lesson is not None:
                try:
                    logger.error(
                        "Rendering the canonical version with a warning.")

                    lesson_url, subpage_url, static_url = relative_url_functions(
                        request.path, course, lesson)
                    page = lesson.pages[page]
                    content = page_content(lesson,
                                           page,
                                           solution,
                                           course,
                                           lesson_url=lesson_url,
                                           subpage_url=subpage_url,
                                           static_url=static_url)["content"]
                    title = '{}: {}'.format(course.title, page.title)

                    try:
                        footer_links = course.get_footer_links(
                            lesson.slug,
                            page_slug,
                            request_url=request.path,
                        )
                        for link in footer_links:
                            _prefix = f"/{course.slug}/"
                            if link and link["url"].startswith(_prefix):
                                record_url(link["url"])
                        prev_link, session_link, next_link = footer_links

                    except POSSIBLE_FORK_EXCEPTIONS as e:
                        if raise_errors_from_forks():
                            raise

                        # The fork is failing spectacularly, so the footer
                        # links aren't that important
                        logger.error(
                            "Could not retrieve even footer links from the fork at page %s",
                            request.path)
                        logger.exception(e)

                    rendered_replacement = True
                    kwargs["edit_info"] = get_edit_info(page.edit_path)
                    kwargs["error_in_fork"] = True
                    kwargs["travis_build_id"] = os.environ.get(
                        "TRAVIS_BUILD_ID")

                except Exception as canonical_error:
                    logger.error("Rendering the canonical version failed.")
                    logger.exception(canonical_error)

            if not rendered_replacement:
                logger.exception(e)
                return render_template(
                    "error_in_fork.html",
                    malfunctioning_course=course,
                    edit_info=get_edit_info(course.edit_path),
                    faulty_page="lesson",
                    lesson=lesson_slug,
                    pg=page_slug,  # avoid name conflict
                    solution=solution,
                    root_slug=model.meta.slug,
                    travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
                )
    else:
        if lesson is None:
            abort(404)

        lesson_url, subpage_url, static_url = relative_url_functions(
            request.path, course, lesson)
        page, session, prv, nxt = get_page(course, lesson, page)
        prev_link, session_link, next_link = get_footer_links(
            course, session, prv, nxt, lesson_url)

        content = page_content(lesson,
                               page,
                               solution,
                               course=course,
                               lesson_url=lesson_url,
                               subpage_url=subpage_url,
                               static_url=static_url)
        content = content["content"]
        allowed_elements_parser.reset_and_feed(content)
        title = '{}: {}'.format(course.title, page.title)

        kwargs["edit_info"] = get_edit_info(page.edit_path)

    if solution is not None:
        kwargs["solution_number"] = int(solution)

    return render_template("lesson.html",
                           canonical_url=canonical_url,
                           title=title,
                           content=content,
                           prev_link=prev_link,
                           session_link=session_link,
                           next_link=next_link,
                           root_slug=model.meta.slug,
                           course=course,
                           lesson=lesson,
                           page=page,
                           solution=solution,
                           session=session,
                           **kwargs)