Browse Source

building articles

master
Mohsen Taba 4 months ago
parent
commit
ff0cde6c63
  1. 269
      apps/article/management/commands/populate_article.py
  2. 3
      entrypoint.sh
  3. BIN
      seeds/images/articl3.jpg

269
apps/article/management/commands/populate_article.py

@ -1,11 +1,11 @@
import os import os
import random import random
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.core.files import File
from django.core.files.base import ContentFile
from django.conf import settings from django.conf import settings
from django.db import transaction
from django.utils.text import slugify
# REPLACE 'apps.article.models' WITH YOUR ACTUAL APP PATH
# REPLACE WITH YOUR ACTUAL APP PATHS
from apps.article.models import ( from apps.article.models import (
Article, Article,
ArticleCategory, ArticleCategory,
@ -17,107 +17,182 @@ from apps.article.models import (
) )
class Command(BaseCommand): class Command(BaseCommand):
help = 'Seeds 25 articles with categories, collections, and structured content.'
help = 'Seeds 25 rich Islamic articles (No Atomic Transaction).'
def handle(self, *args, **kwargs): def handle(self, *args, **kwargs):
self.stdout.write('Seeding Articles...')
self.stdout.write('--- Starting Rich Article Seeding ---')
# 1. Configuration
TOTAL_ARTICLES = 25
# ==========================================
# 1. CONFIGURATION
# ==========================================
TOTAL_ARTICLES_TO_CREATE = 25
CATEGORY_IDS = [6, 7, 8, 9] CATEGORY_IDS = [6, 7, 8, 9]
CONTENT_SOURCE_IDS = range(13, 24) # IDs 13 to 23
PROTECTED_SLUG = "امام-حسین-ع-از-ولادت-تا-جاودانگی-تحلیلی-بر-مبانی-ق"
IMAGE_DIR = os.path.join(settings.BASE_DIR, 'seeds', 'images') IMAGE_DIR = os.path.join(settings.BASE_DIR, 'seeds', 'images')
IMAGE_NAMES = [f'articl{i}.jpg' for i in range(2, 5)]
# 2. Fetch Existing Dependencies
# Explicit file names based on your folder structure
IMAGE_NAMES = ['articl2.jpg', 'articl3.jpg', 'articl4.jpg']
RICH_DATA = [
{
'title_en': 'The Event of Ghadir Khumm: Completion of Religion',
'title_fa': 'واقعه غدیر خم: کمال دین و اتمام نعمت',
'summary': 'An analysis of the sermon of the Prophet (PBUH) at Ghadir Khumm.',
'arabic': 'مَنْ كُنْتُ مَوْلَاهُ فَهَذَا عَلِيٌّ مَوْلَاهُ',
'trans': 'For whomever I am his Master (Mawla), then Ali is his Master.',
'content_body': 'The event of Ghadir is one of the most undeniable historical events in Islam...'
},
{
'title_en': 'Lady Fatimah (SA): The Role Model for Women',
'title_fa': 'حضرت فاطمه زهرا (س): الگوی زنان عالم',
'summary': 'Exploring the virtues of the daughter of the Prophet.',
'arabic': 'فَاطِمَةُ بَضْعَةٌ مِنِّي',
'trans': 'Fatimah is a part of me.',
'content_body': 'Lady Fatimah az-Zahra (SA) is not just a role model for women...'
},
{
'title_en': 'Nahj al-Balagha: The Peak of Eloquence',
'title_fa': 'نهج البلاغه: قله فصاحت و بلاغت',
'summary': 'A look into the sermons of Imam Ali (AS).',
'arabic': 'سَلُونِي قَبْلَ أَنْ تَفْقِدُونِي',
'trans': 'Ask me before you lose me.',
'content_body': 'Nahj al-Balagha contains deep philosophical guidance...'
},
{
'title_en': 'The Significance of Laylat al-Qadr',
'title_fa': 'عظمت شب قدر',
'summary': 'Why is the Night of Power better than a thousand months?',
'arabic': 'إِنَّا أَنزَلْنَاهُ فِي لَيْلَةِ الْقَدْرِ',
'trans': 'Indeed, We sent the Qur\'an down during the Night of Decree.',
'content_body': 'The Night of Qadr is the night where destiny is written...'
},
{
'title_en': 'Imam Mahdi (AJ): The Savior',
'title_fa': 'امام مهدی (عج): منجی بشریت',
'summary': 'The concept of the Awaited One in Islamic eschatology.',
'arabic': 'بَقِيَّتُ اللَّهِ خَيْرٌ لَكُمْ',
'trans': 'The remnant of Allah is better for you.',
'content_body': 'The belief in a savior is universal...'
},
{
'title_en': 'Rights of Parents',
'title_fa': 'حقوق والدین',
'summary': 'Examining the status of parents.',
'arabic': 'وَبِالْوَالِدَيْنِ إِحْسَانًا',
'trans': 'And be good to parents.',
'content_body': 'Respecting parents is placed immediately after worship of God...'
}
]
# ==========================================
# 2. CLEANUP
# ==========================================
self.stdout.write("Cleaning old articles (preserving protected one)...")
deleted_count, _ = Article.objects.exclude(slug=PROTECTED_SLUG).delete()
ArticleCollection.objects.all().delete()
self.stdout.write(self.style.WARNING(f"Deleted {deleted_count} articles."))
# ==========================================
# 3. PREPARE DEPENDENCIES
# ==========================================
categories = list(ArticleCategory.objects.filter(id__in=CATEGORY_IDS)) categories = list(ArticleCategory.objects.filter(id__in=CATEGORY_IDS))
if not categories:
self.stdout.write(self.style.WARNING(f"Categories with IDs {CATEGORY_IDS} not found. Skipping category assignment."))
# 3. Create 4 New Collections
collection_names = [
("Islamic History", "تاریخ اسلام"),
("Theology & Beliefs", "عقاید و کلام"),
("Ethics & Spirituality", "اخلاق و عرفان"),
("Quran & Hadith", "قرآن و حدیث")
]
collections = [] collections = []
for i in range(1, 5):
col, created = ArticleCollection.objects.get_or_create(
title=f'Seeded Collection {i}',
defaults={
'slug': f'seeded-collection-{i}',
'summary': f'This is a generated summary for collection {i}',
'display_position': ArticleCollection.DisplayPosition.MIDDLE
}
for en_name, fa_name in collection_names:
col = ArticleCollection.objects.create(
title=en_name,
slug=slugify(en_name),
summary=f'Collection of {en_name}',
display_position=ArticleCollection.DisplayPosition.MIDDLE
) )
collections.append(col) collections.append(col)
self.stdout.write(self.style.SUCCESS(f'Created/Loaded {len(collections)} collections.'))
# 4. Fetch Source Content for Cloning
# NOTE: Since ArticleContent is a ForeignKey (One-to-Many), we cannot
# link existing content objects to new articles without removing them
# from their old articles. Instead, we will CLONE their data.
source_contents = list(ArticleContent.objects.filter(id__in=CONTENT_SOURCE_IDS))
if not source_contents:
self.stdout.write(self.style.WARNING(f"ArticleContents with IDs 13-23 not found. Creating generic content instead."))
# 5. Main Loop: Create Articles
with transaction.atomic():
for i in range(1, TOTAL_ARTICLES + 1):
title = f'Seeded Article Title {i}'
# Create Article
article = Article.objects.create(
title=title,
description=f'Description for article {i}. Lorem ipsum dolor sit amet.',
content=f'Full content body for article {i}.',
status=True
)
# Assign Thumbnail (Cycle through images 1-5)
img_name = IMAGE_NAMES[(i - 1) % len(IMAGE_NAMES)]
img_path = os.path.join(IMAGE_DIR, img_name)
if os.path.exists(img_path):
# ==========================================
# 4. MAIN CREATION LOOP (NO ATOMIC TRANSACTION)
# ==========================================
self.stdout.write("Starting creation loop...")
for i in range(1, TOTAL_ARTICLES_TO_CREATE + 1):
# Select Data
data = RICH_DATA[(i - 1) % len(RICH_DATA)]
# Construct Unique Data
final_title = f"{data['title_en']} (Vol {i})"
short_slug_base = slugify(data['title_en'])[:30]
unique_slug = f"{short_slug_base}-{i}"
if unique_slug == PROTECTED_SLUG:
unique_slug = f"{unique_slug}-generated"
# 1. Create Article Object
article = Article.objects.create(
title=final_title,
slug=unique_slug,
description=data['summary'],
content=data['content_body'],
status=True
)
# 2. Handle Image
img_name = IMAGE_NAMES[(i - 1) % len(IMAGE_NAMES)]
img_path = os.path.join(IMAGE_DIR, img_name)
if os.path.exists(img_path):
try:
with open(img_path, 'rb') as f: with open(img_path, 'rb') as f:
article.thumbnail.save(img_name, File(f), save=True)
else:
self.stdout.write(self.style.WARNING(f"Image not found at {img_path}"))
# Assign Categories (Randomly pick 1 or 2)
if categories:
article.categories.add(*random.sample(categories, k=random.randint(1, 2)))
# Assign to Collections (Randomly pick 1)
random_col = random.choice(collections)
ArticleInCollection.objects.create(
article=article,
collection=random_col,
order=i
)
# Create Article Content (Cloning from IDs 13-23 or Generic)
if source_contents:
# Pick a random template from the requested IDs
template = random.choice(source_contents)
content_title = f"{template.title} (Copy for Article {i})"
content_body = template.content
else:
content_title = f"Structured Content for Article {i}"
content_body = "This is a section of structured content."
# Create the ArticleContent object linked to this NEW article
ac = ArticleContent.objects.create(
article=article,
title=content_title,
content=content_body,
priority=1
)
# Create nested ContentParts and TextSections
part = ContentPart.objects.create(article_content=ac, order=1)
TextSection.objects.create(
content_part=part,
arabic_text="نص تجريبي باللغة العربية",
translation="Experimental text in English translation",
order=1
)
self.stdout.write(f'Created Article: {article.title}')
self.stdout.write(self.style.SUCCESS(f'Successfully seeded {TOTAL_ARTICLES} articles!'))
file_content = f.read()
# NOTE: If the script hangs here, check Article.save() signals
article.thumbnail.save(img_name, ContentFile(file_content), save=True)
except Exception as e:
self.stdout.write(self.style.ERROR(f"Error saving image for item {i}: {e}"))
else:
self.stdout.write(self.style.WARNING(f"Image missing: {img_path}"))
# 3. Assign Categories
if categories:
article.categories.add(*random.sample(categories, k=random.randint(1, 2)))
# 4. Assign Collection
random_col = random.choice(collections)
ArticleInCollection.objects.create(
article=article,
collection=random_col,
order=i
)
# 5. Create Content Hierarchy
ac = ArticleContent.objects.create(
article=article,
title=data['title_fa'],
content=data['content_body'],
priority=1
)
part = ContentPart.objects.create(article_content=ac, order=1)
TextSection.objects.create(
content_part=part,
arabic_text=data['arabic'],
translation=data['trans'],
order=1
)
TextSection.objects.create(
content_part=part,
arabic_text=f"شرح: {data['title_fa']}",
translation=f"Commentary on {data['title_en']}",
order=2
)
self.stdout.write(f"[{i}/{TOTAL_ARTICLES_TO_CREATE}] Created: {unique_slug}")
self.stdout.write(self.style.SUCCESS(f'Successfully seeded {TOTAL_ARTICLES_TO_CREATE} rich articles.'))

3
entrypoint.sh

@ -7,6 +7,7 @@ python manage.py collectstatic --noinput
# python manage.py populate_books # python manage.py populate_books
# python manage.py populate_book_reference # python manage.py populate_book_reference
python manage.py populate_refrence_images
# python manage.py populate_refrence_images
python manage.py populate_article
exec "$@" exec "$@"

BIN
seeds/images/articl3.jpg

After

Width: 1192  |  Height: 796  |  Size: 254 KiB

Loading…
Cancel
Save