From a4840e3b8ee9307adbff02a110c6d2c1787b7b9f Mon Sep 17 00:00:00 2001 From: Hanne Moa Date: Thu, 14 Nov 2024 09:50:13 +0100 Subject: [PATCH] Wash list of theme names Make themes list depend on installed themes, not themes in settings. If they are out of sync it will be reported on startup with a chcek, and logged. --- README.rst | 5 +- src/argus_htmx/appconfig.py | 1 + src/argus_htmx/apps.py | 5 ++ src/argus_htmx/checks.py | 29 +++++++++ src/argus_htmx/context_processors.py | 10 +-- .../management/commands/tailwind_config.py | 4 +- src/argus_htmx/settings.py | 2 + src/argus_htmx/themes/utils.py | 61 ++++++++++++++++--- 8 files changed, 100 insertions(+), 17 deletions(-) create mode 100644 src/argus_htmx/checks.py diff --git a/README.rst b/README.rst index ccbb285b..af8aca80 100644 --- a/README.rst +++ b/README.rst @@ -242,7 +242,10 @@ How to customize the look: * `list of daisyUI color names`_ * `Tailwind CSS theme customization`_ -* Override the default main stylesheet path by providing a ``path_to_stylesheet`` value in a template ``context``. +* Override the default main stylesheet path by setting + ``ARGUS_STYLESHEET_PATH`` in the environment. The path is under + ``STATIC_URL``. This depends on the context processor + ``argus_htmx.context_processors.path_to_stylesheet``. * Include additional styles/stylesheets using the ``head`` block in your templates. * Generate a Tailwind config file by running the ``tailwind_config`` management command. By default the generated file will be based on diff --git a/src/argus_htmx/appconfig.py b/src/argus_htmx/appconfig.py index 62be90b2..534e6015 100644 --- a/src/argus_htmx/appconfig.py +++ b/src/argus_htmx/appconfig.py @@ -25,6 +25,7 @@ }, "context_processors": [ "argus.auth.context_processors.preferences", + "argus_htmx.context_processors.path_to_stylesheet", ], "middleware": { "argus_htmx.middleware.LoginRequiredMiddleware": "end", diff --git a/src/argus_htmx/apps.py b/src/argus_htmx/apps.py index cefe9aad..cc6169b5 100644 --- a/src/argus_htmx/apps.py +++ b/src/argus_htmx/apps.py @@ -1,4 +1,5 @@ import pathlib + from django.apps import AppConfig @@ -9,3 +10,7 @@ class HtmxFrontendConfig(AppConfig): def tailwind_css_files(self): yield from pathlib.Path(__file__).parent.glob("tailwindtheme/snippets/*.css") + + def ready(self): + # Register checks + from .checks import check_for_valid_themes_list # noqa: F401 diff --git a/src/argus_htmx/checks.py b/src/argus_htmx/checks.py new file mode 100644 index 00000000..6e55ad10 --- /dev/null +++ b/src/argus_htmx/checks.py @@ -0,0 +1,29 @@ +from django.core.checks import Error, Warning, register +from django.core.exceptions import ImproperlyConfigured + +from .themes.utils import get_theme_names, get_stylesheet_path + + +@register +def check_for_valid_themes_list(app_configs, **kwargs): + styles_path = get_stylesheet_path() + themes = [] + try: + themes = get_theme_names(quiet=False) + except ImproperlyConfigured as e: + return [ + Warning( + str(e), + hint=f"Regenerate {styles_path}", + id="argus_htmx.T001", + ) + ] + if not themes: + return [ + Error( + "no themes installed", + hint=f'Check the settings "DAISYUI_THEMES" and "TAILWIND_THEME_OVERRIDE" and regenerate {styles_path}', + id="argus_htmx.T002", + ) + ] + return [] diff --git a/src/argus_htmx/context_processors.py b/src/argus_htmx/context_processors.py index 67ccdd68..3f2b002e 100644 --- a/src/argus_htmx/context_processors.py +++ b/src/argus_htmx/context_processors.py @@ -7,12 +7,8 @@ See django settings for ``TEMPLATES``. """ -from argus.auth.models import Preferences +from .settings import STYLESHEET_PATH -def preferences(request): - pref_sets = Preferences.objects.filter(user=request.user) - prefdict = {} - for pref_set in pref_sets: - prefdict[pref_set._namespace] = pref_set.get_context() - return {"preferences": prefdict} +def path_to_stylesheet(request): + return {"path_to_stylesheet": STYLESHEET_PATH} diff --git a/src/argus_htmx/management/commands/tailwind_config.py b/src/argus_htmx/management/commands/tailwind_config.py index 0f4f68c1..a01d1408 100644 --- a/src/argus_htmx/management/commands/tailwind_config.py +++ b/src/argus_htmx/management/commands/tailwind_config.py @@ -8,7 +8,7 @@ from django.template.context import make_context from django.template.loader import get_template -from argus_htmx.themes.utils import get_themes +from argus_htmx.themes.utils import get_raw_themes_setting from argus_htmx import settings as argus_htmx_settings @@ -78,7 +78,7 @@ def get_context(self, target_dir: pathlib.Path): argus_htmx_settings.TAILWIND_THEME_OVERRIDE, ), "daisyuithemes": textwrap.indent( - json.dumps(get_themes(), indent=2), + json.dumps(get_raw_themes_setting(), indent=2), prefix=10 * " ", predicate=lambda line: line != "[\n", # this is kinda hacky, but eh ), diff --git a/src/argus_htmx/settings.py b/src/argus_htmx/settings.py index d72f9f81..00f14bb0 100644 --- a/src/argus_htmx/settings.py +++ b/src/argus_htmx/settings.py @@ -25,6 +25,7 @@ TAILWIND_CONFIG_TARGET = "src/argus_htmx/tailwindtheme/tailwind.config.js" TAILWIND_CSS_TARGET = "src/argus_htmx/tailwindtheme/styles.css" +STYLESHEET_PATH_DEFAULT = "styles.css" DEFAULT_THEMES = [ "dark", "light", @@ -57,3 +58,4 @@ THEME_DEFAULT = get_str_env("ARGUS_THEME_DEFAULT", "argus") DEFAULT_THEME_OVERRIDE = {} TAILWIND_THEME_OVERRIDE = get_json_env("TAILWIND_THEME_OVERRIDE", DEFAULT_THEME_OVERRIDE, quiet=True) +STYLESHEET_PATH = get_str_env("ARGUS_STYLESHEET_PATH", STYLESHEET_PATH_DEFAULT) diff --git a/src/argus_htmx/themes/utils.py b/src/argus_htmx/themes/utils.py index 9927bb5a..2cecbdf2 100644 --- a/src/argus_htmx/themes/utils.py +++ b/src/argus_htmx/themes/utils.py @@ -1,15 +1,32 @@ +import logging +from pathlib import Path +from re import findall + from django.conf import settings -from argus_htmx import settings as argus_htmx_settings +from django.core.exceptions import ImproperlyConfigured +from django.contrib.staticfiles.finders import find + +from argus_htmx import settings as fallbacks + + +__all__ = [ + "get_raw_themes_setting", + "get_theme_names", + "get_theme_default", +] + +LOG = logging.getLogger(__name__) -def get_themes(): - return getattr(settings, "DAISYUI_THEMES", argus_htmx_settings.DAISYUI_THEMES) +def get_raw_themes_setting(): + return getattr(settings, "DAISYUI_THEMES", fallbacks.DAISYUI_THEMES) -def get_theme_names(): - themes = get_themes() + +def get_themes_from_setting(): + themes_setting = get_raw_themes_setting() theme_names = [] - for theme in themes: + for theme in themes_setting: if isinstance(theme, str): theme_names.append(theme) elif isinstance(theme, dict): @@ -17,5 +34,35 @@ def get_theme_names(): return theme_names +def get_stylesheet_path(): + return getattr(settings, "STYLESHEET_PATH", fallbacks.STYLESHEET_PATH) + + +def get_themes_from_css(): + THEME_NAME_RE = "(?P[-_\w]+)" + DATA_THEME_RE = f"\[data-theme={THEME_NAME_RE}\]" + + absolute_stylesheet_path = Path(find(get_stylesheet_path())) + styles_css = absolute_stylesheet_path.read_text() + + return findall(DATA_THEME_RE, styles_css) + + +def get_theme_names(quiet=True): + ERROR_MSG = "Themes in settings are out of sync with themes installed" + + themes_from_setting = set(get_themes_from_setting()) + themes_from_css = set(get_themes_from_css()) + installed_themes = themes_from_setting & themes_from_css + + all_themes = themes_from_setting | themes_from_css + if all_themes != installed_themes: + LOG.warning(ERROR_MSG) + if not quiet: + raise ImproperlyConfigured(ERROR_MSG) + + return installed_themes + + def get_theme_default(): - return getattr(settings, "THEME_DEFAULT", argus_htmx_settings.THEME_DEFAULT) + return getattr(settings, "THEME_DEFAULT", fallbacks.THEME_DEFAULT)