diff options
Diffstat (limited to 'pydis_site')
31 files changed, 328 insertions, 324 deletions
diff --git a/pydis_site/apps/guides/__init__.py b/pydis_site/apps/content/__init__.py index e69de29b..e69de29b 100644 --- a/pydis_site/apps/guides/__init__.py +++ b/pydis_site/apps/content/__init__.py diff --git a/pydis_site/apps/content/apps.py b/pydis_site/apps/content/apps.py new file mode 100644 index 00000000..1e300a48 --- /dev/null +++ b/pydis_site/apps/content/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class ContentConfig(AppConfig): +    """Django AppConfig for content app.""" + +    name = 'content' diff --git a/pydis_site/apps/guides/migrations/__init__.py b/pydis_site/apps/content/migrations/__init__.py index e69de29b..e69de29b 100644 --- a/pydis_site/apps/guides/migrations/__init__.py +++ b/pydis_site/apps/content/migrations/__init__.py diff --git a/pydis_site/apps/content/resources/content/guides/_info.yml b/pydis_site/apps/content/resources/content/guides/_info.yml new file mode 100644 index 00000000..8a38271d --- /dev/null +++ b/pydis_site/apps/content/resources/content/guides/_info.yml @@ -0,0 +1,2 @@ +name: Guides +description: Python and PyDis guides.
\ No newline at end of file diff --git a/pydis_site/apps/guides/resources/guides/how-to-write-a-guide.md b/pydis_site/apps/content/resources/content/guides/how-to-write-a-guide.md index 072c2538..072c2538 100644 --- a/pydis_site/apps/guides/resources/guides/how-to-write-a-guide.md +++ b/pydis_site/apps/content/resources/content/guides/how-to-write-a-guide.md diff --git a/pydis_site/apps/guides/tests/__init__.py b/pydis_site/apps/content/tests/__init__.py index e69de29b..e69de29b 100644 --- a/pydis_site/apps/guides/tests/__init__.py +++ b/pydis_site/apps/content/tests/__init__.py diff --git a/pydis_site/apps/guides/tests/test_guides/category/_info.yml b/pydis_site/apps/content/tests/test_content/category/_info.yml index 8311509d..8311509d 100644 --- a/pydis_site/apps/guides/tests/test_guides/category/_info.yml +++ b/pydis_site/apps/content/tests/test_content/category/_info.yml diff --git a/pydis_site/apps/guides/tests/test_guides/category/test3.md b/pydis_site/apps/content/tests/test_content/category/test3.md index bdde6188..bdde6188 100644 --- a/pydis_site/apps/guides/tests/test_guides/category/test3.md +++ b/pydis_site/apps/content/tests/test_content/category/test3.md diff --git a/pydis_site/apps/guides/tests/test_guides/test.md b/pydis_site/apps/content/tests/test_content/test.md index 7a917899..7a917899 100644 --- a/pydis_site/apps/guides/tests/test_guides/test.md +++ b/pydis_site/apps/content/tests/test_content/test.md diff --git a/pydis_site/apps/guides/tests/test_guides/test2.md b/pydis_site/apps/content/tests/test_content/test2.md index f0852356..f0852356 100644 --- a/pydis_site/apps/guides/tests/test_guides/test2.md +++ b/pydis_site/apps/content/tests/test_content/test2.md diff --git a/pydis_site/apps/content/tests/test_utils.py b/pydis_site/apps/content/tests/test_utils.py new file mode 100644 index 00000000..82e1ac5f --- /dev/null +++ b/pydis_site/apps/content/tests/test_utils.py @@ -0,0 +1,122 @@ +import os +from unittest.mock import patch + +from django.conf import settings +from django.http import Http404 +from django.test import TestCase +from markdown import Markdown + +from pydis_site.apps.content import utils + +BASE_PATH = os.path.join(settings.BASE_DIR, "pydis_site", "apps", "content", "tests", "test_content") + + +class TestGetBasePath(TestCase): +    def test_get_base_path(self): +        """Test does function return content base path.""" +        self.assertEqual( +            utils._get_base_path(), +            os.path.join(settings.BASE_DIR, "pydis_site", "apps", "content", "resources", "content") +        ) + + +class TestGetCategory(TestCase): +    def test_get_category_successfully(self): +        """Check does this get right data from category data file.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            result = utils.get_category("category") + +        self.assertEqual(result, {"name": "My Category", "description": "My Description"}) + +    def test_get_category_not_exists(self): +        """Check does this raise 404 error when category don't exists.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            with self.assertRaises(Http404): +                utils.get_category("invalid") + +    def test_get_category_not_directory(self): +        """Check does this raise 404 error when category isn't directory.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            with self.assertRaises(Http404): +                utils.get_category("test.md") + + +class TestGetCategories(TestCase): +    def test_get_categories(self): +        """Check does this return test content categories.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            result = utils.get_categories() + +        self.assertEqual(result, {"category": {"name": "My Category", "description": "My Description"}}) + + +class TestGetArticles(TestCase): +    def test_get_all_root_articles(self): +        """Check does this return all root level testing content.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            result = utils.get_articles() + +        for case in ["test", "test2"]: +            with self.subTest(guide=case): +                md = Markdown(extensions=['meta']) +                with open(os.path.join(BASE_PATH, f"{case}.md")) as f: +                    md.convert(f.read()) + +                self.assertIn(case, result) +                self.assertEqual(md.Meta, result[case]) + +    def test_get_all_category_articles(self): +        """Check does this return all category testing content.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            result = utils.get_articles("category") + +        md = Markdown(extensions=['meta']) +        with open(os.path.join(BASE_PATH, "category", "test3.md")) as f: +            md.convert(f.read()) + +        self.assertIn("test3", result) +        self.assertEqual(md.Meta, result["test3"]) + + +class TestGetArticle(TestCase): +    def test_get_root_article_success(self): +        """Check does this return article HTML and metadata when root article exist.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            result = utils.get_article("test", None) + +        md = Markdown(extensions=['meta', 'attr_list', 'fenced_code']) + +        with open(os.path.join(BASE_PATH, "test.md")) as f: +            html = md.convert(f.read()) + +        self.assertEqual(result, {"article": html, "metadata": md.Meta}) + +    def test_get_root_article_dont_exist(self): +        """Check does this raise Http404 when root article don't exist.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            with self.assertRaises(Http404): +                utils.get_article("invalid", None) + +    def test_get_category_article_success(self): +        """Check does this return article HTML and metadata when category guide exist.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            result = utils.get_article("test3", "category") + +        md = Markdown(extensions=['meta', 'attr_list', 'fenced_code']) + +        with open(os.path.join(BASE_PATH, "category", "test3.md")) as f: +            html = md.convert(f.read()) + +        self.assertEqual(result, {"article": html, "metadata": md.Meta}) + +    def test_get_category_article_dont_exist(self): +        """Check does this raise Http404 when category article don't exist.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            with self.assertRaises(Http404): +                utils.get_article("invalid", "category") + +    def test_get_category_article_category_dont_exist(self): +        """Check does this raise Http404 when category don't exist.""" +        with patch("pydis_site.apps.content.utils._get_base_path", return_value=BASE_PATH): +            with self.assertRaises(Http404): +                utils.get_article("some-guide", "invalid") diff --git a/pydis_site/apps/content/tests/test_views.py b/pydis_site/apps/content/tests/test_views.py new file mode 100644 index 00000000..98054534 --- /dev/null +++ b/pydis_site/apps/content/tests/test_views.py @@ -0,0 +1,104 @@ +from unittest.mock import patch + +from django.http import Http404 +from django.test import TestCase +from django_hosts.resolvers import reverse + + +class TestGuidesIndexView(TestCase): +    @patch("pydis_site.apps.content.views.articles.get_articles") +    @patch("pydis_site.apps.content.views.articles.get_categories") +    def test_articles_index_return_200(self, get_categories_mock, get_articles_mock): +        """Check that content index return HTTP code 200.""" +        get_categories_mock.return_value = {} +        get_articles_mock.return_value = {} + +        url = reverse('content:content') +        response = self.client.get(url) +        self.assertEqual(response.status_code, 200) +        get_articles_mock.assert_called_once() +        get_categories_mock.assert_called_once() + + +class TestGuideView(TestCase): +    @patch("pydis_site.apps.content.views.article.os.path.getmtime") +    @patch("pydis_site.apps.content.views.article.get_article") +    @patch("pydis_site.apps.content.views.article.get_category") +    def test_guide_return_code_200(self, get_category_mock, get_article_mock, get_time_mock): +        get_article_mock.return_value = {"guide": "test", "metadata": {}} + +        url = reverse("content:article", args=["test-guide"]) +        response = self.client.get(url) +        self.assertEqual(response.status_code, 200) +        get_category_mock.assert_not_called() +        get_article_mock.assert_called_once_with("test-guide", None) + +    @patch("pydis_site.apps.content.views.article.os.path.getmtime") +    @patch("pydis_site.apps.content.views.article.get_article") +    @patch("pydis_site.apps.content.views.article.get_category") +    def test_guide_return_404(self, get_category_mock, get_article_mock, get_time_mock): +        """Check that return code is 404 when invalid article provided.""" +        get_article_mock.side_effect = Http404("Article not found.") + +        url = reverse("content:article", args=["invalid-guide"]) +        response = self.client.get(url) +        self.assertEqual(response.status_code, 404) +        get_article_mock.assert_called_once_with("invalid-guide", None) +        get_category_mock.assert_not_called() + + +class TestCategoryView(TestCase): +    @patch("pydis_site.apps.content.views.category.get_category") +    @patch("pydis_site.apps.content.views.category.get_articles") +    def test_valid_category_code_200(self, get_articles_mock, get_category_mock): +        """Check that return code is 200 when visiting valid category.""" +        get_category_mock.return_value = {"name": "test", "description": "test"} +        get_articles_mock.return_value = {} + +        url = reverse("content:category", args=["category"]) +        response = self.client.get(url) + +        self.assertEqual(response.status_code, 200) +        get_articles_mock.assert_called_once_with("category") +        get_category_mock.assert_called_once_with("category") + +    @patch("pydis_site.apps.content.views.category.get_category") +    @patch("pydis_site.apps.content.views.category.get_articles") +    def test_invalid_category_code_404(self, get_articles_mock, get_category_mock): +        """Check that return code is 404 when trying to visit invalid category.""" +        get_category_mock.side_effect = Http404("Category not found.") + +        url = reverse("content:category", args=["invalid-category"]) +        response = self.client.get(url) + +        self.assertEqual(response.status_code, 404) +        get_category_mock.assert_called_once_with("invalid-category") +        get_articles_mock.assert_not_called() + + +class TestCategoryGuidesView(TestCase): +    @patch("pydis_site.apps.content.views.article.os.path.getmtime") +    @patch("pydis_site.apps.content.views.article.get_article") +    @patch("pydis_site.apps.content.views.article.get_category") +    def test_valid_category_article_code_200(self, get_category_mock, get_article_mock, get_time_mock): +        """Check that return code is 200 when visiting valid category article.""" +        get_article_mock.return_value = {"guide": "test", "metadata": {}} + +        url = reverse("content:category_article", args=["category", "test3"]) +        response = self.client.get(url) +        self.assertEqual(response.status_code, 200) +        get_article_mock.assert_called_once_with("test3", "category") +        get_category_mock.assert_called_once_with("category") + +    @patch("pydis_site.apps.content.views.article.os.path.getmtime") +    @patch("pydis_site.apps.content.views.article.get_article") +    @patch("pydis_site.apps.content.views.article.get_category") +    def test_invalid_category_article_code_404(self, get_category_mock, get_article_mock, get_time_mock): +        """Check that return code is 200 when trying to visit invalid category article.""" +        get_article_mock.side_effect = Http404("Article not found.") + +        url = reverse("content:category_article", args=["category", "invalid"]) +        response = self.client.get(url) +        self.assertEqual(response.status_code, 404) +        get_article_mock.assert_called_once_with("invalid", "category") +        get_category_mock.assert_not_called() diff --git a/pydis_site/apps/content/urls.py b/pydis_site/apps/content/urls.py new file mode 100644 index 00000000..ae9b1e57 --- /dev/null +++ b/pydis_site/apps/content/urls.py @@ -0,0 +1,11 @@ +from django.urls import path + +from . import views + +app_name = "content" +urlpatterns = [ +    path("", views.ArticlesView.as_view(), name='content'), +    path("category/<str:category>/", views.CategoryView.as_view(), name='category'), +    path("category/<str:category>/<str:article>/", views.ArticleView.as_view(), name='category_article'), +    path("<str:article>/", views.ArticleView.as_view(), name='article') +] diff --git a/pydis_site/apps/guides/utils.py b/pydis_site/apps/content/utils.py index c6f668f7..57905a69 100644 --- a/pydis_site/apps/guides/utils.py +++ b/pydis_site/apps/content/utils.py @@ -9,7 +9,7 @@ from markdown import Markdown  def _get_base_path() -> str:      """Have extra function for base path getting for testability.""" -    return os.path.join(settings.BASE_DIR, "pydis_site", "apps", "guides", "resources", "guides") +    return os.path.join(settings.BASE_DIR, "pydis_site", "apps", "content", "resources", "content")  def get_category(category: str) -> Dict[str, str]: @@ -34,14 +34,14 @@ def get_categories() -> Dict[str, Dict]:      return categories -def get_guides(category: Optional[str] = None) -> Dict[str, Dict]: -    """Get all root guides when category is not specified. Otherwise get all this category guides.""" +def get_articles(category: Optional[str] = None) -> Dict[str, Dict]: +    """Get all root content when category is not specified. Otherwise get all this category content."""      if category is None:          base_dir = _get_base_path()      else:          base_dir = os.path.join(_get_base_path(), category) -    guides = {} +    articles = {}      for filename in os.listdir(base_dir):          full_path = os.path.join(base_dir, filename) @@ -50,13 +50,13 @@ def get_guides(category: Optional[str] = None) -> Dict[str, Dict]:              with open(full_path) as f:                  md.convert(f.read()) -            guides[os.path.splitext(filename)[0]] = md.Meta +            articles[os.path.splitext(filename)[0]] = md.Meta -    return guides +    return articles -def get_guide(guide: str, category: Optional[str]) -> Dict[str, Union[str, Dict]]: -    """Get one specific guide. When category is specified, get it from there.""" +def get_article(article: str, category: Optional[str]) -> Dict[str, Union[str, Dict]]: +    """Get one specific article. When category is specified, get it from there."""      if category is None:          base_path = _get_base_path()      else: @@ -65,13 +65,13 @@ def get_guide(guide: str, category: Optional[str]) -> Dict[str, Union[str, Dict]          if not os.path.exists(base_path) or not os.path.isdir(base_path):              raise Http404("Category not found.") -    guide_path = os.path.join(base_path, f"{guide}.md") -    if not os.path.exists(guide_path) or not os.path.isfile(guide_path): -        raise Http404("Guide not found.") +    article_path = os.path.join(base_path, f"{article}.md") +    if not os.path.exists(article_path) or not os.path.isfile(article_path): +        raise Http404("Article not found.")      md = Markdown(extensions=['meta', 'attr_list', 'fenced_code']) -    with open(guide_path) as f: +    with open(article_path) as f:          html = md.convert(f.read()) -    return {"guide": html, "metadata": md.Meta} +    return {"article": html, "metadata": md.Meta} diff --git a/pydis_site/apps/content/views/__init__.py b/pydis_site/apps/content/views/__init__.py new file mode 100644 index 00000000..b50d487b --- /dev/null +++ b/pydis_site/apps/content/views/__init__.py @@ -0,0 +1,5 @@ +from .category import CategoryView +from .article import ArticleView +from .articles import ArticlesView + +__all__ = ["ArticleView", "ArticlesView", "CategoryView"] diff --git a/pydis_site/apps/guides/views/guide.py b/pydis_site/apps/content/views/article.py index bcd68bc4..b34ca3ee 100644 --- a/pydis_site/apps/guides/views/guide.py +++ b/pydis_site/apps/content/views/article.py @@ -8,22 +8,24 @@ from django.http import HttpResponse  from django.shortcuts import render  from django.views import View -from pydis_site.apps.guides.utils import get_category, get_guide +from pydis_site.apps.content.utils import get_category, get_article -class GuideView(View): +class ArticleView(View):      """Shows specific guide page.""" -    def get(self, request: WSGIRequest, guide: str, category: Optional[str] = None) -> HttpResponse: +    def get(self, request: WSGIRequest, article: str, category: Optional[str] = None) -> HttpResponse:          """Collect guide content and display it. When guide don't exist, return 404.""" -        guide_result = get_guide(guide, category) +        article_result = get_article(article, category)          if category is not None:              path = os.path.join( -                settings.BASE_DIR, "pydis_site", "apps", "guides", "resources", "guides", category, f"{guide}.md" +                settings.BASE_DIR, "pydis_site", "apps", "content", "resources", "content", category, f"{article}.md"              )          else: -            path = os.path.join(settings.BASE_DIR, "pydis_site", "apps", "guides", "resources", "guides", f"{guide}.md") +            path = os.path.join( +                settings.BASE_DIR, "pydis_site", "apps", "content", "resources", "content", f"{article}.md" +            )          if category is not None:              category_data = get_category(category) @@ -33,15 +35,15 @@ class GuideView(View):          return render(              request, -            "guides/guide.html", +            "content/article.html",              { -                "guide": guide_result, +                "article": article_result,                  "last_modified": datetime.fromtimestamp(os.path.getmtime(path)).strftime("%dth %B %Y"),                  "category_data": category_data,                  "relevant_links": {                      link: value for link, value in zip( -                        guide_result["metadata"].get("relevantlinks", []), -                        guide_result["metadata"].get("relevantlinkvalues", []) +                        article_result["metadata"].get("relevantlinks", []), +                        article_result["metadata"].get("relevantlinkvalues", [])                      )                  }              } diff --git a/pydis_site/apps/content/views/articles.py b/pydis_site/apps/content/views/articles.py new file mode 100644 index 00000000..ff945a19 --- /dev/null +++ b/pydis_site/apps/content/views/articles.py @@ -0,0 +1,14 @@ +from django.core.handlers.wsgi import WSGIRequest +from django.http import HttpResponse +from django.shortcuts import render +from django.views import View + +from pydis_site.apps.content.utils import get_categories, get_articles + + +class ArticlesView(View): +    """Shows all content and categories.""" + +    def get(self, request: WSGIRequest) -> HttpResponse: +        """Shows all content and categories.""" +        return render(request, "content/articles.html", {"content": get_articles(), "categories": get_categories()}) diff --git a/pydis_site/apps/content/views/category.py b/pydis_site/apps/content/views/category.py new file mode 100644 index 00000000..62e80a47 --- /dev/null +++ b/pydis_site/apps/content/views/category.py @@ -0,0 +1,18 @@ +from django.core.handlers.wsgi import WSGIRequest +from django.http import HttpResponse +from django.shortcuts import render +from django.views import View + +from pydis_site.apps.content.utils import get_category, get_articles + + +class CategoryView(View): +    """Handles content category page.""" + +    def get(self, request: WSGIRequest, category: str) -> HttpResponse: +        """Handles page that displays category content.""" +        return render( +            request, +            "content/category.html", +            {"category_info": get_category(category), "content": get_articles(category), "category_name": category} +        ) diff --git a/pydis_site/apps/guides/apps.py b/pydis_site/apps/guides/apps.py deleted file mode 100644 index 8dfa4f65..00000000 --- a/pydis_site/apps/guides/apps.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.apps import AppConfig - - -class GuidesConfig(AppConfig): -    """Django AppConfig for guides app.""" - -    name = 'guides' diff --git a/pydis_site/apps/guides/tests/test_utils.py b/pydis_site/apps/guides/tests/test_utils.py deleted file mode 100644 index e7448be6..00000000 --- a/pydis_site/apps/guides/tests/test_utils.py +++ /dev/null @@ -1,122 +0,0 @@ -import os -from unittest.mock import patch - -from django.conf import settings -from django.http import Http404 -from django.test import TestCase -from markdown import Markdown - -from pydis_site.apps.guides import utils - -BASE_PATH = os.path.join(settings.BASE_DIR, "pydis_site", "apps", "guides", "tests", "test_guides") - - -class TestGetBasePath(TestCase): -    def test_get_base_path(self): -        """Test does function return guides base path.""" -        self.assertEqual( -            utils._get_base_path(), -            os.path.join(settings.BASE_DIR, "pydis_site", "apps", "guides", "resources", "guides") -        ) - - -class TestGetCategory(TestCase): -    def test_get_category_successfully(self): -        """Check does this get right data from category data file.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            result = utils.get_category("category") - -        self.assertEqual(result, {"name": "My Category", "description": "My Description"}) - -    def test_get_category_not_exists(self): -        """Check does this raise 404 error when category don't exists.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            with self.assertRaises(Http404): -                utils.get_category("invalid") - -    def test_get_category_not_directory(self): -        """Check does this raise 404 error when category isn't directory.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            with self.assertRaises(Http404): -                utils.get_category("test.md") - - -class TestGetCategories(TestCase): -    def test_get_categories(self): -        """Check does this return test guides categories.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            result = utils.get_categories() - -        self.assertEqual(result, {"category": {"name": "My Category", "description": "My Description"}}) - - -class TestGetGuides(TestCase): -    def test_get_all_root_guides(self): -        """Check does this return all root level testing guides.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            result = utils.get_guides() - -        for case in ["test", "test2"]: -            with self.subTest(guide=case): -                md = Markdown(extensions=['meta']) -                with open(os.path.join(BASE_PATH, f"{case}.md")) as f: -                    md.convert(f.read()) - -                self.assertIn(case, result) -                self.assertEqual(md.Meta, result[case]) - -    def test_get_all_category_guides(self): -        """Check does this return all category testing guides.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            result = utils.get_guides("category") - -        md = Markdown(extensions=['meta']) -        with open(os.path.join(BASE_PATH, "category", "test3.md")) as f: -            md.convert(f.read()) - -        self.assertIn("test3", result) -        self.assertEqual(md.Meta, result["test3"]) - - -class TestGetGuide(TestCase): -    def test_get_root_guide_success(self): -        """Check does this return guide HTML and metadata when root guide exist.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            result = utils.get_guide("test", None) - -        md = Markdown(extensions=['meta', 'attr_list', 'fenced_code']) - -        with open(os.path.join(BASE_PATH, "test.md")) as f: -            html = md.convert(f.read()) - -        self.assertEqual(result, {"guide": html, "metadata": md.Meta}) - -    def test_get_root_guide_dont_exist(self): -        """Check does this raise Http404 when root guide don't exist.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            with self.assertRaises(Http404): -                result = utils.get_guide("invalid", None) - -    def test_get_category_guide_success(self): -        """Check does this return guide HTML and metadata when category guide exist.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            result = utils.get_guide("test3", "category") - -        md = Markdown(extensions=['meta', 'attr_list', 'fenced_code']) - -        with open(os.path.join(BASE_PATH, "category", "test3.md")) as f: -            html = md.convert(f.read()) - -        self.assertEqual(result, {"guide": html, "metadata": md.Meta}) - -    def test_get_category_guide_dont_exist(self): -        """Check does this raise Http404 when category guide don't exist.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            with self.assertRaises(Http404): -                result = utils.get_guide("invalid", "category") - -    def test_get_category_guide_category_dont_exist(self): -        """Check does this raise Http404 when category don't exist.""" -        with patch("pydis_site.apps.guides.utils._get_base_path", return_value=BASE_PATH): -            with self.assertRaises(Http404): -                result = utils.get_guide("some-guide", "invalid") diff --git a/pydis_site/apps/guides/tests/test_views.py b/pydis_site/apps/guides/tests/test_views.py deleted file mode 100644 index e3945136..00000000 --- a/pydis_site/apps/guides/tests/test_views.py +++ /dev/null @@ -1,104 +0,0 @@ -from unittest.mock import patch - -from django.http import Http404 -from django.test import TestCase -from django_hosts.resolvers import reverse - - -class TestGuidesIndexView(TestCase): -    @patch("pydis_site.apps.guides.views.guides.get_guides") -    @patch("pydis_site.apps.guides.views.guides.get_categories") -    def test_guides_index_return_200(self, get_categories_mock, get_guides_mock): -        """Check that guides index return HTTP code 200.""" -        get_categories_mock.return_value = {} -        get_guides_mock.return_value = {} - -        url = reverse('guide:guides') -        response = self.client.get(url) -        self.assertEqual(response.status_code, 200) -        get_guides_mock.assert_called_once() -        get_categories_mock.assert_called_once() - - -class TestGuideView(TestCase): -    @patch("pydis_site.apps.guides.views.guide.os.path.getmtime") -    @patch("pydis_site.apps.guides.views.guide.get_guide") -    @patch("pydis_site.apps.guides.views.guide.get_category") -    def test_guide_return_code_200(self, get_category_mock, get_guide_mock, get_time_mock): -        get_guide_mock.return_value = {"guide": "test", "metadata": {}} - -        url = reverse("guide:guide", args=["test-guide"]) -        response = self.client.get(url) -        self.assertEqual(response.status_code, 200) -        get_category_mock.assert_not_called() -        get_guide_mock.assert_called_once_with("test-guide", None) - -    @patch("pydis_site.apps.guides.views.guide.os.path.getmtime") -    @patch("pydis_site.apps.guides.views.guide.get_guide") -    @patch("pydis_site.apps.guides.views.guide.get_category") -    def test_guide_return_404(self, get_category_mock, get_guide_mock, get_time_mock): -        """Check that return code is 404 when invalid guide provided.""" -        get_guide_mock.side_effect = Http404("Guide not found.") - -        url = reverse("guide:guide", args=["invalid-guide"]) -        response = self.client.get(url) -        self.assertEqual(response.status_code, 404) -        get_guide_mock.assert_called_once_with("invalid-guide", None) -        get_category_mock.assert_not_called() - - -class TestCategoryView(TestCase): -    @patch("pydis_site.apps.guides.views.category.get_category") -    @patch("pydis_site.apps.guides.views.category.get_guides") -    def test_valid_category_code_200(self, get_guides_mock, get_category_mock): -        """Check that return code is 200 when visiting valid category.""" -        get_category_mock.return_value = {"name": "test", "description": "test"} -        get_guides_mock.return_value = {} - -        url = reverse("guide:category", args=["category"]) -        response = self.client.get(url) - -        self.assertEqual(response.status_code, 200) -        get_guides_mock.assert_called_once_with("category") -        get_category_mock.assert_called_once_with("category") - -    @patch("pydis_site.apps.guides.views.category.get_category") -    @patch("pydis_site.apps.guides.views.category.get_guides") -    def test_invalid_category_code_404(self, get_guides_mock, get_category_mock): -        """Check that return code is 404 when trying to visit invalid category.""" -        get_category_mock.side_effect = Http404("Category not found.") - -        url = reverse("guide:category", args=["invalid-category"]) -        response = self.client.get(url) - -        self.assertEqual(response.status_code, 404) -        get_category_mock.assert_called_once_with("invalid-category") -        get_guides_mock.assert_not_called() - - -class TestCategoryGuidesView(TestCase): -    @patch("pydis_site.apps.guides.views.guide.os.path.getmtime") -    @patch("pydis_site.apps.guides.views.guide.get_guide") -    @patch("pydis_site.apps.guides.views.guide.get_category") -    def test_valid_category_guide_code_200(self, get_category_mock, get_guide_mock, get_time_mock): -        """Check that return code is 200 when visiting valid category article.""" -        get_guide_mock.return_value = {"guide": "test", "metadata": {}} - -        url = reverse("guide:category_guide", args=["category", "test3"]) -        response = self.client.get(url) -        self.assertEqual(response.status_code, 200) -        get_guide_mock.assert_called_once_with("test3", "category") -        get_category_mock.assert_called_once_with("category") - -    @patch("pydis_site.apps.guides.views.guide.os.path.getmtime") -    @patch("pydis_site.apps.guides.views.guide.get_guide") -    @patch("pydis_site.apps.guides.views.guide.get_category") -    def test_invalid_category_guide_code_404(self, get_category_mock, get_guide_mock, get_time_mock): -        """Check that return code is 200 when trying to visit invalid category article.""" -        get_guide_mock.side_effect = Http404("Guide not found.") - -        url = reverse("guide:category_guide", args=["category", "invalid"]) -        response = self.client.get(url) -        self.assertEqual(response.status_code, 404) -        get_guide_mock.assert_called_once_with("invalid", "category") -        get_category_mock.assert_not_called() diff --git a/pydis_site/apps/guides/urls.py b/pydis_site/apps/guides/urls.py deleted file mode 100644 index 69641638..00000000 --- a/pydis_site/apps/guides/urls.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.urls import path - -from . import views - -app_name = "guides" -urlpatterns = [ -    path("", views.GuidesView.as_view(), name='guides'), -    path("category/<str:category>/", views.CategoryView.as_view(), name='category'), -    path("category/<str:category>/<str:guide>/", views.GuideView.as_view(), name='category_guide'), -    path("<str:guide>/", views.GuideView.as_view(), name='guide') -] diff --git a/pydis_site/apps/guides/views/__init__.py b/pydis_site/apps/guides/views/__init__.py deleted file mode 100644 index 17a244c1..00000000 --- a/pydis_site/apps/guides/views/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .category import CategoryView -from .guide import GuideView -from .guides import GuidesView - -__all__ = ["GuideView", "GuidesView", "CategoryView"] diff --git a/pydis_site/apps/guides/views/category.py b/pydis_site/apps/guides/views/category.py deleted file mode 100644 index 33e8c97b..00000000 --- a/pydis_site/apps/guides/views/category.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.core.handlers.wsgi import WSGIRequest -from django.http import HttpResponse -from django.shortcuts import render -from django.views import View - -from pydis_site.apps.guides.utils import get_category, get_guides - - -class CategoryView(View): -    """Handles guides category page.""" - -    def get(self, request: WSGIRequest, category: str) -> HttpResponse: -        """Handles page that displays category guides.""" -        return render( -            request, -            "guides/category.html", -            {"category_info": get_category(category), "guides": get_guides(category), "category_name": category} -        ) diff --git a/pydis_site/apps/guides/views/guides.py b/pydis_site/apps/guides/views/guides.py deleted file mode 100644 index bb8b565e..00000000 --- a/pydis_site/apps/guides/views/guides.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.core.handlers.wsgi import WSGIRequest -from django.http import HttpResponse -from django.shortcuts import render -from django.views import View - -from pydis_site.apps.guides.utils import get_categories, get_guides - - -class GuidesView(View): -    """Shows all guides and categories.""" - -    def get(self, request: WSGIRequest) -> HttpResponse: -        """Shows all guides and categories.""" -        return render(request, "guides/guides.html", {"guides": get_guides(), "categories": get_categories()}) diff --git a/pydis_site/apps/home/urls.py b/pydis_site/apps/home/urls.py index 06d62352..e49fd4e0 100644 --- a/pydis_site/apps/home/urls.py +++ b/pydis_site/apps/home/urls.py @@ -39,5 +39,5 @@ urlpatterns = [      path('admin/', admin.site.urls),      path('notifications/', include('django_nyt.urls')), -    path('guides/', include('pydis_site.apps.guides.urls', namespace='guide')), +    path('content/', include('pydis_site.apps.content.urls', namespace='content')),  ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/pydis_site/settings.py b/pydis_site/settings.py index 6aff0fc1..aaac3bfe 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -91,7 +91,7 @@ INSTALLED_APPS = [      'pydis_site.apps.api',      'pydis_site.apps.home',      'pydis_site.apps.staff', -    'pydis_site.apps.guides', +    'pydis_site.apps.content',      'django.contrib.admin',      'django.contrib.auth', diff --git a/pydis_site/static/css/guides/guide.css b/pydis_site/static/css/content/articles.css index fa7a0ba5..fa7a0ba5 100644 --- a/pydis_site/static/css/guides/guide.css +++ b/pydis_site/static/css/content/articles.css diff --git a/pydis_site/templates/guides/guide.html b/pydis_site/templates/content/article.html index 97fc8262..de6cd28d 100644 --- a/pydis_site/templates/guides/guide.html +++ b/pydis_site/templates/content/article.html @@ -3,10 +3,10 @@  {% block title %}{{ metadata.title|first }}{% endblock %}  {% block head %} -  <meta property="og:title" content="Python Discord - {{ guide.metadata.title|first }}" /> +  <meta property="og:title" content="Python Discord - {{ article.metadata.title|first }}" />    <meta property="og:type" content="website" /> -  <meta property="og:description" content="{{ guide.metadata.shortdescription|first }}" /> -  <link rel="stylesheet" href="{% static "css/guides/guide.css" %}"> +  <meta property="og:description" content="{{ article.metadata.shortdescription|first }}" /> +  <link rel="stylesheet" href="{% static "css/content/articles.css" %}">    <link rel="stylesheet" href="//cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/styles/default.min.css">    <script src="//cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/highlight.min.js"></script>    <script>hljs.initHighlightingOnLoad();</script> @@ -19,11 +19,11 @@          <div class="container">              <nav class="breadcrumb is-pulled-left" aria-label="breadcrumbs">                  <ul> -                    <li><a href="/guides">Guides</a></li> +                    <li><a href="/content">Pages</a></li>                      {% if category_data.raw_name is not None %} -                        <li><a href="/guides/category/{{ category_data.raw_name }}">{{ category_data.name }}</a></li> +                        <li><a href="/content/category/{{ category_data.raw_name }}">{{ category_data.name }}</a></li>                      {% endif %} -                    <li class="is-active"><a href="#">{{ guide.metadata.title|first }}</a></li> +                    <li class="is-active"><a href="#">{{ article.metadata.title|first }}</a></li>                  </ul>              </nav>          </div> @@ -32,13 +32,13 @@      <section class="section">          <div class="content">              <div class="container"> -                <h1 class="title">{{ guide.metadata.title|first }}</h1> +                <h1 class="title">{{ article.metadata.title|first }}</h1>                  <div class="columns is-variable is-8">                      <div class="column is-two-thirds"> -                        {{ guide.guide|safe }} +                        {{ article.article|safe }}                          <p class="has-text-weight-light is-size-7">                              <strong>Last modified:</strong> {{ last_modified }}<br/> -                            <strong>Contributors:</strong> {{ guide.metadata.contributors|join:", " }} +                            <strong>Contributors:</strong> {{ article.metadata.contributors|join:", " }}                          </p>                      </div>                      <div class="column"> diff --git a/pydis_site/templates/guides/guides.html b/pydis_site/templates/content/articles.html index 0e6f2073..6fea66e5 100644 --- a/pydis_site/templates/guides/guides.html +++ b/pydis_site/templates/content/articles.html @@ -3,7 +3,7 @@  {% block title %}Guides{% endblock %}  {% block head %} -  <link rel="stylesheet" href="{% static "css/guides/guide.css" %}"> +  <link rel="stylesheet" href="{% static "css/content/articles.css" %}">  {% endblock %}  {% block content %} @@ -13,7 +13,7 @@          <div class="container">              <nav class="breadcrumb is-pulled-left" aria-label="breadcrumbs">                  <ul> -                    <li class="is-active"><a href="/guides">Guides</a></li> +                    <li class="is-active"><a href="/content">Pages</a></li>                  </ul>              </nav>          </div> @@ -22,13 +22,13 @@      <section class="section">          <div class="container">              <div class="content"> -                <h1>Guides</h1> -                {% for guide, data in guides.items %} +                <h1>Articles</h1> +                {% for article, data in content.items %}                      <div class="box" style="max-width: 800px;">                          <span class="icon is-size-4 is-medium">                              <i class="fab fa-python is-size-3 is-black has-icon-padding" aria-hidden="true"></i>                          </span> -                        <a href="{{ guide }}/"> +                        <a href="{{ article }}/">                                  <span class="is-size-4 has-text-weight-bold">{{ data.title.0 }}</span>                          </a>                          <p class="is-italic">{{ data.shortdescription.0 }}</p> diff --git a/pydis_site/templates/guides/category.html b/pydis_site/templates/content/category.html index f3a8c2ce..61e20c43 100644 --- a/pydis_site/templates/guides/category.html +++ b/pydis_site/templates/content/category.html @@ -6,7 +6,7 @@    <meta property="og:title" content="Python Discord - {{ category_info.name }}" />    <meta property="og:type" content="website" />    <meta property="og:description" content="{{ category_info.description }}" /> -  <link rel="stylesheet" href="{% static "css/guides/guide.css" %}"> +  <link rel="stylesheet" href="{% static "css/content/articles.css" %}">  {% endblock %}  {% block content %} @@ -16,7 +16,7 @@          <div class="container">              <nav class="breadcrumb is-pulled-left" aria-label="breadcrumbs">                  <ul> -                    <li><a href="/guides">Guides</a></li> +                    <li><a href="/content">Pages</a></li>                      <li class="is-active"><a href="#">{{ category_info.name }}</a></li>                  </ul>              </nav> @@ -27,12 +27,12 @@          <div class="container">              <div class="content">                  <h1>{{ category_info.name }}</h1> -                {% for guide, data in guides.items %} +                {% for article, data in content.items %}                      <div class="box" style="max-width: 800px;">                          <span class="icon is-size-4 is-medium">                              <i class="fab fa-python is-size-3 is-black has-icon-padding" aria-hidden="true"></i>                          </span> -                        <a href="/guides/category/{{ category_name }}/{{ guide }}/"> +                        <a href="/content/category/{{ category_name }}/{{ article }}/">                                  <span class="is-size-4 has-text-weight-bold">{{ data.title.0 }}</span>                          </a>                          <p class="is-italic">{{ data.shortdescription.0 }}</p>  |