aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/content
diff options
context:
space:
mode:
Diffstat (limited to 'pydis_site/apps/content')
-rw-r--r--pydis_site/apps/content/urls.py12
-rw-r--r--pydis_site/apps/content/utils.py101
-rw-r--r--pydis_site/apps/content/views/page_category.py5
-rw-r--r--pydis_site/apps/content/views/tags.py79
4 files changed, 159 insertions, 38 deletions
diff --git a/pydis_site/apps/content/urls.py b/pydis_site/apps/content/urls.py
index b4ffc07d..03c0015a 100644
--- a/pydis_site/apps/content/urls.py
+++ b/pydis_site/apps/content/urls.py
@@ -39,15 +39,21 @@ def get_all_pages() -> DISTILL_RETURN:
def get_all_tags() -> DISTILL_RETURN:
- """Return all tag names in the repository in static builds."""
+ """Return all tag names and groups in static builds."""
+ groups = {None}
for tag in utils.get_tags_static():
- yield {"name": tag.name}
+ groups.add(tag.group)
+ yield {"location": (f"{tag.group}/" if tag.group else "") + tag.name}
+
+ groups.remove(None)
+ for group in groups:
+ yield {"location": group}
urlpatterns = [
distill_path("", views.PageOrCategoryView.as_view(), name='pages'),
distill_path(
- "tags/<str:name>/",
+ "tags/<path:location>/",
views.TagView.as_view(),
name="tag",
distill_func=get_all_tags
diff --git a/pydis_site/apps/content/utils.py b/pydis_site/apps/content/utils.py
index cc08f81f..da6a024d 100644
--- a/pydis_site/apps/content/utils.py
+++ b/pydis_site/apps/content/utils.py
@@ -2,6 +2,7 @@ import datetime
import functools
import tarfile
import tempfile
+import typing
from io import BytesIO
from pathlib import Path
@@ -16,7 +17,6 @@ from markdown.extensions.toc import TocExtension
from pydis_site import settings
from .models import Tag
-TAG_URL_BASE = "https://github.com/python-discord/bot/tree/main/bot/resources/tags"
TAG_CACHE_TTL = datetime.timedelta(hours=1)
@@ -44,9 +44,13 @@ def get_tags_static() -> list[Tag]:
"""
Fetch tag information in static builds.
+ This also includes some fake tags to preview the tag groups feature.
This will return a cached value, so it should only be used for static builds.
"""
- return fetch_tags()
+ tags = fetch_tags()
+ for tag in tags[3:5]:
+ tag.group = "very-cool-group"
+ return tags
def fetch_tags() -> list[Tag]:
@@ -79,10 +83,15 @@ def fetch_tags() -> list[Tag]:
repo.extractall(folder, included)
for tag_file in Path(folder).rglob("*.md"):
+ group = None
+ if tag_file.parent.name != "tags":
+ # Tags in sub-folders are considered part of a group
+ group = tag_file.parent.name
+
tags.append(Tag(
name=tag_file.name.removesuffix(".md"),
+ group=group,
body=tag_file.read_text(encoding="utf-8"),
- url=f"{TAG_URL_BASE}/{tag_file.name}"
))
return tags
@@ -114,31 +123,85 @@ def get_tags() -> list[Tag]:
return Tag.objects.all()
-def get_tag(name: str) -> Tag:
- """Return a tag by name."""
- tags = get_tags()
- for tag in tags:
- if tag.name == name:
+def get_tag(path: str) -> typing.Union[Tag, list[Tag]]:
+ """
+ Return a tag based on the search location.
+
+ The tag name and group must match. If only one argument is provided in the path,
+ it's assumed to either be a group name, or a no-group tag name.
+
+ If it's a group name, a list of tags which belong to it is returned.
+ """
+ path = path.split("/")
+ if len(path) == 2:
+ group, name = path[0], path[1]
+ else:
+ name = path[0]
+ group = None
+
+ matches = []
+ for tag in get_tags():
+ if tag.name == name and tag.group == group:
return tag
+ elif tag.group == name and group is None:
+ matches.append(tag)
+
+ if matches:
+ return matches
raise Tag.DoesNotExist()
-def get_category_pages(path: Path) -> dict[str, dict]:
- """Get all page names and their metadata at a category path."""
- # Special handling for tags
- if path == Path(__file__).parent / "resources/tags":
- tags = {}
- for tag in get_tags():
- content = frontmatter.parse(tag.body)[1]
+def get_tag_category(
+ tags: typing.Optional[list[Tag]] = None, *, collapse_groups: bool
+) -> dict[str, dict]:
+ """
+ Generate context data for `tags`, or all tags if None.
+
+ If `tags` is None, `get_tag` is used to populate the data.
+ If `collapse_groups` is True, tags with parent groups are not included in the list,
+ and instead the parent itself is included as a single entry with it's sub-tags
+ in the description.
+ """
+ if not tags:
+ tags = get_tags()
+
+ data = []
+ groups = {}
- tags[tag.name] = {
+ # Create all the metadata for the tags
+ for tag in tags:
+ if tag.group is None or not collapse_groups:
+ content = frontmatter.parse(tag.body)[1]
+ data.append({
"title": tag.name,
"description": markdown.markdown(content, extensions=["pymdownx.superfences"]),
- "icon": "fas fa-tag"
- }
+ "icon": "fas fa-tag",
+ })
+ else:
+ if tag.group not in groups:
+ groups[tag.group] = {
+ "title": tag.group,
+ "description": [tag.name],
+ "icon": "fas fa-tags",
+ }
+ else:
+ groups[tag.group]["description"].append(tag.name)
- return {name: tags[name] for name in sorted(tags)}
+ # Flatten group description into a single string
+ for group in groups.values():
+ group["description"] = "Contains the following tags: " + ", ".join(group["description"])
+ data.append(group)
+
+ # Sort the tags, and return them in the proper format
+ return {tag["title"]: tag for tag in sorted(data, key=lambda tag: tag["title"].lower())}
+
+
+def get_category_pages(path: Path) -> dict[str, dict]:
+ """Get all page names and their metadata at a category path."""
+ # Special handling for tags
+ if path == Path(__file__).parent / "resources/tags":
+ return get_tag_category(collapse_groups=True)
pages = {}
diff --git a/pydis_site/apps/content/views/page_category.py b/pydis_site/apps/content/views/page_category.py
index 01ce8402..062c2bc1 100644
--- a/pydis_site/apps/content/views/page_category.py
+++ b/pydis_site/apps/content/views/page_category.py
@@ -5,7 +5,7 @@ from django.conf import settings
from django.http import Http404, HttpRequest, HttpResponse
from django.views.generic import TemplateView
-from pydis_site.apps.content import utils
+from pydis_site.apps.content import models, utils
class PageOrCategoryView(TemplateView):
@@ -91,4 +91,7 @@ class PageOrCategoryView(TemplateView):
"page_title": category["title"],
"page_description": category["description"],
"icon": category.get("icon"),
+ "app_name": "content:page_category",
+ "is_tag_listing": "/resources/tags" in path.as_posix(),
+ "tag_url": models.Tag.URL_BASE,
}
diff --git a/pydis_site/apps/content/views/tags.py b/pydis_site/apps/content/views/tags.py
index 5295537d..a8df65db 100644
--- a/pydis_site/apps/content/views/tags.py
+++ b/pydis_site/apps/content/views/tags.py
@@ -1,4 +1,5 @@
import re
+import typing
import frontmatter
import markdown
@@ -16,23 +17,65 @@ COMMAND_REGEX = re.compile(r"`*!tags? (?P<name>[\w\d-]+)`*")
class TagView(TemplateView):
"""Handles tag pages."""
- template_name = "content/tag.html"
+ tag: typing.Union[Tag, list[Tag]]
+ is_group: bool
+
+ def setup(self, *args, **kwargs) -> None:
+ """Look for a tag, and configure the view."""
+ super().setup(*args, **kwargs)
- def get_context_data(self, **kwargs) -> dict:
- """Get the relevant context for this tag page."""
try:
- tag = utils.get_tag(kwargs.get("name"))
+ self.tag = utils.get_tag(kwargs.get("location"))
+ self.is_group = isinstance(self.tag, list)
except Tag.DoesNotExist:
raise Http404
+ def get_template_names(self) -> list[str]:
+ """Either return the tag page template, or the listing."""
+ if self.is_group:
+ template_name = "content/listing.html"
+ else:
+ template_name = "content/tag.html"
+
+ return [template_name]
+
+ def get_context_data(self, **kwargs) -> dict:
+ """Get the relevant context for this tag page or group."""
context = super().get_context_data(**kwargs)
- context["page_title"] = tag.name
+ context["breadcrumb_items"] = [{
+ "name": utils.get_category(settings.CONTENT_PAGES_PATH / location)["title"],
+ "path": location,
+ } for location in (".", "tags")]
+
+ if self.is_group:
+ self._set_group_context(context, self.tag)
+ else:
+ self._set_tag_context(context, self.tag)
+
+ return context
+
+ @staticmethod
+ def _set_tag_context(context: dict[str, any], tag: Tag) -> None:
+ """Update the context with the information for a tag page."""
+ context.update({
+ "page_title": tag.name,
+ "tag": tag,
+ })
+
+ if tag.group:
+ # Add group names to the breadcrumbs
+ context["breadcrumb_items"].append({
+ "name": tag.group,
+ "path": f"tags/{tag.group}",
+ })
+
+ # Clean up tag body
body = frontmatter.parse(tag.body)
content = body[1]
# Check for tags which can be hyperlinked
def sub(match: re.Match) -> str:
- link = reverse("content:tag", kwargs={"name": match.group("name")})
+ link = reverse("content:tag", kwargs={"location": match.group("name")})
return f"[{match.group()}]({link})"
content = COMMAND_REGEX.sub(sub, content)
@@ -42,14 +85,20 @@ class TagView(TemplateView):
if image := embed.get("image"):
content = f"![{embed['title']}]({image['url']})\n\n" + content
+ # Insert the content
+ context["page"] = markdown.markdown(content, extensions=["pymdownx.superfences"])
+
+ @staticmethod
+ def _set_group_context(context: dict[str, any], tags: list[Tag]) -> None:
+ """Update the context with the information for a group of tags."""
+ group = tags[0].group
context.update({
- "page": markdown.markdown(content, extensions=["pymdownx.superfences"]),
- "tag": tag,
+ "categories": {},
+ "pages": utils.get_tag_category(tags, collapse_groups=False),
+ "page_title": group,
+ "icon": "fab fa-tags",
+ "is_tag_listing": True,
+ "app_name": "content:tag",
+ "path": f"{group}/",
+ "tag_url": f"{tags[0].URL_BASE}/{group}"
})
-
- context["breadcrumb_items"] = [{
- "name": utils.get_category(settings.CONTENT_PAGES_PATH / location)["title"],
- "path": str(location)
- } for location in [".", "tags"]]
-
- return context