Skip to content

Commit

Permalink
Merge pull request #22 from VaagenIM/develop
Browse files Browse the repository at this point in the history
fix: debug logging should not be present in the generated pages
  • Loading branch information
sondregronas authored Sep 4, 2024
2 parents 45ec05d + 934b000 commit bda1a75
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 42 deletions.
19 changes: 14 additions & 5 deletions piggy/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
from piggy import ASSIGNMENT_ROUTE, MEDIA_ROUTE, AssignmentTemplate
from piggy.api import api_routes
from piggy.api import generate_thumbnail
from piggy.caching import lru_cache_wrapper, _render_assignment, cache_directory, _render_assignment_wildcard
from piggy.caching import cache_directory, _render_assignment_wildcard
from piggy.exceptions import PiggyHTTPException
from piggy.piggybank import PIGGYMAP, get_piggymap_segment_from_path
from piggy.utils import normalize_path_to_str
from piggy.utils import normalize_path_to_str, lru_cache_wrapper

# Ensure the working directory is the root of the project
os.chdir(os.path.dirname(Path(__file__).parent.absolute()))
Expand All @@ -19,9 +19,11 @@
# TODO: Logging


def create_app():
def create_app(debug: bool = False) -> Flask:
app = Flask(__name__, static_folder="static")

app.debug = debug

assignment_routes = Blueprint(ASSIGNMENT_ROUTE, __name__, url_prefix=f"/{ASSIGNMENT_ROUTE}")
media_routes = Blueprint(MEDIA_ROUTE, __name__, url_prefix=f"/{MEDIA_ROUTE}")

Expand All @@ -43,6 +45,7 @@ def context_processor():
}

@app.template_global()
@lru_cache_wrapper
def get_template_name_from_index(i: int):
"""Return the template path for the index."""
# Used to get the name of the template from the index via a path (breadcrumbs)
Expand All @@ -55,6 +58,7 @@ def index():

@assignment_routes.route("/<path:path>")
@assignment_routes.route("/")
@lru_cache_wrapper
def get_assignment_wildcard(path="", lang=""):
path = path.strip("/")
path = normalize_path_to_str(path, replace_spaces=True)
Expand All @@ -75,6 +79,7 @@ def get_assignment_wildcard(path="", lang=""):

