aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/resources/views/resources.py
diff options
context:
space:
mode:
authorGravatar minalike <[email protected]>2022-02-20 21:20:29 -0500
committerGravatar minalike <[email protected]>2022-02-20 21:20:29 -0500
commit0e1257d6eca90fa45ba7c11454a0fba7e3fc74f7 (patch)
treee3ad78be879f615b2621c0afba9db813e70fafff /pydis_site/apps/resources/views/resources.py
parentAdd table of content and final edits (diff)
parentMerge pull request #668 from python-discord/update/name-profile-policy (diff)
Merge branch 'main' into content/update-help-channel-timing
Diffstat (limited to 'pydis_site/apps/resources/views/resources.py')
-rw-r--r--pydis_site/apps/resources/views/resources.py127
1 files changed, 123 insertions, 4 deletions
diff --git a/pydis_site/apps/resources/views/resources.py b/pydis_site/apps/resources/views/resources.py
index 25ce3e50..2375f722 100644
--- a/pydis_site/apps/resources/views/resources.py
+++ b/pydis_site/apps/resources/views/resources.py
@@ -1,7 +1,126 @@
-from django.views.generic import TemplateView
+import json
+import typing as t
+from pathlib import Path
+import yaml
+from django.core.handlers.wsgi import WSGIRequest
+from django.http import HttpResponse, HttpResponseNotFound
+from django.shortcuts import render
+from django.views import View
-class ResourcesView(TemplateView):
- """View for resources index page."""
+from pydis_site import settings
+from pydis_site.apps.resources.templatetags.to_kebabcase import to_kebabcase
- template_name = "resources/resources.html"
+RESOURCES_PATH = Path(settings.BASE_DIR, "pydis_site", "apps", "resources", "resources")
+
+
+class ResourceView(View):
+ """Our curated list of good learning resources."""
+
+ @staticmethod
+ def _sort_key_disregard_the(tuple_: tuple) -> str:
+ """Sort a tuple by its key alphabetically, disregarding 'the' as a prefix."""
+ name, resource = tuple_
+ name = name.casefold()
+ if name.startswith("the ") or name.startswith("the_"):
+ return name[4:]
+ return name
+
+ def __init__(self, *args, **kwargs):
+ """Set up all the resources."""
+ super().__init__(*args, **kwargs)
+
+ # Load the resources from the yaml files in /resources/
+ self.resources = {
+ path.stem: yaml.safe_load(path.read_text())
+ for path in RESOURCES_PATH.rglob("*.yaml")
+ }
+
+ # Sort the resources alphabetically
+ self.resources = dict(sorted(self.resources.items(), key=self._sort_key_disregard_the))
+
+ # Parse out all current tags
+ resource_tags = {
+ "topics": set(),
+ "payment_tiers": set(),
+ "difficulty": set(),
+ "type": set(),
+ }
+ for resource_name, resource in self.resources.items():
+ css_classes = []
+ for tag_type in resource_tags.keys():
+ # Store the tags into `resource_tags`
+ tags = resource.get("tags", {}).get(tag_type, [])
+ for tag in tags:
+ tag = tag.title()
+ tag = tag.replace("And", "and")
+ resource_tags[tag_type].add(tag)
+
+ # Make a CSS class friendly representation too, while we're already iterating.
+ for tag in tags:
+ css_tag = to_kebabcase(f"{tag_type}-{tag}")
+ css_classes.append(css_tag)
+
+ # Now add the css classes back to the resource, so we can use them in the template.
+ self.resources[resource_name]["css_classes"] = " ".join(css_classes)
+
+ # Set up all the filter checkbox metadata
+ self.filters = {
+ "Difficulty": {
+ "filters": sorted(resource_tags.get("difficulty")),
+ "icon": "fas fa-brain",
+ "hidden": False,
+ },
+ "Type": {
+ "filters": sorted(resource_tags.get("type")),
+ "icon": "fas fa-photo-video",
+ "hidden": False,
+ },
+ "Payment tiers": {
+ "filters": sorted(resource_tags.get("payment_tiers")),
+ "icon": "fas fa-dollar-sign",
+ "hidden": True,
+ },
+ "Topics": {
+ "filters": sorted(resource_tags.get("topics")),
+ "icon": "fas fa-lightbulb",
+ "hidden": True,
+ }
+ }
+
+ # The bottom topic should always be "Other".
+ self.filters["Topics"]["filters"].remove("Other")
+ self.filters["Topics"]["filters"].append("Other")
+
+ # A complete list of valid filter names
+ self.valid_filters = {
+ "topics": [to_kebabcase(topic) for topic in self.filters["Topics"]["filters"]],
+ "payment_tiers": [
+ to_kebabcase(tier) for tier in self.filters["Payment tiers"]["filters"]
+ ],
+ "type": [to_kebabcase(type_) for type_ in self.filters["Type"]["filters"]],
+ "difficulty": [to_kebabcase(tier) for tier in self.filters["Difficulty"]["filters"]],
+ }
+
+ def get(self, request: WSGIRequest, resource_type: t.Optional[str] = None) -> HttpResponse:
+ """List out all the resources, and any filtering options from the URL."""
+ # Add type filtering if the request is made to somewhere like /resources/video.
+ # We also convert all spaces to dashes, so they'll correspond with the filters.
+ if resource_type:
+ dashless_resource_type = resource_type.replace("-", " ")
+
+ if dashless_resource_type.title() not in self.filters["Type"]["filters"]:
+ return HttpResponseNotFound()
+
+ resource_type = resource_type.replace(" ", "-")
+
+ return render(
+ request,
+ template_name="resources/resources.html",
+ context={
+ "resources": self.resources,
+ "filters": self.filters,
+ "valid_filters": json.dumps(self.valid_filters),
+ "resource_type": resource_type,
+ }
+ )