Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions lessons/beginners/install/info.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ style: md
attribution: Pro PyLadies Brno napsal Petr Viktorin, 2014-2017.
license: cc-by-sa-40
subpages:
index:
next: null
linux:
title: Instalace Pythonu - Linux
prev: index
next: first-steps
prev: Instalace:beginners/install
next: První příkazy v Pythonu:beginners/install:first-steps
windows:
title: Instalace Pythonu - Windows
prev: index
next: first-steps
prev: Instalace:beginners/install
next: První příkazy v Pythonu:beginners/install:first-steps
macos:
title: Instalace Pythonu - MacOS
prev: index
next: first-steps
prev: Instalace:beginners/install
next: První příkazy v Pythonu:beginners/install:first-steps
first-steps:
title: První krůčky s Pythonem
prev: index
prev: Instalace:beginners/install
next: Nastavení editoru:beginners/install-editor
26 changes: 26 additions & 0 deletions naucse/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ def jinja(self):
def css(self):
return self.info.get('css')

@reify
def subpages(self):
if "subpages" in self.info.keys():
return self.info['subpages']
else:
return None


def _prevnext(self, name, default):
if name in self.info:
page_slug = self.info[name]
Expand Down Expand Up @@ -260,3 +268,21 @@ def get_lesson(self, name, base_collection=None):
base_collection, name = name.split('/', 2)
collection = self.collections[base_collection]
return collection.lessons[name]


class Navigation(Model):
"""Represents navigation link.

Arguments:
title the title of the link location
url <lesson_type>/<lesson>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not really a URL.

page the subpage of the lesson, index is default

"""
def __init__(self, title, url, page='index'):
self.title = title
self.url = url
self.page = page

def __str__(self):
return "[{}]({}/{})".format(self.title, self.url, self.page)
134 changes: 104 additions & 30 deletions naucse/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from jinja2 import PrefixLoader, FileSystemLoader, StrictUndefined, Markup
from jinja2.exceptions import TemplateNotFound
from werkzeug.local import LocalProxy
from collections import namedtuple

from naucse import models
from naucse.urlconverters import register_url_converters
Expand All @@ -13,8 +14,11 @@

app = Flask('naucse')
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.config['TRAP_HTTP_EXCEPTIONS'] = True


lesson_template_loader = FileSystemLoader(os.path.join(app.root_path, '..', 'lessons'))
session_template_loader = FileSystemLoader(os.path.join(app.root_path, '..', 'runs'))


@LocalProxy
Expand All @@ -33,21 +37,18 @@ def set_vars():

@app.route('/')
def index():
"""Index page."""
return render_template("index.html",
page_wip=True)


@app.route('/about/')
def about():
"""About page."""
return render_template("about.html",
page_wip=True)


