diff --git a/main/tables.py b/main/tables.py
index 940d5662..0dae608a 100644
--- a/main/tables.py
+++ b/main/tables.py
@@ -3,17 +3,38 @@
from typing import TYPE_CHECKING
import django_tables2 as tables
+from django.db.models.query import QuerySet
from django.urls import reverse
from django.utils.html import format_html
from django.utils.safestring import SafeString, mark_safe
-from .models import LearningResource, Skill
+from .models import LearningResource, Skill, ToolLanguageMethodology
if TYPE_CHECKING: # pragma: no cover
from django.db.models.fields.related_descriptors import ManyRelatedManager
+external_link_html = (
+ '{}'
+)
+badge_html = '{}'
-class LearningResourcesTable(tables.Table):
+
+def _render_skills(qs: QuerySet[Skill]) -> SafeString:
+ """Helper function for rendering skills links as buttons in tables."""
+ return mark_safe(
+ " ".join(
+ format_html(
+ external_link_html,
+ reverse("skill_detail", args=(skill.slug,)),
+ "btn btn-outline-primary rounded-pill btn-sm",
+ skill.name,
+ )
+ for skill in qs
+ )
+ )
+
+
+class LearningResourceTable(tables.Table):
"""Table class for the LearningResources model."""
skill_set = tables.ManyToManyColumn(verbose_name="Skills")
@@ -27,10 +48,12 @@ class Meta:
def render_name(self, value: str, record: LearningResource) -> SafeString:
"""Include the URL in the name."""
- return format_html(
- '{}',
- record.url,
- value,
+ return format_html(external_link_html, record.url, "fs-lg", value)
+
+ def render_language(self, value: str) -> SafeString:
+ """Render the language field as a badge."""
+ return mark_safe(
+ " ".join(format_html(badge_html, val) for val in value.split(","))
)
def render_provider(self, value: str, record: LearningResource) -> SafeString:
@@ -38,22 +61,33 @@ def render_provider(self, value: str, record: LearningResource) -> SafeString:
if record.provider is None or not record.provider.url:
return mark_safe(value)
- return format_html(
- '{}',
- record.provider.url,
- value,
- )
+ return format_html(external_link_html, record.provider.url, "", value)
def render_skill_set(self, value: "ManyRelatedManager[Skill]") -> SafeString:
- """Include the relevant skills as badges."""
- return mark_safe(
- "".join(
- format_html(
- '{}',
- reverse("skill_detail", args=(skill.slug,)),
- skill.name,
- )
- for skill in value.all()
- )
- )
+ """Include the relevant skills as button links."""
+ return _render_skills(value.all())
+
+
+class ToolLanguageMethodologyTable(tables.Table):
+ """Table class for the LearningResources model."""
+
+ skill_set = tables.ManyToManyColumn(verbose_name="Skills")
+
+ class Meta:
+ """Meta options for the LearningResourcesTable."""
+
+ model = ToolLanguageMethodology
+ fields = ("name", "kind")
+ order_by = "name"
+
+ def render_name(self, value: str, record: ToolLanguageMethodology) -> SafeString:
+ """Include the URL in the name."""
+ return format_html(external_link_html, record.url, "fs-lg", value)
+
+ def render_kind(self, value: str) -> SafeString:
+ """Render the kind field as a badge."""
+ return format_html(badge_html, value)
+
+ def render_skill_set(self, value: "ManyRelatedManager[Skill]") -> SafeString:
+ """Include the relevant skills as button links."""
+ return _render_skills(value.all())
diff --git a/main/templates/main/pages/tools-languages-methodologies.html b/main/templates/main/pages/tools-languages-methodologies.html
new file mode 100644
index 00000000..c83668e3
--- /dev/null
+++ b/main/templates/main/pages/tools-languages-methodologies.html
@@ -0,0 +1,22 @@
+{% extends "main/base.page.html" %}
+{% load static %}
+{% load render_table from django_tables2 %}
+{% block title %}
+ Digital Research Competencies Framework
+{% endblock title %}
+{% block breadcrumb_items %}
+
+ Framework
+
+ Tools, languages and methodologies
+{% endblock breadcrumb_items %}
+{% block content %}
+
+
+
+
Tools, languages and methodologies
+ {% render_table table %}
+
+
+
+{% endblock content %}
diff --git a/main/templates/main/snippets/navbar.html b/main/templates/main/snippets/navbar.html
index ba313b63..491c8484 100644
--- a/main/templates/main/snippets/navbar.html
+++ b/main/templates/main/snippets/navbar.html
@@ -115,7 +115,11 @@ Learning resources
- Roles & career pathways
+ Tools, languages and methodologies
+
+
+ Roles and career pathways
diff --git a/main/urls.py b/main/urls.py
index 9cadf697..476593a9 100644
--- a/main/urls.py
+++ b/main/urls.py
@@ -22,6 +22,11 @@
views.LearningResourcesPageView.as_view(),
name="learning_resources",
),
+ path(
+ "tools-languages-methodologies/",
+ views.ToolsLanguagesMethodologiesPageView.as_view(),
+ name="tools_languages_methodologies",
+ ),
path("roles/", views.RolesPageView.as_view(), name="roles"),
path("skills//", views.SkillPageView.as_view(), name="skill_detail"),
]
diff --git a/main/views/page_views.py b/main/views/page_views.py
index e3273a25..b224ec98 100644
--- a/main/views/page_views.py
+++ b/main/views/page_views.py
@@ -26,7 +26,7 @@
SkillLevel,
ToolLanguageMethodology,
)
-from ..tables import LearningResourcesTable
+from ..tables import LearningResourceTable, ToolLanguageMethodologyTable
logger = logging.getLogger(__name__)
@@ -103,10 +103,18 @@ class LearningResourcesPageView(SingleTableView):
"""View that renders the page with all learning resources."""
model = LearningResource
- table_class = LearningResourcesTable
+ table_class = LearningResourceTable
template_name = "main/pages/learning-resources.html"
+class ToolsLanguagesMethodologiesPageView(SingleTableView):
+ """View that renders the page with all tools, languages and methodologies."""
+
+ model = ToolLanguageMethodology
+ table_class = ToolLanguageMethodologyTable
+ template_name = "main/pages/tools-languages-methodologies.html"
+
+
class GetInvolvedPageView(TemplateView):
"""View that renders the get involved page."""
diff --git a/tests/main/test_main_views.py b/tests/main/test_main_views.py
index 8082cdaa..cf79baf0 100644
--- a/tests/main/test_main_views.py
+++ b/tests/main/test_main_views.py
@@ -78,7 +78,12 @@ def test_navbar_contents(self, soup, auth_soup, admin_soup):
href=reverse("learning_resources"),
)
assert framework_dropdown.find(
- tag_with_text_filter("a", "Roles & career pathways"), href=reverse("roles")
+ tag_with_text_filter("a", "Tools, languages and methodologies"),
+ href=reverse("tools_languages_methodologies"),
+ )
+ assert framework_dropdown.find(
+ tag_with_text_filter("a", "Roles and career pathways"),
+ href=reverse("roles"),
)
# Community Dropdown
@@ -415,13 +420,13 @@ def test_page_content(self, learning_resource, skill, soup):
assert tr.find(
tag_with_text_filter("a", "Learning Resource"), href=learning_resource.url
)
- assert tr.find(tag_with_text_filter("td", "English"))
+ assert tr.find(tag_with_text_filter("span", "English"), class_="badge")
assert tr.find(
tag_with_text_filter("a", "Provider"), href=learning_resource.provider.url
)
assert tr.find(
tag_with_text_filter("a", "Skill"),
- class_="badge",
+ class_="btn",
href=reverse("skill_detail", args=(skill.slug,)),
)
diff --git a/tests/main/test_tables.py b/tests/main/test_tables.py
index f46480b4..93a648ba 100644
--- a/tests/main/test_tables.py
+++ b/tests/main/test_tables.py
@@ -3,27 +3,27 @@
import pytest
from django.urls import reverse
-from main.models import LearningResource, Provider, Skill
-from main.tables import LearningResourcesTable
+from main.models import LearningResource, Provider, Skill, ToolLanguageMethodology
+from main.tables import LearningResourceTable, ToolLanguageMethodologyTable
@pytest.mark.django_db
def test_learning_resources_table_render_name(learning_resource: LearningResource):
"""Test the learning resource name renders as an external link."""
- table = LearningResourcesTable([])
+ table = LearningResourceTable([])
rendered = table.render_name(learning_resource.name, learning_resource)
assert str(rendered) == (
f'Learning Resource'
+ 'rel="noopener noreferrer" class="fs-lg">Learning Resource'
)
@pytest.mark.django_db
def test_learning_resources_table_render_provider(learning_resource: LearningResource):
"""Test the provider name renders as an external link when a URL exists."""
- table = LearningResourcesTable([])
+ table = LearningResourceTable([])
assert isinstance(learning_resource.provider, Provider)
@@ -31,7 +31,7 @@ def test_learning_resources_table_render_provider(learning_resource: LearningRes
assert str(rendered) == (
f'Provider'
+ 'rel="noopener noreferrer" class="">Provider'
)
@@ -40,7 +40,7 @@ def test_learning_resources_table_render_provider_without_url(
learning_resource: LearningResource,
):
"""Test the provider name renders as plain text when no URL exists."""
- table = LearningResourcesTable([])
+ table = LearningResourceTable([])
learning_resource.provider = None
rendered = table.render_provider("Provider", learning_resource)
@@ -53,11 +53,53 @@ def test_learning_resources_table_render_skill_set(
learning_resource: LearningResource, skill: Skill
):
"""Test related skills render as badge links."""
- table = LearningResourcesTable([])
+ table = LearningResourceTable([])
rendered = table.render_skill_set(learning_resource.skill_set) # type: ignore[arg-type]
assert str(rendered) == (
'Skill'
+ 'class="btn btn-outline-primary rounded-pill btn-sm">Skill'
+ ).format(reverse("skill_detail", args=(skill.slug,)))
+
+
+@pytest.mark.django_db
+def test_tool_language_methodology_table_render_name(
+ tool: ToolLanguageMethodology,
+):
+ """Test the tool name renders as an external link."""
+ table = ToolLanguageMethodologyTable([])
+
+ rendered = table.render_name(tool.name, tool)
+
+ assert str(rendered) == (
+ f'Tool'
+ )
+
+
+@pytest.mark.django_db
+def test_tool_language_methodology_table_render_kind(
+ tool: ToolLanguageMethodology,
+):
+ """Test the tool kind renders as a badge."""
+ table = ToolLanguageMethodologyTable([])
+
+ rendered = table.render_kind(tool.kind)
+
+ assert str(rendered) == 'tool'
+
+
+@pytest.mark.django_db
+def test_tool_language_methodology_table_render_skill_set(
+ tool: ToolLanguageMethodology, skill: Skill
+):
+ """Test related skills render as badge links."""
+ table = ToolLanguageMethodologyTable([])
+
+ rendered = table.render_skill_set(tool.skill_set) # type: ignore[arg-type]
+
+ assert str(rendered) == (
+ 'Skill'
).format(reverse("skill_detail", args=(skill.slug,)))