Browse Source

Add management commands for category and collection seeding

- Introduced a new command to evenly distribute source types across existing HadisCategory instances.
- Added a command to seed categories and collections in the library, relating existing books to them.
- Enhanced HadisCategory model to allow Unicode in slugs through a migration.
- Updated Nginx configuration to include a new location for proxying requests to a FastAPI agent.
master
Mohsen Taba 4 months ago
parent
commit
786efb9167
  1. 42
      apps/hadis/management/commands/category_fix.py
  2. 19
      apps/hadis/migrations/0005_alter_hadiscategory_slug.py
  3. 2
      apps/hadis/models/category.py
  4. 66
      apps/library/management/commands/seed_category_collection.py
  5. 11
      nginx/dovodi.conf

42
apps/hadis/management/commands/category_fix.py

@ -0,0 +1,42 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from apps.hadis.models import HadisCategory
class Command(BaseCommand):
help = 'توزیع متعادل انواع منابع (Source Types) بین تمام دسته‌بندی‌های موجود'
def handle(self, *args, **options):
self.stdout.write(self.style.WARNING('--- در حال به‌روزرسانی انواع منابع ---'))
# دریافت تمام گزینه‌های موجود در SourceType
source_choices = [choice[0] for choice in HadisCategory.SourceType.choices]
num_choices = len(source_choices)
# دریافت تمام دسته‌بندی‌ها به ترتیب شناسه یا ترتیب دلخواه
categories = HadisCategory.objects.all().order_by('id')
total_count = categories.count()
if total_count == 0:
self.stdout.write(self.style.ERROR('هیچ دسته‌بندی یافت نشد.'))
return
updated_count = 0
with transaction.atomic():
for index, category in enumerate(categories):
# انتخاب نوع منبع به صورت چرخشی
# این کار باعث می‌شود اگر ۱۲۰ مورد دارید، به هر نوع منبع دقیقاً ۲۴ مورد برسد
new_source = source_choices[index % num_choices]
category.source_type = new_source
# استفاده از update_fields برای افزایش سرعت و جلوگیری از اجرای سیگنال‌های اضافی در صورت تمایل
category.save(update_fields=['source_type'])
updated_count += 1
self.stdout.write(self.style.SUCCESS(f'✓ عملیات با موفقیت انجام شد.'))
self.stdout.write(self.style.SUCCESS(f'تعداد {updated_count} دسته‌بندی به‌روزرسانی شدند.'))
# نمایش آمار نهایی برای اطمینان از تعادل
for source in source_choices:
count = HadisCategory.objects.filter(source_type=source).count()
self.stdout.write(f" - {source}: {count} مورد")

19
apps/hadis/migrations/0005_alter_hadiscategory_slug.py

@ -0,0 +1,19 @@
# Generated by Django 4.2.27 on 2026-01-28 10:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("hadis", "0004_alter_hadiscollection_thumbnail_and_more"),
]
operations = [
migrations.AlterField(
model_name="hadiscategory",
name="slug",
field=models.SlugField(
allow_unicode=True, blank=True, max_length=255, null=True
),
),
]

2
apps/hadis/models/category.py

@ -76,7 +76,7 @@ class HadisCategory(MPTTModel):
description = models.JSONField(default = list , verbose_name=_('Description')) description = models.JSONField(default = list , verbose_name=_('Description'))
order = models.IntegerField(default=0, verbose_name=_('order')) order = models.IntegerField(default=0, verbose_name=_('order'))
xmind_file = models.FileField(upload_to='hadis/xmind_files/', verbose_name=_('xmind file'), null=True, blank=True) xmind_file = models.FileField(upload_to='hadis/xmind_files/', verbose_name=_('xmind file'), null=True, blank=True)
slug = models.SlugField(max_length=255, null=True, blank=True)
slug = models.SlugField(max_length=255, null=True, blank=True,allow_unicode=True)
content_type = None content_type = None
language = None language = None
language_id = None language_id = None

66
apps/library/management/commands/seed_category_collection.py

@ -0,0 +1,66 @@
import random
from django.core.management.base import BaseCommand
from django.db import transaction
from apps.library.models import Book, Category, BookCollection
class Command(BaseCommand):
help = 'Seeds categories and collections and relates all existing books to them.'
def handle(self, *args, **options):
self.stdout.write(self.style.WARNING('--- Starting Library Relation Seeding ---'))
with transaction.atomic():
# 1. CREATE CATEGORIES (10 Instances)
categories_data = [
'Classic Literature', 'Dystopian Fiction', 'Modern Philosophy',
'Historical Mystery', 'Self-Improvement', 'Biographical Memoir',
'Fantasy & Adventure', 'Social Science', 'Science & Technology',
'Religious Studies'
]
category_objs = []
for title in categories_data:
cat, created = Category.objects.get_or_create(title=title)
category_objs.append(cat)
if created:
self.stdout.write(f"Created Category: {title}")
# 2. CREATE COLLECTIONS (5 Instances)
collections_data = [
{'title': 'Editor\'s Choice 2026', 'pos': BookCollection.DisplayPosition.PINNED},
{'title': 'Most Downloaded This Month', 'pos': BookCollection.DisplayPosition.PINNED},
{'title': 'New Arrivals', 'pos': BookCollection.DisplayPosition.MIDDLE},
{'title': 'Essential Reading List', 'pos': BookCollection.DisplayPosition.MIDDLE},
{'title': 'Summer Recommendations', 'pos': BookCollection.DisplayPosition.MIDDLE},
]
collection_objs = []
for item in collections_data:
col, created = BookCollection.objects.get_or_create(
title=item['title'],
defaults={'display_position': item['pos'], 'pin_top': True}
)
collection_objs.append(col)
if created:
self.stdout.write(f"Created Collection: {item['title']}")
# 3. RELATE BOOKS
books = Book.objects.all()
if not books.exists():
self.stdout.write(self.style.ERROR('No books found! Please run the book population script first.'))
return
self.stdout.write(self.style.SUCCESS(f'Processing {books.count()} books...'))
for book in books:
# Rule: Every book must have at least 3 categories
# We pick 3 to 4 random categories to ensure variety
selected_cats = random.sample(category_objs, k=random.randint(3, 4))
book.categories.set(selected_cats)
# Rule: Every book must be in 1 or 2 collections
selected_cols = random.sample(collection_objs, k=random.randint(1, 2))
book.collections.set(selected_cols)
self.stdout.write(self.style.SUCCESS('--- Seeding Completed Successfully ---'))
self.stdout.write(f'Summary: {len(category_objs)} Categories, {len(collection_objs)} Collections applied to {books.count()} books.')

11
nginx/dovodi.conf

@ -14,6 +14,17 @@ server {
proxy_read_timeout 600s; proxy_read_timeout 600s;
# ========== Django Admin Paths (باید قبل از location / باشند) ========== # ========== Django Admin Paths (باید قبل از location / باشند) ==========
# Add this inside the 'server' block of the existing config
location /agent/ {
proxy_pass http://88.99.212.243:8098; # The port of your FastAPI Agent
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# با prefix زبانی # با prefix زبانی
location /en/dovoodi/ { location /en/dovoodi/ {

Loading…
Cancel
Save