diff --git a/articles/compressor.py b/articles/compressor.py new file mode 100644 index 0000000..ed3a014 --- /dev/null +++ b/articles/compressor.py @@ -0,0 +1,37 @@ +import itertools + +from django.conf import settings + + +class DummyArticleWithCode: + has_code = True + + +class DummyArticleNoCode: + has_code = False + + +class DummyNonAuthenticatedUser: + is_authenticated = False + + +class DummyAuthenticatedUser: + is_authenticated = True + + +def offline_context(): + article_possibilities = [None, DummyArticleWithCode(), DummyArticleNoCode()] + user_possibilities = [DummyAuthenticatedUser(), DummyNonAuthenticatedUser()] + goatcounter_possibilities = [None, settings.GOATCOUNTER_DOMAIN] + all_possibilities = [ + article_possibilities, + user_possibilities, + goatcounter_possibilities, + ] + for _tuple in itertools.product(*all_possibilities): + yield { + "STATIC_URL": settings.STATIC_URL, + "article": _tuple[0], + "user": _tuple[1], + "goatcounter_domain": _tuple[2], + } diff --git a/articles/models.py b/articles/models.py index 22a97c1..c98d700 100644 --- a/articles/models.py +++ b/articles/models.py @@ -1,7 +1,8 @@ import random import uuid -from functools import cached_property, reduce +from functools import cached_property +import rcssmin import readtime from django.contrib.auth.models import AbstractUser from django.contrib.contenttypes.models import ContentType @@ -121,6 +122,10 @@ class Article(models.Model): filter(None, map(lambda k: k.strip().lower(), self.keywords.split(","))) ) + @cached_property + def get_minified_custom_css(self): + return rcssmin.cssmin(self.custom_css) + def get_admin_url(self): content_type = ContentType.objects.get_for_model(self.__class__) return reverse( diff --git a/articles/templates/articles/article_detail.html b/articles/templates/articles/article_detail.html index 9c5f076..db14712 100644 --- a/articles/templates/articles/article_detail.html +++ b/articles/templates/articles/article_detail.html @@ -1,9 +1,7 @@ {% extends 'articles/base.html' %} {% block append_css %} - + {% endblock %} {% block title %}{{ article.title }} | {% endblock %} diff --git a/articles/templates/articles/article_list.html b/articles/templates/articles/article_list.html index 2f1d137..b489771 100644 --- a/articles/templates/articles/article_list.html +++ b/articles/templates/articles/article_list.html @@ -1,15 +1,8 @@ {% extends 'articles/base.html' %} {% block append_css %} - - + + {% endblock %} {% block title %}{% endblock %} diff --git a/articles/templates/articles/base.html b/articles/templates/articles/base.html index 2d0bd1e..6b49484 100644 --- a/articles/templates/articles/base.html +++ b/articles/templates/articles/base.html @@ -22,15 +22,10 @@ {% if user.is_authenticated %} {% endif %} - {% block append_css %} - {% endblock %} {% endcompress %} - {% compress js inline %} - {% if user.is_authenticated %} - - {% endif %} - {% endcompress %} + {% block append_css %} + {% endblock %} {% include "articles/snippets/favicon.html" %} @@ -60,7 +55,14 @@ for ongoing builds here.

+ {% include "articles/snippets/analytics.html" %} +{% compress js inline %} + {% if user.is_authenticated %} + + {% endif %} +{% endcompress %} + {% endspaceless %} diff --git a/articles/tests/conftest.py b/articles/tests/conftest.py index a0348aa..f79088a 100644 --- a/articles/tests/conftest.py +++ b/articles/tests/conftest.py @@ -1,6 +1,7 @@ import uuid import pytest +from django.core.management import call_command from django.utils import timezone from articles.models import Article, User @@ -43,3 +44,14 @@ def unpublished_article(author: User) -> Article: content="## some draft article markdown\n\n[a draft article link](https://article.com)", draft_key=uuid.uuid4(), ) + + +@pytest.fixture(autouse=True) +def enable_compressor(settings): + settings.COMPRESS_ENABLED = True + + +@pytest.fixture(autouse=True, scope="session") +def collect_static(): + call_command("collectstatic", "--no-input", "--clear") + call_command("compress", "--force") diff --git a/articles/tests/test_models.py b/articles/tests/test_models.py index 7a80f96..2bdd268 100644 --- a/articles/tests/test_models.py +++ b/articles/tests/test_models.py @@ -39,3 +39,41 @@ def test_save_article_doesnt_change_existing_slug(published_article: Article): published_article.title = "This is a brand new title" published_article.save() assert published_article.slug == original_slug + + +@pytest.mark.django_db +def test_empty_custom_css_minified(published_article): + published_article.custom_css = "" + assert published_article.get_minified_custom_css == "" + + +@pytest.mark.django_db +def test_simple_custom_css_minified(published_article): + published_article.custom_css = ".cls {\n background-color: red;\n}" + assert published_article.get_minified_custom_css == ".cls{background-color:red}" + + +@pytest.mark.django_db +def test_larger_custom_css_minified(published_article): + published_article.custom_css = """\ +.profile { + display: flex; + justify-content: space-evenly; + flex-wrap: wrap; +} + +.profile img { + max-width: 200px; + min-width: 100px; + max-height: 200px; + min-height: 100px; + border-radius: 10%; + padding: 1rem; + flex-shrink: 1; + flex-grow: 0; + padding: 0; +}""" + assert ( + published_article.get_minified_custom_css + == ".profile{display:flex;justify-content:space-evenly;flex-wrap:wrap}.profile img{max-width:200px;min-width:100px;max-height:200px;min-height:100px;border-radius:10%;padding:1rem;flex-shrink:1;flex-grow:0;padding:0}" + ) diff --git a/blog/settings.py b/blog/settings.py index 8b2e90e..7fc3965 100644 --- a/blog/settings.py +++ b/blog/settings.py @@ -117,6 +117,13 @@ if MEMCACHED_LOCATION: "LOCATION": MEMCACHED_LOCATION, } } +else: + CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.db.DatabaseCache", + "LOCATION": "cache", + } + } # Database # https://docs.djangoproject.com/en/3.1/ref/settings/#databases @@ -203,10 +210,7 @@ GOATCOUNTER_DOMAIN = os.getenv("GOATCOUNTER_DOMAIN") LOGIN_URL = "admin:login" -# ASSETS_AUTO_BUILD = DEBUG -# ASSETS_DEBUG = False - -COMPRESS_ENABLED = True +# COMPRESS_ENABLED = True # Enable this if you want to force compression during dev COMPRESS_FILTERS = { "css": [ "compressor.filters.css_default.CssAbsoluteFilter", @@ -218,3 +222,6 @@ COMPRESS_FILTERS = { } if DEBUG: COMPRESS_DEBUG_TOGGLE = "nocompress" + +COMPRESS_OFFLINE = True +COMPRESS_OFFLINE_CONTEXT = "articles.compressor.offline_context" diff --git a/docker/run.sh b/docker/run.sh index 5d188a6..1c6db9e 100755 --- a/docker/run.sh +++ b/docker/run.sh @@ -2,4 +2,5 @@ set -eux yes yes | python manage.py migrate python manage.py collectstatic --noinput --clear +python manage.py compress gunicorn blog.wsgi -b 0.0.0.0:8000 --log-file -