feat: Refactor outlets with multilingual support and dynamic queries

- Replace static articles array with dynamic source_keyword queries
- Use MongoDB _id as unique identifier for outlets
- Add multilingual translations (9 languages: ko, en, zh_cn, zh_tw, ja, fr, de, es, it)
- Add OutletService for database operations
- Add outlet migration script with Korean source_keyword matching
- Remove JSON file-based outlet loading
- Add /outlets/{outlet_id}/articles endpoint for dynamic article retrieval

This resolves the design issues with:
1. Static articles array requiring constant updates
2. Lack of multilingual support for outlet names/descriptions
3. Broken image URLs
4. Korean entity matching for article queries

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jungwoo choi
2025-10-13 16:52:34 +09:00
parent deb52e51f2
commit e467e76d02
6 changed files with 515 additions and 28 deletions

View File

@ -0,0 +1,47 @@
from pydantic import BaseModel, Field
from typing import List, Optional, Dict
class OutletTranslations(BaseModel):
ko: Optional[str] = None
en: Optional[str] = None
zh_cn: Optional[str] = None
zh_tw: Optional[str] = None
ja: Optional[str] = None
fr: Optional[str] = None
de: Optional[str] = None
es: Optional[str] = None
it: Optional[str] = None
class OutletBase(BaseModel):
source_keyword: str # Used to query articles dynamically
category: str # people, topics, companies
name_translations: OutletTranslations = Field(default_factory=lambda: OutletTranslations())
description_translations: OutletTranslations = Field(default_factory=lambda: OutletTranslations())
image: Optional[str] = None
# Deprecated - kept for backward compatibility during migration
name: Optional[str] = None
description: Optional[str] = None
class OutletCreate(OutletBase):
pass
class OutletUpdate(BaseModel):
source_keyword: Optional[str] = None
category: Optional[str] = None
name_translations: Optional[OutletTranslations] = None
description_translations: Optional[OutletTranslations] = None
image: Optional[str] = None
# Deprecated
name: Optional[str] = None
description: Optional[str] = None
articles: Optional[List[str]] = None
class Outlet(OutletBase):
class Config:
from_attributes = True
class OutletList(BaseModel):
outlets: List[Outlet]
total: int