Exemplo n.º 1
0
def course(course):
    if course.is_link():
        naucse.utils.views.forks_raise_if_disabled()

        try:
            data_from_fork = course.render_course(request_url=request.path)
            edit_info = links.process_edit_info(
                data_from_fork.get("edit_info"))
            content = data_from_fork.get("content")
            if content is None:
                raise InvalidInfo("Content of the page can't be None.")
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            # there's no way to replace this page, render an error page instead
            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            logger.exception(e)
            return render_template(
                "error_in_fork.html",
                malfunctioning_course=course,
                edit_info=get_edit_info(course.edit_path),
                faulty_page="course",
                root_slug=model.meta.slug,
                travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
            )
        record_content_urls(data_from_fork, f"/{course.slug}/")
        kwargs = {
            "course_content": content,
            "edit_info": edit_info,
        }
    else:
        content = course_content(course)
        allowed_elements_parser.reset_and_feed(content)

        kwargs = {
            "course_content": content,
            "edit_info": get_edit_info(course.edit_path),
        }

    recent_runs = get_recent_runs(course)

    try:
        return render_template("course.html",
                               course=course,
                               title=course.title,
                               recent_runs=recent_runs,
                               **kwargs)
    except TemplateNotFound:
        abort(404)
Exemplo n.º 2
0
def lesson(lesson, page, solution=None):
    """Render the html of the given lesson page."""

    lesson_url, subpage_url, static_url = relative_url_functions(
        request.path, None, lesson)

    page = lesson.pages[page]

    content = page_content(lesson,
                           page,
                           solution=solution,
                           lesson_url=lesson_url,
                           subpage_url=subpage_url,
                           static_url=static_url)

    content = content["content"]
    allowed_elements_parser.reset_and_feed(content)

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

    return render_template("lesson.html",
                           content=content,
                           page=page,
                           lesson=lesson,
                           edit_info=get_edit_info(page.edit_path),
                           title=page.title,
                           **kwargs)
Exemplo n.º 3
0
def course_calendar_ics(course):
    if not course.start_date:
        abort(404)

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

        try:
            data_from_fork = course.render_calendar_ics(
                request_url=request.path)
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            logger.exception(e)
            return render_template(
                "error_in_fork.html",
                malfunctioning_course=course,
                edit_info=get_edit_info(course.edit_path),
                faulty_page="calendar",
                root_slug=model.meta.slug,
                travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
            )

        calendar = data_from_fork["calendar"]
    else:
        try:
            calendar = generate_calendar_ics(course)
        except ValueError:
            abort(404)

    return Response(str(calendar), mimetype="text/calendar")
Exemplo n.º 4
0
def course_calendar(course):
    if course.is_link():
        naucse.utils.views.forks_raise_if_disabled()

        try:
            data_from_fork = course.render_calendar(request_url=request.path)
            record_content_urls(data_from_fork, f"/{course.slug}/")

            course = process_course_data(data_from_fork.get("course"),
                                         slug=course.slug)
            edit_info = links.process_edit_info(
                data_from_fork.get("edit_info"))
            content = data_from_fork.get("content")

            if content is None:
                raise InvalidInfo("Content of the page can't be None.")
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            logger.exception(e)
            return render_template(
                "error_in_fork.html",
                malfunctioning_course=course,
                edit_info=get_edit_info(course.edit_path),
                faulty_page="calendar",
                root_slug=model.meta.slug,
                travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
            )

        kwargs = {"course": course, "edit_info": edit_info, "content": content}
    else:
        if not course.start_date:
            abort(404)

        content = course_calendar_content(course)
        allowed_elements_parser.reset_and_feed(content)

        kwargs = {
            "course": course,
            "edit_info": get_edit_info(course.edit_path),
            "content": content
        }

    return render_template('course_calendar.html', **kwargs)
Exemplo n.º 5
0
def course_calendar_ics(course):
    if not course.start_date:
        abort(404)

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

        try:
            data_from_fork = course.render_calendar_ics(
                request_url=request.path)
            record_content_urls(data_from_fork, f"/{course.slug}/")

            calendar = data_from_fork.get("calendar")

            if calendar is None:
                raise InvalidInfo("The calendar can't be None.")
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            logger.exception(e)
            error_html = render_template(
                "error_in_fork.html",
                malfunctioning_course=course,
                edit_info=get_edit_info(course.edit_path),
                faulty_page="calendar",
                root_slug=model.meta.slug,
                travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
            )
            # XXX: Hack for static site generation -- the .ics extension
            # means the error page is served as text/calendar. There's
            # no good way to make an actual calendar show an error.
            # https://github.com/pyvec/naucse.python.cz/issues/424
            return Response(error_html, mimetype="text/calendar")
    else:
        try:
            calendar = generate_calendar_ics(course)
        except ValueError:
            abort(404)

    return Response(str(calendar), mimetype="text/calendar")
