Skip to content

Commit 0fd622d

Browse files
Merge pull request #689 from direct-framework/learning-resources-page
Learning resources page
2 parents 55ea0b9 + be667a0 commit 0fd622d

12 files changed

Lines changed: 211 additions & 45 deletions

File tree

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ repos:
4242
- django-import-export
4343
- types-django-import-export
4444
- django-multiselectfield
45+
- django-tables2
4546
- repo: https://github.com/igorshubovych/markdownlint-cli
4647
rev: v0.48.0
4748
hooks:

dev-requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ django==6.0.3
4141
# django-registration
4242
# django-stubs
4343
# django-stubs-ext
44+
# django-tables2
4445
django-bootstrap5==26.2
4546
# via direct_webapp (pyproject.toml)
4647
django-crispy-forms==2.6
@@ -61,6 +62,8 @@ django-stubs-ext==6.0.2
6162
# via
6263
# direct_webapp (pyproject.toml)
6364
# django-stubs
65+
django-tables2==3.0.0
66+
# via direct_webapp (pyproject.toml)
6467
djlint==1.36.4
6568
# via direct_webapp (pyproject.toml)
6669
editorconfig==0.17.1

direct_webapp/settings/settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
"crispy_forms",
128128
"crispy_bootstrap5",
129129
"import_export",
130+
"django_tables2",
130131
]
131132
MIDDLEWARE.insert(1, "whitenoise.middleware.WhiteNoiseMiddleware")
132133
STATIC_ROOT = BASE_DIR / "staticfiles"
@@ -138,6 +139,8 @@
138139
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
139140
CRISPY_TEMPLATE_PACK = "bootstrap5"
140141

142+
DJANGO_TABLES2_TEMPLATE = "django_tables2/bootstrap5-responsive.html"
143+
141144
# Ensure the logs directory exists
142145
LOGS_DIR = BASE_DIR / "logs"
143146
os.makedirs(LOGS_DIR, exist_ok=True)

doc-requirements.txt

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ certifi==2026.2.25
1515
charset-normalizer==3.4.6
1616
# via requests
1717
click==8.3.1
18-
# via mkdocs
18+
# via
19+
# mkdocs
20+
# properdocs
1921
colorama==0.4.6
2022
# via mkdocs-material
2123
confusable-homoglyphs==3.3.1
@@ -34,6 +36,7 @@ django==6.0.3
3436
# django-multiselectfield
3537
# django-registration
3638
# django-stubs-ext
39+
# django-tables2
3740
django-bootstrap5==26.2
3841
# via direct_webapp (pyproject.toml)
3942
django-crispy-forms==2.6
@@ -48,8 +51,12 @@ django-registration==5.2.1
4851
# via direct_webapp (pyproject.toml)
4952
django-stubs-ext==6.0.2
5053
# via direct_webapp (pyproject.toml)
54+
django-tables2==3.0.0
55+
# via direct_webapp (pyproject.toml)
5156
ghp-import==2.1.0
52-
# via mkdocs
57+
# via
58+
# mkdocs
59+
# properdocs
5360
griffelib==2.0.2
5461
# via mkdocstrings-python
5562
gunicorn==25.3.0
@@ -61,19 +68,22 @@ jinja2==3.1.6
6168
# mkdocs
6269
# mkdocs-material
6370
# mkdocstrings
71+
# properdocs
6472
markdown==3.10.2
6573
# via
6674
# mkdocs
6775
# mkdocs-autorefs
6876
# mkdocs-material
6977
# mkdocstrings
78+
# properdocs
7079
# pymdown-extensions
7180
markupsafe==3.0.3
7281
# via
7382
# jinja2
7483
# mkdocs
7584
# mkdocs-autorefs
7685
# mkdocstrings
86+
# properdocs
7787
mergedeep==1.3.4
7888
# via
7989
# mkdocs
@@ -115,12 +125,22 @@ packaging==26.0
115125
# via
116126
# gunicorn
117127
# mkdocs
128+
# properdocs
118129
paginate==0.5.7
119130
# via mkdocs-material
120131
pathspec==1.0.4
121-
# via mkdocs
132+
# via
133+
# mkdocs
134+
# properdocs
122135
platformdirs==4.9.4
123-
# via mkdocs-get-deps
136+
# via
137+
# mkdocs-get-deps
138+
# properdocs
139+
properdocs==1.6.7
140+
# via
141+
# mkdocs-gen-files
142+
# mkdocs-literate-nav
143+
# mkdocs-section-index
124144
pygments==2.20.0
125145
# via mkdocs-material
126146
pymdown-extensions==10.21.2
@@ -133,10 +153,13 @@ pyyaml==6.0.3
133153
# via
134154
# mkdocs
135155
# mkdocs-get-deps
156+
# properdocs
136157
# pymdown-extensions
137158
# pyyaml-env-tag
138159
pyyaml-env-tag==1.1
139-
# via mkdocs
160+
# via
161+
# mkdocs
162+
# properdocs
140163
requests==2.33.1
141164
# via mkdocs-material
142165
six==1.17.0
@@ -150,6 +173,8 @@ typing-extensions==4.15.0
150173
urllib3==2.6.3
151174
# via requests
152175
watchdog==6.0.0
153-
# via mkdocs
176+
# via
177+
# mkdocs
178+
# properdocs
154179
whitenoise==6.12.0
155180
# via direct_webapp (pyproject.toml)