@app.route('/runs/')
def runs():
"""Runs page."""
return render_template("run_list.html",
run_years=model.run_years,
title="Seznam offline kurzů Pythonu",
Expand All @@ -56,23 +57,29 @@ def runs():

@app.route('/courses/')
def courses():
"""Page with listed online courses."""
return render_template("course_list.html", courses=model.courses,
title="Seznam online kurzů Pythonu",
page_wip=True)


@app.route('/lessons/<lesson:lesson>/static/<path:path>')
def lesson_static(lesson, path):
"""Static files in lessons."""
"""Get the endpoint for static files in lessons.

Args:
lesson location of the file <lesson_type>/<lesson_name>
path path to file in the static folder

Returns:
endpoint for the static file
"""
directory = str(lesson.path)
filename = os.path.join('static', path)
return send_from_directory(directory, filename)


@app.route('/courses/<course:course>/')
def course_page(course):
"""Course page."""
try:
return render_template('course.html',
course=course, plan=course.sessions,
Expand All @@ -83,11 +90,9 @@ def course_page(course):

@app.route('/<run:run>/')
def run(run):
"""Run's page."""
g.vars = dict(run.vars)

def lesson_url(lesson, *args, **kwargs):
"""Link to the specific lesson."""
return url_for('run_page', run=run, lesson=lesson, *args, **kwargs)

try:
Expand All @@ -99,23 +104,35 @@ def lesson_url(lesson, *args, **kwargs):


def prv_nxt_teller(run, lesson):
"""Determine the previous and the next lesson."""
lessons = [
material.lesson
for session in run.sessions.values()
for material in session.materials
if material.lesson
]
"""Determine the previous and the next lesson and the parent session.

Args:
run run of the current lesson
lesson current lesson

Returns:
3-tuple: previous lesson, next lesson and the parent session
"""
lessons = [material.lesson for session in run.sessions.values() for material in session.materials if material.lesson]

session_link = None
for session in run.sessions.values():
for material in session.materials:
if str(material.lesson) == str(lesson):
session_link = models.Navigation(session.title, session.slug)

prv, nxt = None, None

for prev, current, next in zip([None] + lessons,
lessons,
lessons[1:] + [None]):
if current.slug == lesson.slug:
if prev:
prev = prev.index_page
prv = models.Navigation(prev.title, prev.slug)
if next:
next = next.index_page
return prev, next
return None, None
nxt = models.Navigation(next.title, next.slug)

return prv, nxt, session_link


def lesson_template_or_404(lesson, page):
Expand Down Expand Up @@ -161,45 +178,61 @@ def subpage_url(page_slug):
template_name = 'solution.html'
kwargs.setdefault('solution_number', solution)

# XXX: Link to fragment
kwargs['prv'] = page
kwargs['nxt'] = None
else:
template_name = 'lesson.html'

kwargs['prv'] = page.previous_page(kwargs.get('prv'))
kwargs['nxt'] = page.next_page(kwargs.get('nxt'))

kwargs.setdefault('title', page.title)
kwargs.setdefault('content', content)

if (page.subpages != None and page.slug in page.subpages.keys()
and "prev" in page.subpages[page.slug].keys() and "next" in page.subpages[page.slug].keys()) :
prev_segments = page.subpages[page.slug]["prev"].split(':')
next_segments = page.subpages[page.slug]["next"].split(':')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading correctly, this way the title of the link can't have a colon in it. Is that a reasonable limitation?

The configuration already comes from YAML. Why invent a special format for this field?


if len(next_segments) == 2:
next_segments.append("index")

if len(prev_segments) == 2:
prev_segments.append("index")

kwargs['prv'] = models.Navigation(prev_segments[0], prev_segments[1], prev_segments[2])
kwargs['nxt'] = models.Navigation(next_segments[0], next_segments[1], next_segments[2])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the code to create a Navigation is duplicated twice here. Is there a better place for initialization code to go?


return render_template(template_name, **kwargs)


@app.route('/<run:run>/<lesson:lesson>/', defaults={'page': 'index'})
@app.route('/<run:run>/<lesson:lesson>/<page>/')
@app.route('/<run:run>/<lesson:lesson>/<page>/solutions/<int:solution>/')
def run_page(run, lesson, page, solution=None):
"""Run's lesson page."""
"""Render the lesson page of the run.

Args:
run where the lesson belongs
lesson name of the lesson <lesson_type>/<lesson_name>
page page of the lesson, index is default

Returns:
rendered lesson page
"""
page = lesson.pages[page]
g.vars = dict(run.vars)
g.vars.update(page.vars)

def lesson_url(lesson, *args, **kwargs):
"""Link to the specific lesson."""
return url_for('run_page', run=run, lesson=lesson, *args, **kwargs)

def subpage_url(page_slug):
return url_for('run_page', run=run, lesson=lesson, page=page_slug)

prv, nxt = prv_nxt_teller(run, lesson)
prv, nxt, session_link = prv_nxt_teller(run, lesson)
title = title='{}: {}'.format(run.title, page.title)

return render_page(page=page, title=title,
lesson_url=lesson_url,
subpage_url=subpage_url,
run=run, nxt=nxt, prv=prv,
run=run, prv=prv, nxt=nxt,
session_link=session_link,
page_wip=not page.license,
solution=solution)

Expand All @@ -208,7 +241,48 @@ def subpage_url(page_slug):
@app.route('/lessons/<lesson:lesson>/<page>/')
@app.route('/lessons/<lesson:lesson>/<page>/solutions/<int:solution>/')
def lesson(lesson, page, solution=None):
"""Lesson page."""
"""Render the lesson page.

Args:
lesson name of the lesson <lesson_type>/<lesson_name>
page page of the lesson, index is default

Returns:
rendered lesson page
"""
page = lesson.pages[page]
g.vars = dict(page.vars)
return render_page(page=page, page_wip=True, solution=solution)


def session_template_or_404(run, session, page):
env = app.jinja_env.overlay(loader=session_template_loader)
name = '{}/sessions/{}/{}.md'.format(run.slug, session, page)
try:
return env.get_template(name)
except TemplateNotFound:
abort(404)


@app.route('/runs/<run:run>/sessions/<session>/', defaults={'page': 'front'})
@app.route('/runs/<run:run>/sessions/<session>/<page>/')
def session_page(run, session, page):
"""Render the session page.

Args:
run run where the session belongs
session name of the session
page page of the session, front is default

Returns:
rendered session page
"""

def session_url(session):
return url_for('session_page', run=run, session=session, page=page)

template = session_template_or_404(run, session, page)
content = Markup(template.render())
md_content = Markup(convert_markdown(content))

return render_template('lesson.html', content=md_content, page=page, session=True)
5 changes: 5 additions & 0 deletions naucse/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ def lesson_url(lesson, page='index'):
return url_for('lesson', lesson=lesson, page=page)


@template_function
def session_url(run, session, page='front'):
return url_for('session_page', run=run, session=session, page=page)


@template_function
def var(name):
"""Return a page variable
Expand Down
12 changes: 11 additions & 1 deletion naucse/templates/_lessons_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@
{% for session in plan.values() %}
<div class="section{{ loop.index }}">
<h4>
{% if run is defined %}
<a href="{{ session_url(run, session.slug) }}">
{% if session.date %}
Lekce {{ loop.index }} - {{ session.title }} ({{ session.date }})
{% else %}
Lekce {{ loop.index }} - {{ session.title }}
{% endif %}
</h4>
</a>
{% else %}
{% if session.date %}
Lekce {{ loop.index }} - {{ session.title }} ({{ session.date }})
{% else %}
Lekce {{ loop.index }} - {{ session.title }}
{% endif %}
{% endif %}
</h4>
{% for mat in session.materials %}
<div>
<span class="glyphicon-pencil"></span>
Expand Down
Loading