From f15b1d4eab7016c3b53ddf642bc22e9292246e85 Mon Sep 17 00:00:00 2001 From: Mandel Olaiya Date: Mon, 6 Apr 2026 02:34:12 +0200 Subject: [PATCH] Add demo data purge command --- Jenkinsfile | 2 +- .../management/commands/purge_demo_data.py | 120 ++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 mandelstudio/management/commands/purge_demo_data.py diff --git a/Jenkinsfile b/Jenkinsfile index d10592c..1e8afd3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -82,7 +82,7 @@ PY pip install ruff vdt.versionplugin.wheel pip install --upgrade "setuptools==69.5.1" wheel manage.py migrate --no-input --skip-checks - manage.py purge_demo_data + manage.py purge_demo_data --keep-only-idea-products manage.py collectstatic --no-input --verbosity=0 pip install "httpx<0.28" ''' diff --git a/mandelstudio/management/commands/purge_demo_data.py b/mandelstudio/management/commands/purge_demo_data.py new file mode 100644 index 0000000..2e079b3 --- /dev/null +++ b/mandelstudio/management/commands/purge_demo_data.py @@ -0,0 +1,120 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from django.core.management.base import BaseCommand + + +IDEA_PRODUCT_TITLES = { + "B2B Webshop Starter Blueprint", + "AI Product Description System", + "High-Converting Landing Page Framework", + "Subscription-Based Service Website Model", + "Marketplace Platform Architecture (Django)", +} + + +@dataclass(frozen=True) +class PurgeStats: + pages: int = 0 + products: int = 0 + + +class Command(BaseCommand): + help = "Remove demo pages/products from Mandel Blog while preserving idea marketplace content." + + def add_arguments(self, parser): + parser.add_argument( + "--dry-run", + action="store_true", + help="Show what would be removed without deleting.", + ) + parser.add_argument( + "--keep-only-idea-products", + action="store_true", + help="Delete all products except the 5 idea marketplace products.", + ) + + def handle(self, *args, **options): + dry_run = bool(options["dry_run"]) + keep_only_idea_products = bool(options["keep_only_idea_products"]) + + page_count = self._purge_pages(dry_run=dry_run) + product_count = self._purge_products( + dry_run=dry_run, + keep_only_idea_products=keep_only_idea_products, + ) + stats = PurgeStats(pages=page_count, products=product_count) + + mode = "DRY RUN" if dry_run else "APPLIED" + self.stdout.write(self.style.SUCCESS(f"[{mode}] Removed pages={stats.pages}, products={stats.products}")) + + def _purge_pages(self, *, dry_run: bool) -> int: + from wagtail.models import Page + + slug_markers = [ + "starter-website", + "business-website", + "demo", + "voorbeeld", + "sample", + "template", + ] + title_markers = [ + "starter website", + "business website", + "demo", + "voorbeeld", + "sample", + "template", + ] + + qs = ( + Page.objects.exclude(depth=1) + .exclude(slug__in=["home"]) + .specific() + ) + + candidates = [] + for page in qs: + slug = (page.slug or "").lower() + title = (page.title or "").lower() + if any(marker in slug for marker in slug_markers) or any( + marker in title for marker in title_markers + ): + candidates.append(page) + + removed = 0 + for page in candidates: + removed += 1 + self.stdout.write(f"PAGE {'[dry-run] ' if dry_run else ''}delete id={page.id} slug={page.slug} title={page.title}") + if not dry_run: + page.delete() + return removed + + def _purge_products(self, *, dry_run: bool, keep_only_idea_products: bool) -> int: + from oscar.core.loading import get_model + + Product = get_model("catalogue", "Product") + products = Product.objects.all() + + if keep_only_idea_products: + candidates = products.exclude(title__in=IDEA_PRODUCT_TITLES) + else: + title_markers = ["demo", "sample", "placeholder", "starter website", "business website"] + candidates = [ + p + for p in products + if any(marker in (p.title or "").lower() for marker in title_markers) + ] + + removed = 0 + iterable = candidates if isinstance(candidates, list) else list(candidates) + for product in iterable: + removed += 1 + self.stdout.write( + f"PRODUCT {'[dry-run] ' if dry_run else ''}delete id={product.id} title={product.title}" + ) + if not dry_run: + product.delete() + return removed