feat(blog): add production taxonomy seed commands
This commit is contained in:
125
apps/blog/management/commands/seed_blog_categories.py
Normal file
125
apps/blog/management/commands/seed_blog_categories.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from apps.blog.models import Category
|
||||
|
||||
|
||||
CATEGORIES = [
|
||||
{
|
||||
"name": "اخبار و اطلاعیهها",
|
||||
"description": "خبرها، اطلاعیهها و گزارشهای مرتبط با انجمن، دانشکده و جامعه دانشجویی.",
|
||||
"children": [
|
||||
("اخبار انجمن", "خبرها و گزارشهای رسمی انجمن علمی مهندسی کامپیوتر."),
|
||||
("اطلاعیههای آموزشی", "اطلاعیههای مهم آموزشی، انتخاب واحد، امتحانات و امور دانشجویی."),
|
||||
("رویدادها و کارگاهها", "معرفی، گزارش و پیگیری رویدادها، نشستها و کارگاهها."),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "آموزش و مسیر یادگیری",
|
||||
"description": "مطالب آموزشی و مسیرهای یادگیری برای دانشجویان علوم و مهندسی کامپیوتر.",
|
||||
"children": [
|
||||
("راهنمای شروع", "مطالب مقدماتی برای شروع برنامهنویسی، دانشگاه و مهارتآموزی."),
|
||||
("آموزشهای فنی", "آموزشهای عملی، گامبهگام و مسئلهمحور در حوزههای فنی."),
|
||||
("منابع یادگیری", "معرفی کتاب، دوره، مستندات، مسیر مطالعه و منابع مفید."),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "فناوری و مهندسی نرمافزار",
|
||||
"description": "مقالههای فنی درباره توسعه نرمافزار، ابزارها، معماری و فناوریهای روز.",
|
||||
"children": [
|
||||
("برنامهنویسی", "زبانها، الگوها، نکتههای کدنویسی و تجربههای عملی توسعه."),
|
||||
("وب و اپلیکیشن", "فرانتاند، بکاند، موبایل، API و تجربه ساخت محصول."),
|
||||
("دواپس و ابزارها", "لینوکس، گیت، CI/CD، استقرار، کانتینر و ابزارهای توسعه."),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "هوش مصنوعی و داده",
|
||||
"description": "مطالب مرتبط با هوش مصنوعی، یادگیری ماشین، داده و کاربردهای آنها.",
|
||||
"children": [
|
||||
("یادگیری ماشین", "مفاهیم، تمرینها و تجربههای یادگیری ماشین و مدلسازی."),
|
||||
("علم داده", "تحلیل داده، مصورسازی، آمار کاربردی و پروژههای دادهمحور."),
|
||||
("هوش مصنوعی کاربردی", "ابزارها، کاربردها، ایدهها و تجربههای عملی با AI."),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "دانشگاه و پژوهش",
|
||||
"description": "محتوای علمی، پژوهشی و دانشگاهی برای دانشجویان و اعضای انجمن.",
|
||||
"children": [
|
||||
("پژوهش دانشجویی", "تجربهها، معرفی مقاله، ایده پژوهشی و همکاریهای علمی."),
|
||||
("درس و دانشگاه", "راهنمای درسها، پروژههای درسی، امتحان و تجربه دانشگاهی."),
|
||||
("مسابقات علمی", "برنامهنویسی رقابتی، مسابقات، چالشها و آمادگی تیمی."),
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "پروژهها و تجربهها",
|
||||
"description": "تجربههای واقعی دانشجویان از پروژه، کار تیمی، کارآموزی و مسیر حرفهای.",
|
||||
"children": [
|
||||
("پروژههای دانشجویی", "معرفی، کالبدشکافی و گزارش پروژههای دانشجویی و تیمی."),
|
||||
("کارآموزی و بازار کار", "رزومه، مصاحبه، کارآموزی، مسیر شغلی و تجربه ورود به کار."),
|
||||
("تجربههای انجمنی", "روایتها و درسآموختههای فعالیت در انجمن و تیمهای دانشجویی."),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def console_safe(value: str) -> str:
|
||||
encoding = sys.stdout.encoding or "utf-8"
|
||||
return value.encode(encoding, errors="backslashreplace").decode(encoding)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Create or update production blog categories for the CS association blog."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print planned categories without writing changes.")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
dry_run = options["dry_run"]
|
||||
root_count = 0
|
||||
child_count = 0
|
||||
|
||||
for root_spec in CATEGORIES:
|
||||
if dry_run:
|
||||
self.stdout.write(console_safe(f"[root] {root_spec['name']}"))
|
||||
for child_name, _ in root_spec["children"]:
|
||||
self.stdout.write(console_safe(f" [child] {child_name}"))
|
||||
continue
|
||||
|
||||
root = self._upsert_category(
|
||||
name=root_spec["name"],
|
||||
description=root_spec["description"],
|
||||
parent=None,
|
||||
)
|
||||
root_count += 1
|
||||
|
||||
for child_name, child_description in root_spec["children"]:
|
||||
self._upsert_category(
|
||||
name=child_name,
|
||||
description=child_description,
|
||||
parent=root,
|
||||
)
|
||||
child_count += 1
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.WARNING("Dry run only. No categories were changed."))
|
||||
return
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f"Blog categories synchronized: {root_count} roots, {child_count} children."))
|
||||
|
||||
def _upsert_category(self, *, name: str, description: str, parent: Category | None) -> Category:
|
||||
if parent and parent.parent_id:
|
||||
raise ValueError(f"Invalid category tree: parent '{parent.name}' is not a root category.")
|
||||
|
||||
category, _ = Category.all_objects.update_or_create(
|
||||
name=name,
|
||||
defaults={
|
||||
"description": description,
|
||||
"parent": parent,
|
||||
"is_deleted": False,
|
||||
"deleted_at": None,
|
||||
},
|
||||
)
|
||||
return category
|
||||
111
apps/blog/management/commands/seed_blog_tags.py
Normal file
111
apps/blog/management/commands/seed_blog_tags.py
Normal file
@@ -0,0 +1,111 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from apps.blog.models import Tag
|
||||
|
||||
|
||||
TAGS = [
|
||||
"انجمن علمی",
|
||||
"دانشکده",
|
||||
"اطلاعیه",
|
||||
"رویداد",
|
||||
"کارگاه",
|
||||
"گزارش رویداد",
|
||||
"برنامهنویسی",
|
||||
"پایتون",
|
||||
"جاوااسکریپت",
|
||||
"تایپاسکریپت",
|
||||
"جاوا",
|
||||
"سیپلاسپلاس",
|
||||
"گولنگ",
|
||||
"فرانتاند",
|
||||
"بکاند",
|
||||
"React",
|
||||
"Next.js",
|
||||
"Django",
|
||||
"REST API",
|
||||
"پایگاه داده",
|
||||
"PostgreSQL",
|
||||
"Redis",
|
||||
"گیت",
|
||||
"لینوکس",
|
||||
"Docker",
|
||||
"DevOps",
|
||||
"استقرار",
|
||||
"امنیت",
|
||||
"شبکه",
|
||||
"سیستمعامل",
|
||||
"الگوریتم",
|
||||
"ساختمان داده",
|
||||
"برنامهنویسی رقابتی",
|
||||
"حل مسئله",
|
||||
"هوش مصنوعی",
|
||||
"یادگیری ماشین",
|
||||
"یادگیری عمیق",
|
||||
"علم داده",
|
||||
"تحلیل داده",
|
||||
"دادهکاوی",
|
||||
"پردازش زبان طبیعی",
|
||||
"بینایی ماشین",
|
||||
"پژوهش",
|
||||
"مقالهخوانی",
|
||||
"پروژه دانشجویی",
|
||||
"پروژه درسی",
|
||||
"تیمسازی",
|
||||
"مدیریت پروژه",
|
||||
"طراحی نرمافزار",
|
||||
"معماری نرمافزار",
|
||||
"تست نرمافزار",
|
||||
"تجربه کاربری",
|
||||
"طراحی رابط کاربری",
|
||||
"اپنسورس",
|
||||
"کارآموزی",
|
||||
"رزومه",
|
||||
"مصاحبه",
|
||||
"مسیر شغلی",
|
||||
"منابع یادگیری",
|
||||
"کتاب",
|
||||
"دوره آموزشی",
|
||||
"تجربه دانشجویی",
|
||||
"انتخاب واحد",
|
||||
"امتحانات",
|
||||
"آموزش",
|
||||
"راهنمای شروع",
|
||||
]
|
||||
|
||||
|
||||
def console_safe(value: str) -> str:
|
||||
encoding = sys.stdout.encoding or "utf-8"
|
||||
return value.encode(encoding, errors="backslashreplace").decode(encoding)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Create or update production blog tags for the CS association blog."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("--dry-run", action="store_true", help="Print planned tags without writing changes.")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
dry_run = options["dry_run"]
|
||||
|
||||
if dry_run:
|
||||
for name in TAGS:
|
||||
self.stdout.write(console_safe(f"[tag] {name}"))
|
||||
self.stdout.write(self.style.WARNING("Dry run only. No tags were changed."))
|
||||
return
|
||||
|
||||
count = 0
|
||||
for name in TAGS:
|
||||
Tag.all_objects.update_or_create(
|
||||
name=name,
|
||||
defaults={
|
||||
"is_deleted": False,
|
||||
"deleted_at": None,
|
||||
},
|
||||
)
|
||||
count += 1
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f"Blog tags synchronized: {count} tags."))
|
||||
Reference in New Issue
Block a user