Exemplo n.º 6
0
def courses():
    # since even the basic info about the forked courses can be broken,
    # we need to make sure the required info is provided.
    # If ``RAISE_FORK_ERRORS`` is set, exceptions are raised here,
    # otherwise the course is ignored completely.
    safe_courses = []

    for course in model.courses.values():
        if not course.is_link():
            if not course.is_meta:
                safe_courses.append(course)
        elif naucse.utils.views.forks_enabled() and does_course_return_info(
                course):
            safe_courses.append(course)

    return render_template("course_list.html",
                           courses=safe_courses,
                           title="Seznam online kurzů Pythonu",
                           edit_info=get_edit_info(model.courses_edit_path))
Exemplo n.º 7
0
def session_coverpage(course, session, coverpage):
    """Render the session coverpage.

    Args:
        course      course where the session belongs
        session     name of the session
        coverpage   coverpage of the session, front is default

    Returns:
        rendered session coverpage
    """
    if course.is_link():
        naucse.utils.views.forks_raise_if_disabled()

        try:
            data_from_fork = course.render_session_coverpage(
                session, coverpage, request_url=request.path)
            record_content_urls(data_from_fork, f"/{course.slug}/")

            content = data_from_fork.get("content")
            if content is None:
                raise InvalidInfo("Content of the page can't be None.")

            kwargs = {
                "course":
                process_course_data(data_from_fork.get("course"),
                                    slug=course.slug),
                "session":
                process_session_data(data_from_fork.get("session"),
                                     slug=session),
                "edit_info":
                links.process_edit_info(data_from_fork.get("edit_info")),
                "content":
                content
            }
        except POSSIBLE_FORK_EXCEPTIONS as e:
            if raise_errors_from_forks():
                raise

            # there's no way to replace this page, render an error page instead
            logger.error("There was an error rendering url %s for course '%s'",
                         request.path, course.slug)
            logger.exception(e)
            return render_template(
                "error_in_fork.html",
                malfunctioning_course=course,
                edit_info=get_edit_info(course.edit_path),
                faulty_page=f"session_{coverpage}",
                session=session,
                root_slug=model.meta.slug,
                travis_build_id=os.environ.get("TRAVIS_BUILD_ID"),
            )
    else:
        session = course.sessions.get(session)

        content = session_coverpage_content(course, session, coverpage)
        allowed_elements_parser.reset_and_feed(content)

        kwargs = {
            "course": course,
            "session": session,
            "edit_info":
            get_edit_info(session.get_edit_path(course, coverpage)),
            "content": content
        }

    return render_template("coverpage.html", **kwargs)
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
def runs(year=None, all=None):
    today = datetime.date.today()

    # List of years to show in the pagination
    # If the current year is not there (no runs that start in the current year
    # yet), add it manually
    all_years = model.safe_run_years.keys()
    if today.year not in all_years:
        all_years.append(today.year)
    first_year, last_year = min(all_years), max(all_years)

    if year is not None:
        if year > last_year:
            # Instead of showing a future year, redirect to the 'Current' page
            return redirect(url_for('runs'))
        if year not in all_years:
            # Otherwise, if there are no runs in requested year, return 404.
            abort(404)

    if all is not None:
        run_data = model.safe_run_years

        paginate_prev = {'year': first_year}
        paginate_next = {'all': 'all'}
    elif year is None:
        # Show runs that are either ongoing or ended in the last 3 months
        runs = (model.runs_from_year(today.year) +
                model.runs_from_year(today.year - 1) +
                model.runs_from_year(today.year - 2))
        ongoing = [run for run in runs if run.end_date >= today]
        cutoff = today - datetime.timedelta(days=3 * 31)
        recent = [run for run in runs if today > run.end_date > cutoff]
        run_data = {"ongoing": ongoing, "recent": recent}

        paginate_prev = {'year': None}
        paginate_next = {'year': last_year}
    else:
        run_data = {
            year: [
                run for run in model.runs_from_year(year) +
                model.runs_from_year(year - 1) if run.end_date.year >= year
            ]
        }

        past_years = [y for y in all_years if y < year]
        if past_years:
            paginate_next = {'year': max(past_years)}
        else:
            paginate_next = {'all': 'all'}

        future_years = [y for y in all_years if y > year]
        if future_years:
            paginate_prev = {'year': min(future_years)}
        else:
            paginate_prev = {'year': None}

    return render_template("run_list.html",
                           run_data=run_data,
                           title="Seznam offline kurzů Pythonu",
                           today=datetime.date.today(),
                           year=year,
                           all=all,
                           all_years=all_years,
                           paginate_next=paginate_next,
                           paginate_prev=paginate_prev,
                           edit_info=get_edit_info(model.runs_edit_path))
