aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/resources/views.py
diff options
context:
space:
mode:
authorGravatar Johannes Christ <[email protected]>2023-04-14 14:42:16 +0200
committerGravatar GitHub <[email protected]>2023-04-14 12:42:16 +0000
commit0524176aa3392aa9978a420c6089012e91ebbbc3 (patch)
tree49c1d5c08d2c68468dc5e4b3bcbf424d6c268ccc /pydis_site/apps/resources/views.py
parentMerge pull request #938 from python-discord/dependabot/pip/httpx-0.24.0 (diff)
Add README to the resources app (#934)
Diffstat (limited to 'pydis_site/apps/resources/views.py')
-rw-r--r--pydis_site/apps/resources/views.py126
1 files changed, 126 insertions, 0 deletions
diff --git a/pydis_site/apps/resources/views.py b/pydis_site/apps/resources/views.py
new file mode 100644
index 00000000..2375f722
--- /dev/null
+++ b/pydis_site/apps/resources/views.py
@@ -0,0 +1,126 @@
+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
+
+from pydis_site import settings
+from pydis_site.apps.resources.templatetags.to_kebabcase import to_kebabcase
+
+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,
+ }
+ )