main/tables.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Tables module for the main app."""
2+
3+
from typing import TYPE_CHECKING
4+
5+
import django_tables2 as tables
6+
from django.urls import reverse
7+
from django.utils.html import format_html
8+
from django.utils.safestring import SafeString, mark_safe
9+
10+
from .models import LearningResource, Skill
11+
12+
if TYPE_CHECKING: # pragma: no cover
13+
from django.db.models.fields.related_descriptors import ManyRelatedManager
14+
15+
16+
class LearningResourcesTable(tables.Table):
17+
"""Table class for the LearningResources model."""
18+
19+
skill_set = tables.ManyToManyColumn(verbose_name="Skills")
20+
21+
class Meta:
22+
"""Meta options for the LearningResourcesTable."""
23+
24+
model = LearningResource
25+
fields = ("name", "language", "provider")
26+
order_by = "name"
27+
28+
def render_name(self, value: str, record: LearningResource) -> SafeString:
29+
"""Include the URL in the name."""
30+
return format_html(
31+
'<a href="{}" target="_blank" rel="noopener noreferrer">{}</a>',
32+
record.url,
33+
value,
34+
)
35+
36+
def render_provider(self, value: str, record: LearningResource) -> SafeString:
37+
"""Include the URL in the provider name."""
38+
if record.provider is None or not record.provider.url:
39+
return mark_safe(value)
40+
41+
return format_html(
42+
'<a href="{}" target="_blank" rel="noopener noreferrer">{}</a>',
43+
record.provider.url,
44+
value,
45+
)
46+
47+
def render_skill_set(self, value: "ManyRelatedManager[Skill]") -> SafeString:
48+
"""Include the relevant skills as badges."""
49+
return mark_safe(
50+
"".join(
51+
format_html(
52+
'<a href="{}" target="_blank" rel="noopener noreferrer" '
53+
'class="badge bg-secondary">{}</a>',
54+
reverse("skill_detail", args=(skill.slug,)),
55+
skill.name,
56+
)
57+
for skill in value.all()
58+
)
59+
)

main/templates/main/pages/framework-overview.html

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,6 @@ <h1 class="pb-2 pb-lg-3">Framework overview</h1>
3131
href="{% url 'skills_and_competencies' %}">Browse framework</a>
3232
{% include "main/snippets/framework-mermaid-diagram.html" %}
3333
</div>
34-
<aside class="col-lg-3 offset-xl-1">
35-
<div class="offcanvas-lg offcanvas-end" id="sidebar">
36-
<div class="offcanvas-header">
37-
<h4 class="offcanvas-title">Sidebar</h4>
38-
<button class="btn-close ms-auto"
39-
type="button"
40-
data-bs-dismiss="offcanvas"
41-
data-bs-target="#sidebar"
42-
aria-label="Close"></button>
43-
</div>
44-
<div class="offcanvas-body">
45-
<h4 class="pt-1 pt-lg-0 mt-lg-n2"></h4>
46-
</div>
47-
</div>
48-
</aside>
4934
</div>
5035
</section>
5136
{% endblock content %}
Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{% extends "main/base.page.html" %}
22
{% load static %}
3+
{% load render_table from django_tables2 %}
34
{% block title %}
45
Digital Research Competencies Framework
56
{% endblock title %}
@@ -10,29 +11,12 @@
1011
<li class="breadcrumb-item active" aria-current="page">Learning resources</li>
1112
{% endblock breadcrumb_items %}
1213
{% block content %}
13-
<section id="training">
14+
<section id="learning-resources">
1415
<div class="row">
15-
<div class="col-lg-9 col-xl-8 pe-lg-4 pe-xl-0">
16+
<div class="col pe-lg-4 pe-xl-0">
1617
<h1 class="pb-2 pb-lg-3">Learning resources</h1>
17-
<p class="fs-lg mb-5">
18-
This section will be developed in future to provide resources for training and personal development pathways.
19-
</p>
18+
{% render_table table %}
2019
</div>
21-
<aside class="col-lg-3 offset-xl-1">
22-
<div class="offcanvas-lg offcanvas-end" id="sidebar">
23-
<div class="offcanvas-header">
24-
<h4 class="offcanvas-title">Sidebar</h4>
25-
<button class="btn-close ms-auto"
26-
type="button"
27-
data-bs-dismiss="offcanvas"
28-
data-bs-target="#sidebar"
29-
aria-label="Close"></button>
30-
</div>
31-
<div class="offcanvas-body">
32-
<h4 class="pt-1 pt-lg-0 mt-lg-n2"></h4>
33-
</div>
34-
</div>
35-
</aside>
3620
</div>
3721
</section>
3822
{% endblock content %}

main/views/page_views.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,16 @@
1111
from django.shortcuts import get_object_or_404
1212
from django.templatetags.static import static
1313
from django.views.generic.base import TemplateView
14+
from django_tables2 import SingleTableView
1415

15-
from ..models import CompetencyDomain, Skill, SkillLevel, ToolLanguageMethodology
16+
from ..models import (
17+
CompetencyDomain,
18+
LearningResource,
19+
Skill,
20+
SkillLevel,
21+
ToolLanguageMethodology,
22+
)
23+
from ..tables import LearningResourcesTable
1624

1725
logger = logging.getLogger(__name__)
1826

@@ -85,9 +93,11 @@ def get_context_data(self, **kwargs: Mapping[str, Any]) -> dict[str, Any]:
8593
return context
8694

8795

88-
class LearningResourcesPageView(TemplateView):
96+
class LearningResourcesPageView(SingleTableView):
8997
"""View that renders the page with all learning resources."""
9098

99+
model = LearningResource
100+
table_class = LearningResourcesTable
91101
template_name = "main/pages/learning-resources.html"
92102

93103

pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ dependencies = [
1717
"whitenoise",
1818
"mysqlclient",
1919
"django-import-export",
20-
"django-multiselectfield"
20+
"django-multiselectfield",
21+
"django-tables2"
2122
]
2223

2324
[project.optional-dependencies]
@@ -82,6 +83,10 @@ follow_untyped_imports = true
8283
module = ["django_registration.*"]
8384
follow_untyped_imports = true
8485

86+
[[tool.mypy.overrides]]
87+
module = ["django_tables2.*"]
88+
ignore_missing_imports = true
89+
8590
[[tool.mypy.overrides]]
8691
module = ["main.io_resources"]
8792
disallow_any_generics = false

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ django==6.0.3
2222
# django-multiselectfield
2323
# django-registration
2424
# django-stubs-ext
25+
# django-tables2
2526
django-bootstrap5==26.2
2627
# via direct_webapp (pyproject.toml)
2728
django-crispy-forms==2.6
@@ -36,6 +37,8 @@ django-registration==5.2.1
3637
# via direct_webapp (pyproject.toml)
3738
django-stubs-ext==6.0.2
3839
# via direct_webapp (pyproject.toml)
40+
django-tables2==3.0.0
41+
# via direct_webapp (pyproject.toml)
3942
gunicorn==25.3.0
4043
# via direct_webapp (pyproject.toml)
4144
mysqlclient==2.2.8

0 commit comments

Comments
 (0)