diff --git a/mandelstudio/idea_marketplace.py b/mandelstudio/idea_marketplace.py index 07972ac..ea56f71 100644 --- a/mandelstudio/idea_marketplace.py +++ b/mandelstudio/idea_marketplace.py @@ -211,7 +211,9 @@ def _get_attribute_text(product, code: str) -> str: def _set_attribute_text(product, attribute, text: str) -> None: ProductAttributeValue = get_model("catalogue", "ProductAttributeValue") value_field = ( - "value_richtext" if getattr(attribute, "type", "text") == "richtext" else "value_text" + "value_richtext" + if getattr(attribute, "type", "text") == "richtext" + else "value_text" ) value, _created = ProductAttributeValue.objects.get_or_create( product=product, @@ -240,7 +242,9 @@ def get_idea_full_description(product) -> str: def get_unlockable_description(product, user) -> tuple[str, bool]: unlocked = user_has_unlocked_idea(user, product) if unlocked: - return get_idea_full_description(product) or get_idea_short_description(product), True + return get_idea_full_description(product) or get_idea_short_description( + product + ), True return get_idea_short_description(product), False @@ -261,7 +265,9 @@ def user_has_unlocked_idea(user, product) -> bool: "delayed-payment", } paid_statuses = { - status.strip().lower() for status in paid_statuses if isinstance(status, str) and status.strip() + status.strip().lower() + for status in paid_statuses + if isinstance(status, str) and status.strip() } status_match = Line.objects.filter( @@ -398,7 +404,9 @@ def seed_idea_marketplace_products( ) if hasattr(Product, "STANDALONE") and hasattr(product, "structure"): product.structure = Product.STANDALONE - if hasattr(product, "is_public") and not getattr(product, "is_public", False): + if hasattr(product, "is_public") and not getattr( + product, "is_public", False + ): product.is_public = True product.save() created += 1 @@ -413,7 +421,9 @@ def seed_idea_marketplace_products( if hasattr(product, "slug") and product.slug != slugify(item.title): product.slug = slugify(item.title) dirty_fields.append("slug") - if hasattr(product, "is_public") and not getattr(product, "is_public", False): + if hasattr(product, "is_public") and not getattr( + product, "is_public", False + ): product.is_public = True dirty_fields.append("is_public") if dirty_fields: @@ -431,7 +441,9 @@ def seed_idea_marketplace_products( demo_filter = Q() for marker in DEMO_MARKERS: demo_filter |= Q(title__icontains=marker) | Q(slug__icontains=marker) - demo_queryset = Product.objects.filter(demo_filter).exclude(title__in=keep_titles) + demo_queryset = Product.objects.filter(demo_filter).exclude( + title__in=keep_titles + ) # Also purge any non-canonical products lingering in the Idea Product class # or explicitly grouped under the Digital Ideas category. non_canonical_ideas_queryset = ( diff --git a/mandelstudio/launch_validation.py b/mandelstudio/launch_validation.py index 94b7f22..d100ba0 100644 --- a/mandelstudio/launch_validation.py +++ b/mandelstudio/launch_validation.py @@ -33,7 +33,9 @@ def get_declared_plugins() -> list[str]: def get_declared_payment_apps(installed_apps: list[str] | None = None) -> list[str]: - declared_plugins = [plugin for plugin in get_declared_plugins() if "payment" in plugin.lower()] + declared_plugins = [ + plugin for plugin in get_declared_plugins() if "payment" in plugin.lower() + ] if declared_plugins: return declared_plugins installed_apps = installed_apps or list(settings.INSTALLED_APPS) diff --git a/mandelstudio/management/commands/prepare_idea_marketplace_launch.py b/mandelstudio/management/commands/prepare_idea_marketplace_launch.py index 9d68a2a..b7ae01e 100644 --- a/mandelstudio/management/commands/prepare_idea_marketplace_launch.py +++ b/mandelstudio/management/commands/prepare_idea_marketplace_launch.py @@ -115,9 +115,7 @@ def _update_homepage_stream(page) -> bool: return False page.body = StreamValue(page.body.stream_block, stream_data, is_lazy=True) - page.search_description = ( - "Idea marketplace with premium plans. Preview each strategy and unlock full implementation after purchase." - ) + page.search_description = "Idea marketplace with premium plans. Preview each strategy and unlock full implementation after purchase." page.save() return True @@ -131,7 +129,9 @@ def _purge_demo_pages() -> int: | Q(search_description__icontains=marker) ) candidate_ids = set( - Page.objects.exclude(depth__lte=2).filter(marker_filter).values_list("id", flat=True) + Page.objects.exclude(depth__lte=2) + .filter(marker_filter) + .values_list("id", flat=True) ) candidate_ids.update( Page.objects.exclude(depth__lte=2) diff --git a/mandelstudio/management/commands/purge_demo_data.py b/mandelstudio/management/commands/purge_demo_data.py index 2a47b0e..04f482e 100644 --- a/mandelstudio/management/commands/purge_demo_data.py +++ b/mandelstudio/management/commands/purge_demo_data.py @@ -73,7 +73,9 @@ class Command(BaseCommand): top_level_products = Product.objects.filter(parent__isnull=True) if keep_only_ideas: - products_to_delete = top_level_products.exclude(title__in=IDEA_PRODUCT_TITLES) + products_to_delete = top_level_products.exclude( + title__in=IDEA_PRODUCT_TITLES + ) else: products_to_delete = top_level_products.filter(product_filter).exclude( title__in=IDEA_PRODUCT_TITLES @@ -83,13 +85,17 @@ class Command(BaseCommand): Page.objects.live() .public() .filter(depth__gt=2) - .filter(Q(slug__in=DEMO_PAGE_SLUGS) | _build_demo_text_filter(("title", "slug"))) + .filter( + Q(slug__in=DEMO_PAGE_SLUGS) | _build_demo_text_filter(("title", "slug")) + ) ) product_preview = list(products_to_delete.values_list("id", "title")[:30]) page_preview = list(pages_to_delete.values_list("id", "slug", "title")[:30]) - self.stdout.write(f"Products matched for deletion: {products_to_delete.count()}") + self.stdout.write( + f"Products matched for deletion: {products_to_delete.count()}" + ) for item in product_preview: self.stdout.write(f" - product#{item[0]}: {item[1]}") if products_to_delete.count() > len(product_preview): @@ -102,7 +108,9 @@ class Command(BaseCommand): self.stdout.write(" - ...") if dry_run: - self.stdout.write(self.style.WARNING("Dry run completed. No data was deleted.")) + self.stdout.write( + self.style.WARNING("Dry run completed. No data was deleted.") + ) return deleted_products = products_to_delete.count() diff --git a/mandelstudio/management/commands/validate_idea_marketplace_launch.py b/mandelstudio/management/commands/validate_idea_marketplace_launch.py index b9da699..64b2fb5 100644 --- a/mandelstudio/management/commands/validate_idea_marketplace_launch.py +++ b/mandelstudio/management/commands/validate_idea_marketplace_launch.py @@ -47,8 +47,13 @@ class Command(BaseCommand): if config_path.exists(): with config_path.open("r", encoding="utf-8") as handle: config_payload = json.load(handle) - config_plugins = [str(plugin) for plugin in config_payload.get("ocyan_plugins", [])] - if any("demodata" in "".join(ch for ch in str(plugin).lower() if ch.isalnum()) for plugin in config_plugins): + config_plugins = [ + str(plugin) for plugin in config_payload.get("ocyan_plugins", []) + ] + if any( + "demodata" in "".join(ch for ch in str(plugin).lower() if ch.isalnum()) + for plugin in config_plugins + ): raise CommandError( "Demo data plugin detected in ocyan.json. Remove it before launch." ) @@ -70,7 +75,9 @@ class Command(BaseCommand): if currency != "EUR": raise CommandError(f"OSCAR_DEFAULT_CURRENCY must be EUR, got '{currency}'.") - product_class = ProductClass.objects.filter(name=IDEA_PRODUCT_CLASS_NAME).first() + product_class = ProductClass.objects.filter( + name=IDEA_PRODUCT_CLASS_NAME + ).first() if product_class is None: raise CommandError(f"Missing ProductClass '{IDEA_PRODUCT_CLASS_NAME}'.") if product_class.requires_shipping: @@ -93,12 +100,14 @@ class Command(BaseCommand): found_titles = set(found_products.values_list("title", flat=True)) missing_titles = sorted(expected_titles - found_titles) if missing_titles: - raise CommandError(f"Missing seeded idea products: {', '.join(missing_titles)}.") + raise CommandError( + f"Missing seeded idea products: {', '.join(missing_titles)}." + ) non_public_idea_titles = list( - found_products.filter(title__in=expected_titles, is_public=False).values_list( - "title", flat=True - ) + found_products.filter( + title__in=expected_titles, is_public=False + ).values_list("title", flat=True) ) if non_public_idea_titles: raise CommandError( @@ -122,9 +131,7 @@ class Command(BaseCommand): missing_stockrecords: list[str] = [] for product in found_products.filter(title__in=expected_titles): stockrecord = ( - StockRecord.objects.filter(product=product) - .order_by("id") - .first() + StockRecord.objects.filter(product=product).order_by("id").first() ) if stockrecord is None: missing_stockrecords.append(product.title) @@ -187,7 +194,9 @@ class Command(BaseCommand): .values_list("title", "slug")[:10] ) if live_demo_pages: - formatted = ", ".join(f"{title} ({slug})" for title, slug in live_demo_pages) + formatted = ", ".join( + f"{title} ({slug})" for title, slug in live_demo_pages + ) raise CommandError( "Demo-like pages are still live/public. Purge them before launch. " f"Examples: {formatted}" diff --git a/mandelstudio/settings/base.py b/mandelstudio/settings/base.py index c714f9c..9447090 100644 --- a/mandelstudio/settings/base.py +++ b/mandelstudio/settings/base.py @@ -27,6 +27,7 @@ INSTALLED_APPS = [ "mandelstudio", ] + INSTALLED_APPS + # Never allow demo-data plugins in this production project context. def _is_demo_data_app(app_label: str) -> bool: normalized = "".join(ch for ch in app_label.lower() if ch.isalnum()) diff --git a/mandelstudio/sitemaps.py b/mandelstudio/sitemaps.py index 794380e..4d2e472 100644 --- a/mandelstudio/sitemaps.py +++ b/mandelstudio/sitemaps.py @@ -7,7 +7,9 @@ from ocyan.plugin.wagtail_oscar_integration.constants import CACHE_DURATION from ocyan.plugin.wagtail_oscar_integration.sitemap import CategorySitemap from ocyan.plugin.wagtail_oscar_integration.sitemap import ProductSitemap from ocyan.plugin.wagtail_oscar_integration.sitemap import ShopSitemap -from ocyan.plugin.wagtail_oscar_integration.sitemap import WagtailSitemap as BaseWagtailSitemap +from ocyan.plugin.wagtail_oscar_integration.sitemap import ( + WagtailSitemap as BaseWagtailSitemap, +) class WagtailSitemap(BaseWagtailSitemap): @@ -15,8 +17,8 @@ class WagtailSitemap(BaseWagtailSitemap): page_ids = [] for locale in Locale.objects.all(): - translated_root_page = self.get_wagtail_site().root_page.get_translation_or_none( - locale + translated_root_page = ( + self.get_wagtail_site().root_page.get_translation_or_none(locale) ) if translated_root_page is None: continue diff --git a/mandelstudio/templatetags/localized_navigation.py b/mandelstudio/templatetags/localized_navigation.py index ccde93e..017aaa7 100644 --- a/mandelstudio/templatetags/localized_navigation.py +++ b/mandelstudio/templatetags/localized_navigation.py @@ -19,7 +19,11 @@ def _fallback_locale_url(language_code: str) -> str: def _is_translatable_page(page) -> bool: - return page is not None and hasattr(page, "translation_key") and hasattr(page, "locale") + return ( + page is not None + and hasattr(page, "translation_key") + and hasattr(page, "locale") + ) def _translated_pages(page): @@ -46,15 +50,16 @@ def _build_absolute_url(request, path: str | None, page=None) -> str: @register.simple_tag def page_language_options(page): labels = { - _normalize_language_code(code): label - for code, label in settings.LANGUAGES + _normalize_language_code(code): label for code, label in settings.LANGUAGES } if not _is_translatable_page(page): return [ { "code": _normalize_language_code(code), - "label": labels.get(_normalize_language_code(code), _normalize_language_code(code)), + "label": labels.get( + _normalize_language_code(code), _normalize_language_code(code) + ), "url": _fallback_locale_url(code), } for code, _label in settings.LANGUAGES @@ -69,7 +74,9 @@ def page_language_options(page): { "code": language_code, "label": labels.get(language_code, language_code), - "url": translated_page.url if translated_page is not None else _fallback_locale_url(language_code), + "url": translated_page.url + if translated_page is not None + else _fallback_locale_url(language_code), } ) return options @@ -103,7 +110,9 @@ def page_hreflang_links(context): links.append( { "code": language_code, - "url": _build_absolute_url(request, translated_page.url, translated_page), + "url": _build_absolute_url( + request, translated_page.url, translated_page + ), } ) diff --git a/mandelstudio/templatetags/mandelstudio_footer.py b/mandelstudio/templatetags/mandelstudio_footer.py index 3e4c01c..885af7c 100644 --- a/mandelstudio/templatetags/mandelstudio_footer.py +++ b/mandelstudio/templatetags/mandelstudio_footer.py @@ -17,10 +17,7 @@ def localized_footer_content(context): language_code = getattr(request, "LANGUAGE_CODE", None) if not language_code: return None - return ( - LocalizedFooterContent.objects.filter( - site=site, - locale__language_code=language_code, - ) - .first() - ) + return LocalizedFooterContent.objects.filter( + site=site, + locale__language_code=language_code, + ).first()