aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/resources/views.py
blob: 2375f722027566e3bf8f06945ebdb9ab9010f806 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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,
            }
        )