production refresh
This commit is contained in:
60
mandelstudio/migrations/0001_initial.py
Normal file
60
mandelstudio/migrations/0001_initial.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# Generated by Django 5.2.11 on 2026-03-25 16:37
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
import wagtail.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("wagtailcore", "0097_alter_page_locale_alter_page_translation_key"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="LocalizedFooterContent",
|
||||
fields=[
|
||||
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("title", models.CharField(default="Footer content", max_length=120)),
|
||||
("translation_key", models.UUIDField(default=uuid.uuid4, editable=False)),
|
||||
(
|
||||
"footer",
|
||||
wagtail.fields.StreamField(
|
||||
[("about_us", 2), ("text", 2), ("page_list", 4), ("SubscriptionBlock", 7)],
|
||||
block_lookup={
|
||||
0: ("wagtail.blocks.CharBlock", (), {"help_text": "Heading of the content block.", "label": "Heading", "required": False}),
|
||||
1: ("wagtail.blocks.RichTextBlock", (), {}),
|
||||
2: ("wagtail.blocks.StructBlock", [[("heading", 0), ("content", 1)]], {}),
|
||||
3: ("wagtail.blocks.PageChooserBlock", (), {"help_text": "List pages below this page", "label": "Page"}),
|
||||
4: ("wagtail.blocks.StructBlock", [[("heading", 0), ("page", 3)]], {}),
|
||||
5: ("wagtail.blocks.CharBlock", (), {"label": "Title", "required": False}),
|
||||
6: ("wagtail.blocks.TextBlock", (), {"label": "Description", "required": False}),
|
||||
7: ("wagtail.blocks.StructBlock", [[("title", 5), ("description", 6)]], {}),
|
||||
},
|
||||
default=list,
|
||||
),
|
||||
),
|
||||
(
|
||||
"mini_footer",
|
||||
wagtail.fields.StreamField(
|
||||
[("text", 0)],
|
||||
block_lookup={0: ("wagtail.blocks.RichTextBlock", (), {})},
|
||||
default=list,
|
||||
),
|
||||
),
|
||||
("locale", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="+", to="wagtailcore.locale")),
|
||||
("site", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="localized_footer_contents", to="wagtailcore.site")),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Localized footer content",
|
||||
"verbose_name_plural": "Localized footer contents",
|
||||
"abstract": False,
|
||||
"constraints": [models.UniqueConstraint(fields=("site", "locale"), name="unique_localized_footer_per_site_locale")],
|
||||
"unique_together": {("translation_key", "locale")},
|
||||
},
|
||||
),
|
||||
]
|
||||
236
mandelstudio/migrations/0002_seed_localized_footer_content.py
Normal file
236
mandelstudio/migrations/0002_seed_localized_footer_content.py
Normal file
@@ -0,0 +1,236 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
CONTENT = {
|
||||
"nl": {
|
||||
"about": "<p>Wij bouwen snelle websites en webshops die je team zelf kan beheren. Van eerste lancering tot doorontwikkeling: helder, schaalbaar en zonder ruis.</p>",
|
||||
"links_heading": "Snelle links",
|
||||
"support_heading": "Help & support",
|
||||
"link_labels": {
|
||||
"about": "Over ons",
|
||||
"services": "Diensten",
|
||||
"projects": "Projecten",
|
||||
"contact": "Contact",
|
||||
"capabilities": "Mogelijkheden",
|
||||
"ai_search": "AI Search",
|
||||
"book_call": "Plan een gesprek",
|
||||
},
|
||||
"mini": "<p><a href=\"{contact}\">Contact</a> - <a href=\"{services}\">Diensten</a> - <a href=\"{projects}\">Projecten</a> - Copyright 2026 - MandelBlog Studio</p>",
|
||||
},
|
||||
"en": {
|
||||
"about": "<p>We build fast websites and webshops your team can manage without friction. From launch to growth, the setup stays clear, scalable, and easy to extend.</p>",
|
||||
"links_heading": "Quick links",
|
||||
"support_heading": "Help & support",
|
||||
"link_labels": {
|
||||
"about": "About us",
|
||||
"services": "Services",
|
||||
"projects": "Projects",
|
||||
"contact": "Contact",
|
||||
"capabilities": "Capabilities",
|
||||
"ai_search": "AI Search",
|
||||
"book_call": "Book a call",
|
||||
},
|
||||
"mini": "<p><a href=\"{contact}\">Contact</a> - <a href=\"{services}\">Services</a> - <a href=\"{projects}\">Projects</a> - Copyright 2026 - MandelBlog Studio</p>",
|
||||
},
|
||||
"de": {
|
||||
"about": "<p>Wir entwickeln schnelle Websites und Webshops, die Ihr Team selbst pflegen kann. Von der ersten Veröffentlichung bis zur Weiterentwicklung bleibt alles klar, skalierbar und wartbar.</p>",
|
||||
"links_heading": "Schnellzugriff",
|
||||
"support_heading": "Hilfe & Support",
|
||||
"link_labels": {
|
||||
"about": "Über uns",
|
||||
"services": "Dienstleistungen",
|
||||
"projects": "Projekte",
|
||||
"contact": "Kontakt",
|
||||
"capabilities": "Möglichkeiten",
|
||||
"ai_search": "KI-Suche",
|
||||
"book_call": "Gespräch planen",
|
||||
},
|
||||
"mini": "<p><a href=\"{contact}\">Kontakt</a> - <a href=\"{services}\">Dienstleistungen</a> - <a href=\"{projects}\">Projekte</a> - Copyright 2026 - MandelBlog Studio</p>",
|
||||
},
|
||||
"fr": {
|
||||
"about": "<p>Nous créons des sites web et des boutiques en ligne rapides que votre équipe peut gérer facilement. Du lancement à la croissance, tout reste clair, évolutif et simple à maintenir.</p>",
|
||||
"links_heading": "Accès rapide",
|
||||
"support_heading": "Aide & support",
|
||||
"link_labels": {
|
||||
"about": "À propos",
|
||||
"services": "Services",
|
||||
"projects": "Projets",
|
||||
"contact": "Contact",
|
||||
"capabilities": "Possibilités",
|
||||
"ai_search": "Recherche IA",
|
||||
"book_call": "Planifier un échange",
|
||||
},
|
||||
"mini": "<p><a href=\"{contact}\">Contact</a> - <a href=\"{services}\">Services</a> - <a href=\"{projects}\">Projets</a> - Copyright 2026 - MandelBlog Studio</p>",
|
||||
},
|
||||
"es": {
|
||||
"about": "<p>Construimos sitios web y tiendas online rápidas que tu equipo puede gestionar sin complicaciones. Desde el lanzamiento hasta el crecimiento, todo se mantiene claro, escalable y fácil de ampliar.</p>",
|
||||
"links_heading": "Accesos rápidos",
|
||||
"support_heading": "Ayuda y soporte",
|
||||
"link_labels": {
|
||||
"about": "Sobre nosotros",
|
||||
"services": "Servicios",
|
||||
"projects": "Proyectos",
|
||||
"contact": "Contacto",
|
||||
"capabilities": "Posibilidades",
|
||||
"ai_search": "Búsqueda con IA",
|
||||
"book_call": "Planificar una llamada",
|
||||
},
|
||||
"mini": "<p><a href=\"{contact}\">Contacto</a> - <a href=\"{services}\">Servicios</a> - <a href=\"{projects}\">Proyectos</a> - Copyright 2026 - MandelBlog Studio</p>",
|
||||
},
|
||||
"it": {
|
||||
"about": "<p>Realizziamo siti web e negozi online veloci che il tuo team può gestire in autonomia. Dal lancio alla crescita, tutto rimane chiaro, scalabile e semplice da estendere.</p>",
|
||||
"links_heading": "Link rapidi",
|
||||
"support_heading": "Aiuto e supporto",
|
||||
"link_labels": {
|
||||
"about": "Chi siamo",
|
||||
"services": "Servizi",
|
||||
"projects": "Progetti",
|
||||
"contact": "Contatto",
|
||||
"capabilities": "Possibilità",
|
||||
"ai_search": "Ricerca AI",
|
||||
"book_call": "Prenota una call",
|
||||
},
|
||||
"mini": "<p><a href=\"{contact}\">Contatto</a> - <a href=\"{services}\">Servizi</a> - <a href=\"{projects}\">Progetti</a> - Copyright 2026 - MandelBlog Studio</p>",
|
||||
},
|
||||
"pt": {
|
||||
"about": "<p>Criamos sites e lojas online rápidos que a sua equipa consegue gerir com autonomia. Do lançamento ao crescimento, tudo permanece claro, escalável e simples de evoluir.</p>",
|
||||
"links_heading": "Acesso rápido",
|
||||
"support_heading": "Ajuda e suporte",
|
||||
"link_labels": {
|
||||
"about": "Sobre nós",
|
||||
"services": "Serviços",
|
||||
"projects": "Projetos",
|
||||
"contact": "Contacto",
|
||||
"capabilities": "Possibilidades",
|
||||
"ai_search": "Pesquisa IA",
|
||||
"book_call": "Marcar conversa",
|
||||
},
|
||||
"mini": "<p><a href=\"{contact}\">Contacto</a> - <a href=\"{services}\">Serviços</a> - <a href=\"{projects}\">Projetos</a> - Copyright 2026 - MandelBlog Studio</p>",
|
||||
},
|
||||
"ru": {
|
||||
"about": "<p>Мы создаём быстрые сайты и интернет-магазины, которыми ваша команда может управлять самостоятельно. От запуска до развития всё остаётся понятным, масштабируемым и удобным для роста.</p>",
|
||||
"links_heading": "Быстрые ссылки",
|
||||
"support_heading": "Помощь и поддержка",
|
||||
"link_labels": {
|
||||
"about": "О нас",
|
||||
"services": "Услуги",
|
||||
"projects": "Проекты",
|
||||
"contact": "Контакт",
|
||||
"capabilities": "Возможности",
|
||||
"ai_search": "AI Search",
|
||||
"book_call": "Запланировать звонок",
|
||||
},
|
||||
"mini": "<p><a href=\"{contact}\">Контакт</a> - <a href=\"{services}\">Услуги</a> - <a href=\"{projects}\">Проекты</a> - Copyright 2026 - MandelBlog Studio</p>",
|
||||
},
|
||||
}
|
||||
|
||||
SOURCE_SLUGS = {
|
||||
"about": "over-ons",
|
||||
"services": "diensten",
|
||||
"projects": "projecten",
|
||||
"contact": "contact",
|
||||
"capabilities": "mogelijkheden",
|
||||
"ai_search": "ai-search",
|
||||
}
|
||||
|
||||
|
||||
def build_urls(Page, code):
|
||||
source_pages = {
|
||||
key: Page.objects.filter(locale__language_code="nl", slug=slug).first()
|
||||
for key, slug in SOURCE_SLUGS.items()
|
||||
}
|
||||
urls = {}
|
||||
for key, page in source_pages.items():
|
||||
if not page:
|
||||
urls[key] = "/"
|
||||
continue
|
||||
translated = Page.objects.filter(
|
||||
translation_key=page.translation_key, locale__language_code=code
|
||||
).first()
|
||||
chosen = translated or page
|
||||
urls[key] = getattr(chosen, "url", None) or "/"
|
||||
return urls
|
||||
|
||||
|
||||
def make_footer_raw(code, urls):
|
||||
content = CONTENT[code]
|
||||
labels = content["link_labels"]
|
||||
links_html = (
|
||||
f'<p><a href="{urls["about"]}">{labels["about"]}</a><br/>'
|
||||
f'<a href="{urls["services"]}">{labels["services"]}</a><br/>'
|
||||
f'<a href="{urls["projects"]}">{labels["projects"]}</a><br/>'
|
||||
f'<a href="{urls["contact"]}">{labels["contact"]}</a></p>'
|
||||
)
|
||||
support_html = (
|
||||
f'<p><a href="{urls["capabilities"]}">{labels["capabilities"]}</a><br/>'
|
||||
f'<a href="{urls["ai_search"]}">{labels["ai_search"]}</a><br/>'
|
||||
f'<a href="{urls["contact"]}">{labels["book_call"]}</a><br/>'
|
||||
f'<a href="mailto:info@mandelblog.com">info@mandelblog.com</a></p>'
|
||||
)
|
||||
return [
|
||||
{
|
||||
"type": "about_us",
|
||||
"id": str(uuid.uuid4()),
|
||||
"value": {"heading": "MandelBlog Studio", "content": content["about"]},
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": str(uuid.uuid4()),
|
||||
"value": {"heading": content["links_heading"], "content": links_html},
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": str(uuid.uuid4()),
|
||||
"value": {"heading": content["support_heading"], "content": support_html},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def make_mini_raw(code, urls):
|
||||
return [
|
||||
{
|
||||
"type": "text",
|
||||
"id": str(uuid.uuid4()),
|
||||
"value": CONTENT[code]["mini"].format(**urls),
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def seed_footer_content(apps, schema_editor):
|
||||
LocalizedFooterContent = apps.get_model("mandelstudio", "LocalizedFooterContent")
|
||||
Site = apps.get_model("wagtailcore", "Site")
|
||||
Locale = apps.get_model("wagtailcore", "Locale")
|
||||
site = Site.objects.order_by("id").first()
|
||||
if site is None:
|
||||
return
|
||||
|
||||
from wagtail.models import Page
|
||||
|
||||
translation_key = uuid.uuid4()
|
||||
for code in CONTENT.keys():
|
||||
locale, _ = Locale.objects.get_or_create(language_code=code)
|
||||
urls = build_urls(Page, code)
|
||||
LocalizedFooterContent.objects.update_or_create(
|
||||
site=site,
|
||||
locale=locale,
|
||||
defaults={
|
||||
"title": f"Footer content ({code})",
|
||||
"translation_key": translation_key,
|
||||
"footer": make_footer_raw(code, urls),
|
||||
"mini_footer": make_mini_raw(code, urls),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def reverse_seed(apps, schema_editor):
|
||||
LocalizedFooterContent = apps.get_model("mandelstudio", "LocalizedFooterContent")
|
||||
LocalizedFooterContent.objects.all().delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [("mandelstudio", "0001_initial")]
|
||||
operations = [migrations.RunPython(seed_footer_content, reverse_seed)]
|
||||
51
mandelstudio/migrations/0003_locale_audit_models.py
Normal file
51
mandelstudio/migrations/0003_locale_audit_models.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [("mandelstudio", "0002_seed_localized_footer_content")]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="LocaleAuditRun",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("started_at", models.DateTimeField(auto_now_add=True)),
|
||||
("finished_at", models.DateTimeField(blank=True, null=True)),
|
||||
("locale_codes", models.JSONField(blank=True, default=list)),
|
||||
("fix_enabled", models.BooleanField(default=False)),
|
||||
("total_urls_checked", models.PositiveIntegerField(default=0)),
|
||||
("issues_found", models.PositiveIntegerField(default=0)),
|
||||
("pages_with_issues", models.PositiveIntegerField(default=0)),
|
||||
("summary", models.JSONField(blank=True, default=dict)),
|
||||
],
|
||||
options={"ordering": ["-started_at"]},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="LocaleAuditIssue",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("locale_code", models.CharField(max_length=12)),
|
||||
("object_id", models.PositiveIntegerField(blank=True, null=True)),
|
||||
("object_type", models.CharField(blank=True, max_length=128)),
|
||||
("url", models.TextField(blank=True)),
|
||||
("title", models.CharField(blank=True, max_length=255)),
|
||||
("severity", models.CharField(max_length=16)),
|
||||
("issue_type", models.CharField(max_length=64)),
|
||||
("field_path", models.CharField(blank=True, max_length=512)),
|
||||
("bad_value", models.TextField(blank=True)),
|
||||
("replacement", models.TextField(blank=True)),
|
||||
("fixed", models.BooleanField(default=False)),
|
||||
("extra", models.JSONField(blank=True, default=dict)),
|
||||
(
|
||||
"run",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="issues",
|
||||
to="mandelstudio.localeauditrun",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={"ordering": ["locale_code", "url", "field_path"]},
|
||||
),
|
||||
]
|
||||
0
mandelstudio/migrations/__init__.py
Normal file
0
mandelstudio/migrations/__init__.py
Normal file
Reference in New Issue
Block a user