@assignment_routes.route("/<path:path>/lang/<lang>")
@assignment_routes.route("/<path:path>/lang/")
@lru_cache_wrapper
def get_assignment_wildcard_lang(path, lang=""):
"""Only used when GitHub Pages is used to host the site."""
return get_assignment_wildcard(path, lang)
Expand All @@ -86,6 +91,10 @@ def get_assignment_media_wildcard(wildcard, filename):
Get a media file from either the media or attachments folder.
(only in MEDIA_URL_PREFIX or ASSIGNMENT_URL_PREFIX)
"""

# TODO: This might be slower than necessary, but the demanding fns are cached in the LRU cache
# (This fn cannot be cached as it handles files)

if ["lang", "attachments"] == request.path.split("/")[-3:-1]:
# If a language is specified, remove it from the wildcard (+ the assignment name)
# This only happens when the language is specified in the URL and not via cookies
Expand All @@ -111,8 +120,8 @@ def get_assignment_media_wildcard(wildcard, filename):
app.register_blueprint(api_routes)

# Cache all assignment related pages if not in debug mode
if not app.debug:
if os.environ.get("USE_CACHE", "1") == "1":
with app.app_context(), app.test_request_context():
cache_directory(PIGGYMAP, directory_fn=_render_assignment_wildcard, assignment_fn=_render_assignment)
cache_directory(PIGGYMAP, fn=get_assignment_wildcard)

return app
55 changes: 37 additions & 18 deletions piggy/caching.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
from pathlib import Path
from typing import Callable
from typing import Callable, Optional

from flask import Response, render_template
from frozendict import deepfreeze
from turtleconverter import mdfile_to_sections, ConversionError

from piggy import (
ASSIGNMENT_ROUTE,
MEDIA_ROUTE,
PIGGYBANK_FOLDER,
AssignmentTemplate,
)
from piggy.exceptions import PiggyHTTPException
from piggy.exceptions import PiggyHTTPException, PiggyErrorException
from piggy.models import LANGUAGES
from piggy.piggybank import (
get_all_meta_from_path,
Expand All @@ -22,40 +22,52 @@
from piggy.utils import (
get_supported_languages,
generate_summary_from_mkdocs_html,
lru_cache_wrapper,
normalize_path_to_str,
lru_cache_wrapper,
)


def cache_directory(
segment: dict, directory_fn: Callable[[str], Response], assignment_fn: Callable[[Path], Response], _path: str = ""
segment: dict,
fn: Callable[[str, Optional[str]], Response],
_path: str = "",
):
"""Cache the directory of assignments."""
for key, value in segment.items():
print(f"Caching: {_path}/{key}")
directory_fn(f"{_path}/{key}".strip("/"))
fn(f"{_path}/{key}".strip("/"))

# If we are just above the assignment level, its children will be the assignments
if len(_path.split("/")) == AssignmentTemplate.ASSIGNMENT.index - 1:
for assignment, assignment_data in value.get("data", {}).items():
assignment_path = f"{_path}/{key}/{assignment}".strip("/")
assignment_path = Path(f"{PIGGYBANK_FOLDER}/{assignment_path}.md")
assignment_fn(assignment_path)
# Get the path of the assignment (Path object
assignment_path_obj = segment.get(key, {}).get("data", {}).get(assignment, {}).get("path", Path(""))

# Set the assignment path to a string with the right url format
assignment_path = str(f"{_path}/{key}/{assignment}")

if not assignment_path_obj.exists():
raise PiggyErrorException(f"Assignment not found: {assignment_path}")

fn(assignment_path, "")
[
assignment_fn(Path(f"{assignment_path.parent}/translations/{lang}/{assignment}.md"))
fn(f"{assignment_path}", lang)
for lang in LANGUAGES.keys()
if Path(f"{assignment_path.parent}/translations/{lang}/{assignment}.md").exists()
if Path(f"{assignment_path_obj.parent}/translations/{lang}/{assignment}.md").exists()
]
# If we are at the assignment level, we are done
elif len(_path.split("/")) > AssignmentTemplate.ASSIGNMENT.index - 1:
return
else:
cache_directory(
value.get("data", {}), directory_fn=directory_fn, assignment_fn=assignment_fn, _path=f"{_path}/{key}"
)
cache_directory(value.get("data", {}), fn=fn, _path=f"{_path}/{key}")


@lru_cache_wrapper
def _render_assignment(p: Path, extra_metadata: dict = dict) -> Response:
def _render_assignment(p: Path, extra_metadata=None) -> Response:
"""Render an assignment from a Path object."""

extra_metadata = dict(extra_metadata)

if not p.exists():
raise PiggyHTTPException("Assignment not found", status_code=404)
try:
Expand All @@ -73,8 +85,8 @@ def _render_assignment(p: Path, extra_metadata: dict = dict) -> Response:
current_language = LANGUAGES.get(lang, "")["name"]

# Get the assignment data
assignment_data = get_assignment_data_from_path(assignment_path, PIGGYMAP).copy()
meta = assignment_data.get("meta", {}).copy()
assignment_data = dict(get_assignment_data_from_path(assignment_path, PIGGYMAP).copy())
meta = dict(assignment_data.get("meta", {}).copy())
if "summary" not in meta:
meta["summary"] = generate_summary_from_mkdocs_html(sections["body"])
assignment_data.pop("meta")
Expand Down Expand Up @@ -102,6 +114,11 @@ def _render_assignment_wildcard(path="", lang="") -> Response:
"""
template_type = get_template_from_path(path)
metadata, segment = get_piggymap_segment_from_path(path, PIGGYMAP)

# If a piggymap segment is not found, raise a 404
if not segment:
raise PiggyHTTPException("Page not found", status_code=404)

metadata = {**metadata, **get_all_meta_from_path(path, PIGGYMAP)}

media_abspath = f"/{MEDIA_ROUTE}/{path}" if path else f"/{MEDIA_ROUTE}"
Expand All @@ -120,7 +137,9 @@ def _render_assignment_wildcard(path="", lang="") -> Response:
# If a language is specified, set the assignment path to the translation
if lang:
assignment = f"translations/{lang}/{assignment}"
return _render_assignment(Path(f"{path}/{assignment}"), extra_metadata=metadata)

# Render the assignment with the metadata (must be deepfrozen to be hashable)
return _render_assignment(Path(f"{path}/{assignment}"), extra_metadata=deepfreeze(metadata))