Exemplo n.º 10
0
def index():
    return render_template("index.html", edit_info=get_edit_info(Path(".")))
Exemplo n.º 11
0
def render(page_type: str, slug: str, *args, **kwargs) -> Dict[str, Any]:
    """Return a rendered page for a course, based on page_type and slug.
    """
    course = get_course_from_slug(slug)

    if course.is_link():
        raise ValueError("Circular dependency.")

    path = []
    if kwargs.get("request_url"):
        path = [kwargs["request_url"]]

    logger = UrlForLogger(views.app)
    with views.app.test_request_context(*path):
        with logger:

            info = {
                "course": {
                    "title": course.title,
                    "url": views.course_url(course),
                    "vars": course.vars,
                    "canonical": course.canonical,
                    "is_derived": course.is_derived,
                },
            }

            if page_type == "course":
                info["content"] = views.course_content(course)
                info["edit_info"] = get_edit_info(course.edit_path)

            elif page_type == "calendar":
                info["content"] = views.course_calendar_content(course)
                info["edit_info"] = get_edit_info(course.edit_path)

            elif page_type == "calendar_ics":
                info["calendar"] = str(views.generate_calendar_ics(course))
                info["edit_info"] = get_edit_info(course.edit_path)

            elif page_type == "course_page":
                lesson_slug, page, solution, *_ = args
                lesson = views.model.get_lesson(lesson_slug)

                content_offer_key = kwargs.get("content_key")

                not_processed = object()
                content = not_processed

                if content_offer_key is not None:
                    # the base repository has a cached version of the content
                    content_key = page_content_cache_key(
                        Repo("."), lesson_slug, page, solution, course.vars)

                    # if the key matches what would be produced here, let's not return anything
                    # and the cached version will be used
                    if content_offer_key == content_key:
                        content = None

                request_url = kwargs.get("request_url")
                if request_url is None:
                    request_url = url_for('course_page',
                                          course=course,
                                          lesson=lesson,
                                          page=page,
                                          solution=solution)

                lesson_url, subpage_url, static_url = views.relative_url_functions(
                    request_url, course, lesson)
                page, session, prv, nxt = views.get_page(course, lesson, page)

                # if content isn't cached or the version was refused, let's render
                # the content here (but just the content and not the whole page with headers, menus etc)
                if content is not_processed:
                    content = views.page_content(
                        lesson,
                        page,
                        solution,
                        course,
                        lesson_url=lesson_url,
                        subpage_url=subpage_url,
                        static_url=static_url,
                        without_cache=True,
                    )

                if content is None:
                    info["content"] = None
                    info["content_urls"] = []
                else:
                    info["content"] = content["content"]
                    info["content_urls"] = content["urls"]

                info.update({
                    "page": {
                        "title": page.title,
                        "css": page.info.get(
                            "css"
                        ),  # not page.css since we want the css without limitation
                        "latex": page.latex,
                        "attributions": page.attributions,
                        "license": serialize_license(page.license),
                        "license_code": serialize_license(page.license_code)
                    },
                    "edit_info": get_edit_info(page.edit_path)
                })

                if session is not None:
                    info["session"] = {
                        "title":
                        session.title,
                        "url":
                        url_for("session_coverpage",
                                course=course.slug,
                                session=session.slug),
                        "slug":
                        session.slug,
                    }

                prev_link, session_link, next_link = views.get_footer_links(
                    course, session, prv, nxt, lesson_url)
                info["footer"] = {
                    "prev_link": prev_link,
                    "session_link": session_link,
                    "next_link": next_link
                }

            elif page_type == "session_coverpage":
                session_slug, coverpage, *_ = args

                session = course.sessions.get(session_slug)

                info.update({
                    "session": {
                        "title":
                        session.title,
                        "url":
                        url_for("session_coverpage",
                                course=course.slug,
                                session=session.slug),
                    },
                    "content":
                    views.session_coverpage_content(course, session,
                                                    coverpage),
                    "edit_info":
                    get_edit_info(session.get_edit_path(course, coverpage)),
                })
            else:
                raise ValueError("Invalid page type.")

        # generate list of absolute urls which need to be frozen further
        urls = set()
        for endpoint, values in logger.iter_calls():
            url = url_for(endpoint, **values)
            if url.startswith(
                    f"/{slug}"
            ):  # this is checked once again in main repo, but let's save cache space
                urls.add(url)

        info["urls"] = list(urls)

    return info