from typing import List, Optional from pydantic import BaseModel, Field, HttpUrl from pydantic_settings import BaseSettings class Settings(BaseSettings): """Application settings with environment variable support.""" anthropic_api_key: str = Field(..., validation_alias="ANTHROPIC_API_KEY") unsplash_api_key: str = Field(..., validation_alias="UNSPLASH_API_KEY") class Config: env_file = ".env" env_file_encoding = "utf-8" class UnsplashImage(BaseModel): """Unsplash image data.""" url: HttpUrl photographer: str local_path: Optional[str] = None class GermanWord(BaseModel): """German word data structure.""" german_word: str part_of_speech: str = Field(..., description="Noun, Verb, Adjective, etc.") english_meaning: str article: Optional[str] = Field(None, description="der/die/das for nouns") plural_form: str example_sentence: str sentence_translation: str usage_notes: str related_words: str image_search_term: str tags: List[str] = Field(default_factory=list) def is_noun(self) -> bool: """Check if the word is a noun.""" return self.part_of_speech.lower() == "noun" def get_full_word(self) -> str: """Get the full word with article if it's a noun.""" if self.is_noun() and self.article: return f"{self.article} {self.german_word}" return self.german_word class AnkiCard(BaseModel): """Anki card data structure.""" word: GermanWord image: Optional[UnsplashImage] = None source: str = Field(..., description="Source of the word (Babbel, Duolingo, etc.)") def to_fields(self) -> List[str]: """Convert to Anki note fields.""" return [ self.word.german_word, self.word.part_of_speech, self.source, self.word.english_meaning, self.word.article or "", self.word.plural_form, self.word.example_sentence, self.word.sentence_translation, self.word.usage_notes, self.word.related_words, self.image.local_path if self.image else "", self.image.photographer if self.image else "", " ".join(self.word.tags) ]