# Render the appropriate template (if it is not the final level)
return Response(
Expand Down
4 changes: 4 additions & 0 deletions piggy/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ class PiggyException(Exception):
pass


class PiggyErrorException(Exception):
pass


class PiggyHTTPException(PiggyException):
def __init__(self, message, status_code):
self.message = message
Expand Down
10 changes: 5 additions & 5 deletions piggy/piggybank.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
from pathlib import Path

from frozendict.cool import deepfreeze
from turtleconverter import mdfile_to_sections

from piggy import AssignmentTemplate, PIGGYBANK_FOLDER, ASSIGNMENT_FILENAME_REGEX
Expand All @@ -24,7 +25,7 @@ def load_meta_json(path: Path):
def get_piggymap_segment_from_path(path: str or Path, piggymap: dict) -> tuple[dict, dict]:
"""Get the metadata and segment from a path."""
path = normalize_path_to_str(path, replace_spaces=True)
segment = dict(piggymap.copy())
segment = piggymap.copy()
meta = segment.get("meta", {})
for path in path.split("/"):
if not path:
Expand All @@ -39,7 +40,6 @@ def get_piggymap_segment_from_path(path: str or Path, piggymap: dict) -> tuple[d


# TODO: these could probably be combined into one function
@lru_cache_wrapper
def get_all_meta_from_path(path: str or Path, piggymap: dict) -> dict:
"""Get all metadata from a path."""
metadata = dict()
Expand Down Expand Up @@ -67,7 +67,6 @@ def get_all_meta_from_path(path: str or Path, piggymap: dict) -> dict:


# TODO: these could probably be combined into one function
@lru_cache_wrapper
def get_assignment_data_from_path(path: str or Path, piggymap: dict) -> dict:
"""Get the assignment data from a path."""
path = normalize_path_to_str(path, replace_spaces=True, normalize_url=True, remove_ext=True)
Expand Down Expand Up @@ -151,12 +150,13 @@ def recursive_sort(data):
return recursive_sort(piggymap)


PIGGYMAP = generate_piggymap(PIGGYBANK_FOLDER)
# Piggymap must be hashable for sooper dooper speed
PIGGYMAP = deepfreeze(generate_piggymap(PIGGYBANK_FOLDER))


# DEVTOOL
def __update_piggymap():
global PIGGYMAP
print("Rebuilding piggymap")
PIGGYMAP = generate_piggymap(PIGGYBANK_FOLDER)
PIGGYMAP = deepfreeze(generate_piggymap(PIGGYBANK_FOLDER))
print("Piggymap rebuilt")
6 changes: 4 additions & 2 deletions piggy/thumbnails.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import textwrap
from pathlib import Path
from piggy.caching import lru_cache_wrapper

import PIL.Image
import PIL.ImageDraw
import PIL.ImageFont
import textwrap

from piggy.utils import lru_cache_wrapper


# TODO: this is a mess.
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
name = "piggy"
version = "0.1.0"
description = ""
license = {file = "LICENSE.md"}
license = { file = "LICENSE.md" }
readme = "README.md"
dependencies = [
"flask~=3.0.3",
"turtleconverter@git+https://github.com/sondregronas/turtleconverter@main",
"gunicorn~=23.0.0",
"pillow~=10.4.0",
"beautifulsoup4~=4.12.3",
"frozendict==2.4.4",
]

[build-system]
Expand Down
33 changes: 22 additions & 11 deletions run.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import os
import subprocess

from piggy.app import create_app


def run_tailwind(reload=False):
cmd = (
Expand All @@ -21,26 +19,39 @@ def checkout_branch(branch):


if __name__ == "__main__":
from piggy.devtools import inject_devtools
from piggy.piggybank import __update_piggymap
# Debug
import logging

# Reduce the amount of logging from werkzeug
log = logging.getLogger("werkzeug")
log.setLevel(logging.ERROR)
os.environ["FLASK_DEBUG"] = "1"

# Set the environment variables for testing
os.environ["USE_CACHE"] = "0"
app = create_app()
inject_devtools(app)
__update_piggymap()
os.environ["FLASK_DEBUG"] = "1"

# Run these once on the first run
if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
# This code will run only once, not in the reloaded processes
checkout_branch("test-output")
run_tailwind(reload=True)
run_tailwind(reload=True) # TODO: This does not keep watching for changes
subprocess.Popen('npx livereload "piggy/, piggybank/"', shell=True)

# Import after setting the environment variables for testing
from piggy.app import create_app
from piggy.devtools import inject_devtools
from piggy.piggybank import __update_piggymap

app = create_app(debug=os.environ.get("FLASK_DEBUG", False) == "1")
inject_devtools(app) # Inject devtools
__update_piggymap() # Run on every reload

app.run(port=5001)
else:
# TODO: Re-enable
# Production
from piggy.app import create_app

# TODO: Re-enable (requires branch to be published) (or a env to pass the branch with a PAT)
# checkout_branch("output")
run_tailwind() # Runs once to generate the CSS file
app = create_app()
app = create_app(debug=os.environ.get("FLASK_DEBUG", False) == "1")

0 comments on commit bda1a75

Please sign in to comment.