Skip to content

Commit 825eb5a

Browse files
mikiczhroncok
authored andcommitted
Rendering courses from forks and content fragments caching (#362)
Merges #362
1 parent 62e6617 commit 825eb5a

36 files changed

+2846
-658
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010

1111
venv
1212
_build
13+
.arca

.travis.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@ language: python
22
python:
33
- '3.6'
44
cache:
5-
- pip
5+
directories:
6+
- $HOME/.cache/pip
7+
- .arca/cache
68
install:
79
- pip install -r test_requirements.txt
10+
before_script: # encrypted variables aren't accessible in PRs
11+
- 'if [ -n "$DOCKER_HUB_PASSWORD" ]; then docker login -u "$DOCKER_HUB_USERNAME" -p "$DOCKER_HUB_PASSWORD"; fi'
12+
- 'if [ -z "$DOCKER_HUB_PASSWORD" ]; then export ARCA_BACKEND_REGISTRY_PULL_ONLY=true; fi'
813
script:
914
- python -m pytest test_naucse
15+
- python -m naucse list_courses # don't remove, there might be timeouts otherwise, see naucse/cli.py for details
1016
- python -m naucse freeze
17+
after_script:
18+
- echo "Suplementary logs:"
19+
- cat .arca/naucse.log
20+
# so Travis CI stores the whole log in its build log (https://github.com/travis-ci/travis-ci/issues/6018)
21+
- sleep 1
1122
deploy:
1223
provider: script
1324
skip_cleanup: true
@@ -18,3 +29,10 @@ deploy:
1829
env:
1930
global:
2031
- secure: pEZZeBdTSg0WUQYfwKZCiM+VMmNf2GyCCDa5Qcw2xGaNi5Z3rb3IYntY5ACKtDazgjzul+AqHra7iEx41yictXu4wYYt1nSY7g+ilBq/SlefiTi92ZRLH9elEChOa4A2njeuFIzwLBRr4Hdnn+21Vd/uxbC0wGhRC5c11ixBVC7Kn6zatT2TXUN+zF5a63NgUDrY/SPMV0jX3nozFHOxAQmZqlV1VL7O5ihct+mLYxB6MhS1NO/v30cMwyQm9nO0wBGJ1YH5/SKk9ThvFKH15d0xLVjZMfhJ/sgqpRnuRw4awH7SV7p2BoF7/07P8VWnCNvJ+wCipd1PqAk3COgqriWCBx80auaevIDxzky4CUk/efd3WYOA4MnzSAvcjbcQjojjsi64WWwVAcWuweTA8AEYMhVdqWlMbo8E3Oa0dZcyscBo+L26OdAdKNUqeNVQ84oIcPN/iUCitLB5GVOc0cIkn9wFer66jSuwK9bRfOTmV6NaRdLt78Edo+e1wXx0sdTVasL/rtIRTJAKKUtF8D/8RFfCNopOcceAyPr/Ay3JjVEFk2rTWeZls8E8os3TN6evVawx0t2xA0qXYb6IIrb0CFxv6sz/pU7vkuzS8g+pdQnfMmNtQ01sGGyedTT4YQRcgGKmWG+kolOXQlxJCGOndgz3VGN3DCppA5Ahbfo=
32+
- ARCA_BACKEND=arca.backend.DockerBackend
33+
- FORKS_ENABLED=true
34+
- DOCKER_HUB_USERNAME=naucse
35+
- secure: eg0Lj+IvZnNwf6rK7ZNsrhZs0V2ap9TylSOh3B+xthmwLBGB2fxtVQrjJEAimwoUEEzO6XAyGWuKa2RMtPUYx0758diEZCBT2Ct9aprhz2m9girLyUynAAfBumVv/0iMYqh9QShzfGLkF/f3Z/bkIheVhKV7jBCy5iHBLGzlSr2i4lZjFUE/lYNnQh+KCVx9HGKmSm4111x57ID+N73poe5mshWE6QYw4gSjjwdMW4SYcABwkARFHLNc1cuZYJKwXtMeN1MHlp2bjdJB2wUtoyb7b8QSoGYu87egVsAHby5+ccClecu9LC6DWgFvXs/pCBriqiHPk72TC0Zfx3pWpAhQcGZ85FqN4ZNL3q2hahodHbML38FjcbgQFNE9s1eT2q+y16R8Hy34HhvDCEx13+4xwjPHNkkjm/kQOzQ1cBwmiP52PgmsxOtNVu9sEOVmSUShf9oz15E0bpX8U+GG4cjJN15vGgfvg5lkpuVdRnoz+voi+9i3yDZ4XBD9E79LZxmHkZ5gEXdRWXnI6YmE9Ejq2csyLr4RPmicslLd24QWWSYmJ2MWzSaJmSFBiw2OZn880EqRlaVKSUS0kzvT5YYo6eb36cslSVK9QVBCAgzRuPH0GHKhnV5cPOQjgNEGISmLbTgMubxILNT077dcXL7cbB29YCN+zIyAoFH9gCs=
36+
sudo: required
37+
services:
38+
- docker

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Chceš-li server spustit na svém počítači, např. proto, že se chceš zapoj
1212
nebo abys ho měl/a k dispozici i bez připojení k Intenetu, je potřeba ho
1313
nejdřív nainstalovat:
1414

15-
* Vytvoř a aktivuj si [virtuální prostředí](http://naucse.python.cz/lessons/beginners/install/) v Pythonu 3.6. (Verze 3.5 by teoreticky měla taky fungovat, ale cokoli staršího fungovat nebude.)
15+
* Vytvoř a aktivuj si [virtuální prostředí](http://naucse.python.cz/lessons/beginners/install/) v Pythonu 3.6.
1616
* Přepni se do adresáře s kódem projektu.
1717
* Nainstaluj závislosti:
1818
```console
@@ -37,7 +37,6 @@ Nainstalovanou aplikaci spustíš následovně:
3737
```
3838
* Program vypíše adresu (např. `http://0.0.0.0:8003/`); tu navštiv v prohlížeči.
3939

40-
4140
## Testy
4241

4342
Chceš-li pustit testy, nainstaluj si závislosti:

naucse/__init__.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,43 @@
1+
import logging
12
import sys
3+
from logging.handlers import RotatingFileHandler
4+
from pathlib import Path
5+
6+
from naucse.freezer import NaucseFreezer
7+
28
if sys.version_info[0] <3 :
39
raise RuntimeError('We love Python 3.')
410

5-
from elsa import cli
6-
from naucse.routes import app
11+
from naucse.cli import cli
12+
from naucse.routes import app, lesson_static_generator
713

814

915
def main():
10-
cli(app, base_url='http://naucse.python.cz')
16+
arca_log_path = Path(".arca/arca.log")
17+
arca_log_path.parent.mkdir(exist_ok=True)
18+
arca_log_path.touch()
19+
20+
naucse_log_path = Path(".arca/naucse.log")
21+
naucse_log_path.touch()
22+
23+
def get_handler(path, **kwargs):
24+
handler = RotatingFileHandler(path, **kwargs)
25+
formatter = logging.Formatter("[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s")
26+
27+
handler.setLevel(logging.INFO)
28+
handler.setFormatter(formatter)
29+
30+
return handler
31+
32+
logger = logging.getLogger("arca")
33+
logger.addHandler(get_handler(arca_log_path, maxBytes=10000, backupCount=0))
34+
35+
logger = logging.getLogger("naucse")
36+
logger.addHandler(get_handler(naucse_log_path))
37+
38+
freezer = NaucseFreezer(app)
39+
40+
# see the generator for details
41+
freezer.register_generator(lesson_static_generator)
42+
43+
cli(app, base_url='http://naucse.python.cz', freezer=freezer)

naucse/cli.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import click
2+
import elsa
3+
4+
from naucse.utils.routes import forks_enabled, does_course_return_info
5+
6+
7+
def cli(app, *, base_url=None, freezer=None):
8+
""" Extends the elsa command line interface with a new command which prints all courses and runs
9+
which are present with basic info about them.
10+
"""
11+
elsa_group = elsa.cli(app, base_url=base_url, freezer=freezer, invoke_cli=False)
12+
13+
@click.group()
14+
def naucse():
15+
pass
16+
17+
@naucse.command()
18+
@click.option("--forks-only", default=False, is_flag=True,
19+
help="Only list courses and runs from forks")
20+
def list_courses(forks_only):
21+
""" List all courses and runs and info about them.
22+
23+
Mainly useful for courses from forks, shows where do they sourced from and if
24+
they return even the most basic information and will therefore be included in
25+
list of courses/runs.
26+
27+
A practical benefit is that on Travis CI the docker images are pulled/built
28+
in this command and the freezing won't timeout after the 10 minute limit if things are taking particularly long.
29+
"""
30+
from naucse.routes import model
31+
32+
def canonical(course, suffix=""):
33+
click.echo(f" {course.slug}: {course.title}{suffix}")
34+
35+
def fork_invalid(course):
36+
click.echo(f" {course.slug}, from {course.repo}@{course.branch}: "
37+
f"Fork doesn't return basic info, will be ignored.")
38+
39+
def fork_valid(course, suffix=""):
40+
click.echo(f" {course.slug}, from {course.repo}@{course.branch}: {course.title}{suffix}")
41+
42+
click.echo(f"Courses:")
43+
44+
for course in model.courses.values():
45+
if forks_only and not course.is_link():
46+
continue
47+
48+
if not course.is_link():
49+
canonical(course)
50+
elif forks_enabled():
51+
if does_course_return_info(course, force_ignore=True):
52+
fork_valid(course)
53+
else:
54+
fork_invalid(course)
55+
56+
click.echo(f"Runs:")
57+
58+
for course in model.runs.values():
59+
if forks_only and not course.is_link():
60+
continue
61+
62+
if not course.is_link():
63+
canonical(course, suffix=f" ({course.start_date} - {course.end_date})")
64+
elif forks_enabled():
65+
if does_course_return_info(course, ["start_date", "end_date"], force_ignore=True):
66+
fork_valid(course, suffix=f" ({course.start_date} - {course.end_date})")
67+
else:
68+
fork_invalid(course)
69+
70+
cli = click.CommandCollection(sources=[naucse, elsa_group])
71+
72+
return cli()

naucse/freezer.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import contextlib
2+
3+
from flask_frozen import UrlForLogger, Freezer
4+
5+
from naucse.utils.routes import absolute_urls_to_freeze
6+
7+
8+
class AllLinksLogger(UrlForLogger):
9+
""" AllLinksLogger primarily logs ``url_for`` calls, but yields urls from ``absolute_urls_to_freeze`` as well.
10+
"""
11+
12+
def iter_calls(self):
13+
""" Yields all logged urls and links parsed from content.
14+
Unfortunately, ``yield from`` cannot be used as the queues are modified on the go.
15+
"""
16+
while self.logged_calls or absolute_urls_to_freeze:
17+
if self.logged_calls:
18+
yield self.logged_calls.popleft()
19+
# prefer urls from :atrr:`logged_calls` - so, ideally, cache is populated from the base repository
20+
continue
21+
if absolute_urls_to_freeze:
22+
yield absolute_urls_to_freeze.popleft()
23+
24+
25+
@contextlib.contextmanager
26+
def temporary_url_for_logger(app):
27+
""" A context manager which temporary adds a new UrlForLogger to the app and yields it, so it can be used
28+
to get logged calls.
29+
"""
30+
logger = UrlForLogger(app)
31+
32+
yield logger
33+
34+
# reverses the following operating from :class:`UrlForLogger`
35+
# self.app.url_default_functions.setdefault(None, []).insert(0, logger)
36+
app.url_default_functions[None].pop(0)
37+
38+
39+
class NaucseFreezer(Freezer):
40+
41+
def __init__(self, app):
42+
super().__init__(app)
43+
self.url_for_logger = AllLinksLogger(app) # override the default url_for_logger with our modified version

0 commit comments

Comments
 (0)