105 lines
3.4 KiB
Python
105 lines
3.4 KiB
Python
|
|
import re
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
from django.conf import settings
|
||
|
|
from django.core.management.base import BaseCommand, CommandError
|
||
|
|
|
||
|
|
from api.models import Aspect, Hero, Item
|
||
|
|
|
||
|
|
# Add data directory to path for imports
|
||
|
|
sys.path.insert(0, str(Path(settings.BASE_DIR) / "data"))
|
||
|
|
from facets import HERO_FACETS
|
||
|
|
|
||
|
|
|
||
|
|
def _strip_comments(text: str) -> str:
|
||
|
|
"""Remove single-line and block comments from TypeScript data."""
|
||
|
|
text = re.sub(r"/\*.*?\*/", "", text, flags=re.S)
|
||
|
|
lines = []
|
||
|
|
for line in text.splitlines():
|
||
|
|
stripped = line.strip()
|
||
|
|
if stripped.startswith("//"):
|
||
|
|
continue
|
||
|
|
lines.append(line)
|
||
|
|
return "\n".join(lines)
|
||
|
|
|
||
|
|
|
||
|
|
def _extract_objects(text: str, require_primary: bool = False):
|
||
|
|
cleaned = _strip_comments(text)
|
||
|
|
objects = []
|
||
|
|
|
||
|
|
for match in re.finditer(r"\{[^}]*\}", cleaned):
|
||
|
|
chunk = match.group(0)
|
||
|
|
entry = {}
|
||
|
|
id_match = re.search(r"['\"]?id['\"]?\s*:\s*(['\"])(.*?)\1", chunk)
|
||
|
|
name_match = re.search(r"['\"]?name['\"]?\s*:\s*(['\"])(.*?)\1", chunk)
|
||
|
|
if not (id_match and name_match):
|
||
|
|
continue
|
||
|
|
entry["slug"] = id_match.group(2)
|
||
|
|
entry["name"] = name_match.group(2)
|
||
|
|
|
||
|
|
if require_primary:
|
||
|
|
primary_match = re.search(r"['\"]?primary['\"]?\s*:\s*(['\"])(.*?)\1", chunk)
|
||
|
|
if not primary_match:
|
||
|
|
continue
|
||
|
|
entry["primary"] = primary_match.group(2)
|
||
|
|
|
||
|
|
if len(entry) == (3 if require_primary else 2):
|
||
|
|
objects.append(entry)
|
||
|
|
|
||
|
|
return objects
|
||
|
|
|
||
|
|
|
||
|
|
class Command(BaseCommand):
|
||
|
|
help = "Load heroes, items and facets from data files into the database."
|
||
|
|
|
||
|
|
def handle(self, *args, **options):
|
||
|
|
base_dir = Path(settings.BASE_DIR)
|
||
|
|
heroes_path = base_dir / "data" / "heroes.ts"
|
||
|
|
items_path = base_dir / "data" / "items.ts"
|
||
|
|
|
||
|
|
if not heroes_path.exists() or not items_path.exists():
|
||
|
|
raise CommandError("Missing data files in the ./data directory.")
|
||
|
|
|
||
|
|
heroes = _extract_objects(heroes_path.read_text(encoding="utf-8"), require_primary=True)
|
||
|
|
items = _extract_objects(items_path.read_text(encoding="utf-8"), require_primary=False)
|
||
|
|
|
||
|
|
if not heroes:
|
||
|
|
raise CommandError("Failed to parse heroes.ts")
|
||
|
|
if not items:
|
||
|
|
raise CommandError("Failed to parse items.ts")
|
||
|
|
|
||
|
|
hero_created = item_created = aspect_created = 0
|
||
|
|
|
||
|
|
for hero in heroes:
|
||
|
|
hero_obj, created = Hero.objects.update_or_create(
|
||
|
|
name=hero["name"],
|
||
|
|
defaults={
|
||
|
|
"primary": hero["primary"],
|
||
|
|
},
|
||
|
|
)
|
||
|
|
hero_created += int(created)
|
||
|
|
|
||
|
|
# Load facets for this hero
|
||
|
|
facets = HERO_FACETS.get(hero["name"], [])
|
||
|
|
for facet_name in facets:
|
||
|
|
_, facet_created = Aspect.objects.update_or_create(
|
||
|
|
hero=hero_obj,
|
||
|
|
name=facet_name,
|
||
|
|
)
|
||
|
|
aspect_created += int(facet_created)
|
||
|
|
|
||
|
|
for item in items:
|
||
|
|
_, created = Item.objects.update_or_create(
|
||
|
|
name=item["name"],
|
||
|
|
)
|
||
|
|
item_created += int(created)
|
||
|
|
|
||
|
|
self.stdout.write(
|
||
|
|
self.style.SUCCESS(
|
||
|
|
f"Loaded {len(heroes)} heroes ({hero_created} new), "
|
||
|
|
f"{len(items)} items ({item_created} new), "
|
||
|
|
f"and {aspect_created} new facets."
|
||
|
|
)
|
||
|
|
)
|