204 lines
9.9 KiB
Python
204 lines
9.9 KiB
Python
from django.contrib.admin.models import CHANGE, LogEntry
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.core.management.base import BaseCommand
|
|
from django.db import DEFAULT_DB_ALIAS, connections, transaction
|
|
|
|
from apps.content.current import AboutContent, AboutItem, HomeContent, ListingContent, SiteContent
|
|
|
|
|
|
SITE_MOCK = {
|
|
"official_name": "نام انجمن نمونه",
|
|
"short_name": "انجمن نمونه",
|
|
"faculty_subline": "دانشکده نمونه",
|
|
"description": "این متن نمونه است و پس از ورود محتوای اصلی از پنل مدیریت جایگزین میشود.",
|
|
"logo": "",
|
|
"logo_alt": "",
|
|
"nav_home": "خانه",
|
|
"nav_about": "درباره ما",
|
|
"nav_events": "رویدادها",
|
|
"nav_blog": "بلاگ",
|
|
"email": "contact@example.invalid",
|
|
"phone": "0000000000",
|
|
"telegram_url": "https://example.invalid/telegram",
|
|
"instagram_url": "https://example.invalid/instagram",
|
|
"website_url": "https://example.invalid",
|
|
"address": "نشانی نمونه",
|
|
"footer_copy": "متن نمونه پابرگ؛ محتوای نهایی را از پنل مدیریت وارد کنید.",
|
|
"enamad_image": "",
|
|
"enamad_image_alt": "",
|
|
"enamad_url": "",
|
|
"seo_title": "عنوان نمونه سایت",
|
|
"seo_description": "توضیحات نمونه برای موتورهای جستوجو.",
|
|
"seo_image": "",
|
|
"seo_image_alt": "",
|
|
}
|
|
|
|
HOME_MOCK = {
|
|
"title": "عنوان نمونه صفحه اصلی",
|
|
"intro": "این متن نمونه صفحه اصلی است و پس از ورود محتوای اصلی جایگزین میشود.",
|
|
"hero_heading": "عنوان نمونه صفحه اصلی",
|
|
"hero_body": "توضیحات نمونه برای معرفی انجمن و فعالیتهای آن.",
|
|
"hero_image": "",
|
|
"hero_image_alt": "",
|
|
"hero_action_label": "مشاهده رویدادها",
|
|
"hero_action_url": "/events",
|
|
"hero_secondary_action_label": "مطالب بلاگ",
|
|
"hero_secondary_action_url": "/blog",
|
|
"featured_events_heading": "رویدادهای نمونه",
|
|
"featured_events_intro": "توضیحات نمونه برای بخش رویدادها.",
|
|
"featured_events_link_label": "همه رویدادها",
|
|
"featured_events_url": "/events",
|
|
"featured_event_action_label": "مشاهده رویداد",
|
|
"featured_posts_heading": "نوشتههای نمونه",
|
|
"featured_posts_intro": "توضیحات نمونه برای بخش نوشتهها.",
|
|
"featured_posts_link_label": "همه نوشتهها",
|
|
"featured_posts_url": "/blog",
|
|
"featured_post_action_label": "خواندن مطلب",
|
|
"participation_heading": "به ما بپیوندید",
|
|
"participation_body": "این متن نمونه راههای مشارکت را توضیح میدهد.",
|
|
"participation_image": "",
|
|
"participation_image_alt": "",
|
|
"participation_action_label": "اطلاعات بیشتر",
|
|
"participation_action_url": "/auth",
|
|
"participation_secondary_action_label": "درباره انجمن",
|
|
"participation_secondary_action_url": "/about",
|
|
"seo_title": "عنوان نمونه صفحه اصلی",
|
|
"seo_description": "توضیحات نمونه صفحه اصلی برای موتورهای جستوجو.",
|
|
"seo_image": "",
|
|
"seo_image_alt": "",
|
|
}
|
|
|
|
ABOUT_MOCK = {
|
|
"title": "عنوان نمونه صفحه درباره ما",
|
|
"intro": "این متن نمونه معرفی انجمن است و بعداً از پنل مدیریت قابل ویرایش است.",
|
|
"identity_heading": "معرفی نمونه",
|
|
"identity_body": "اطلاعات نمونه درباره هویت انجمن.",
|
|
"mission_heading": "ماموریت نمونه",
|
|
"mission_body": "توضیحات نمونه درباره ماموریت انجمن.",
|
|
"vision_heading": "چشمانداز نمونه",
|
|
"vision_body": "جامعهای برای یادگیری، تجربهکردن و رشد در کنار یکدیگر.",
|
|
"activities_heading": "فعالیتهای نمونه",
|
|
"activities_body": "توضیحات نمونه درباره فعالیتها.",
|
|
"values_heading": "ارزشهای نمونه",
|
|
"values_body": "یادگیری، همکاری و مسئولیتپذیری نمونه.",
|
|
"faq_heading": "پرسشهای نمونه",
|
|
"contact_heading": "راههای ارتباطی نمونه",
|
|
"members_heading": "اعضای انجمن",
|
|
"members_intro": "با اعضایی آشنا شوید که برای ساختن تجربههای بهتر در انجمن همکاری میکنند.",
|
|
"gallery_heading": "خاطرات انجمن",
|
|
"gallery_intro": "تصاویری از گردهماییها، برنامهها و لحظههایی که کنار هم ساختهایم.",
|
|
"past_events_heading": "رویدادهای برگزارشده",
|
|
"history_heading": "مسیر انجمن",
|
|
"audience_heading": "برای چه کسانی؟",
|
|
"year_events_heading": "لحظههای هر سال",
|
|
"cta_heading": "به جمع ما بپیوندید",
|
|
"cta_body": "اگر دوست دارید یاد بگیرید و در ساختن تجربههای تازه سهیم شوید، با ما همراه باشید.",
|
|
"cta_action_label": "راههای پیوستن",
|
|
"cta_action_url": "",
|
|
"seo_title": "عنوان نمونه صفحه درباره ما",
|
|
"seo_description": "توضیحات نمونه صفحه درباره ما برای موتورهای جستوجو.",
|
|
"seo_image": "",
|
|
"seo_image_alt": "",
|
|
}
|
|
|
|
LISTING_MOCKS = {
|
|
"events": {
|
|
"title": "عنوان نمونه رویدادها",
|
|
"intro": "این متن نمونه برای معرفی فهرست رویدادهاست.",
|
|
"seo_title": "عنوان نمونه رویدادها",
|
|
"seo_description": "توضیحات نمونه صفحه رویدادها برای موتورهای جستوجو.",
|
|
"seo_image": "",
|
|
"seo_image_alt": "",
|
|
},
|
|
"blog": {
|
|
"title": "عنوان نمونه بلاگ",
|
|
"intro": "این متن نمونه برای معرفی نوشتههای بلاگ است.",
|
|
"seo_title": "عنوان نمونه بلاگ",
|
|
"seo_description": "توضیحات نمونه صفحه بلاگ برای موتورهای جستوجو.",
|
|
"seo_image": "",
|
|
"seo_image_alt": "",
|
|
},
|
|
}
|
|
|
|
ABOUT_ITEM_MOCKS = (
|
|
("mission", "ماموریت نمونه", "این متن نمونه برای توضیح ماموریت انجمن است."),
|
|
("activities", "فعالیت نمونه", "این متن نمونه یکی از فعالیتهای انجمن را معرفی میکند."),
|
|
("values", "ارزش نمونه", "این متن نمونه یکی از ارزشهای انجمن را توضیح میدهد."),
|
|
("faq", "پرسش نمونه", "پاسخ نمونه؛ اطلاعات نهایی را از پنل مدیریت وارد کنید."),
|
|
)
|
|
|
|
RECORDS = (
|
|
("site", SiteContent, {"pk": 1}, SITE_MOCK),
|
|
("home", HomeContent, {"pk": 1}, HOME_MOCK),
|
|
("about", AboutContent, {"pk": 1}, ABOUT_MOCK),
|
|
("events", ListingContent, {"pk": "events"}, LISTING_MOCKS["events"]),
|
|
("blog", ListingContent, {"pk": "blog"}, LISTING_MOCKS["blog"]),
|
|
)
|
|
|
|
|
|
def has_admin_change(model, object_id, using):
|
|
content_type = ContentType.objects.db_manager(using).get_for_model(model)
|
|
return LogEntry.objects.using(using).filter(
|
|
content_type=content_type,
|
|
object_id=str(object_id),
|
|
action_flag=CHANGE,
|
|
).exists()
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = "Fill untouched site content records with obvious sample copy."
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument(
|
|
"--database",
|
|
default=DEFAULT_DB_ALIAS,
|
|
choices=tuple(connections),
|
|
help="Database alias to update.",
|
|
)
|
|
parser.add_argument(
|
|
"--dry-run",
|
|
action="store_true",
|
|
help="Report intended changes without writing to the database.",
|
|
)
|
|
|
|
def handle(self, *args, database, dry_run, **options):
|
|
updated = 0
|
|
skipped = 0
|
|
prefix = "Dry run: " if dry_run else ""
|
|
|
|
with transaction.atomic(using=database):
|
|
for label, model, lookup, values in RECORDS:
|
|
record = model.objects.using(database).select_for_update().filter(**lookup).first()
|
|
object_id = lookup["pk"]
|
|
if record is not None and has_admin_change(model, object_id, database):
|
|
self.stdout.write(f"Skipped {label}: it has a Django admin change entry.")
|
|
skipped += 1
|
|
continue
|
|
|
|
if dry_run:
|
|
action = "create" if record is None else "replace"
|
|
self.stdout.write(f"Dry run: would {action} {label} with mock content.")
|
|
updated += 1
|
|
continue
|
|
|
|
if record is None:
|
|
record = model(**lookup)
|
|
for field, value in values.items():
|
|
setattr(record, field, value)
|
|
record.save(using=database)
|
|
updated += 1
|
|
self.stdout.write(f"Loaded mock content for {label}.")
|
|
|
|
if label == "about":
|
|
AboutItem.objects.using(database).filter(about_id=record.pk).delete()
|
|
AboutItem.objects.using(database).bulk_create(
|
|
[
|
|
AboutItem(about_id=record.pk, kind=kind, title=title, text=text, position=position)
|
|
for position, (kind, title, text) in enumerate(ABOUT_ITEM_MOCKS)
|
|
]
|
|
)
|
|
|
|
action = "would be updated" if dry_run else "updated"
|
|
summary = f"{updated} content records {action}; {skipped} admin-edited records skipped."
|
|
self.stdout.write(f"{prefix}{summary}")
|