132 lines
5.2 KiB
Python
132 lines
5.2 KiB
Python
import os
|
|
import random
|
|
import genanki
|
|
from datetime import datetime
|
|
from typing import List, Tuple, Optional
|
|
from .models import GermanWord, UnsplashImage
|
|
from .clients.llm import AnthropicClient
|
|
from .clients.unsplash import UnsplashClient
|
|
|
|
class GermanDeckPackage(genanki.Package):
|
|
"""Custom Package class to include media files"""
|
|
def __init__(self, deck, media_files):
|
|
super().__init__(deck)
|
|
self.media_files = media_files
|
|
|
|
class CardGenerator:
|
|
def __init__(self, llm_client: AnthropicClient, unsplash_client: UnsplashClient):
|
|
self.llm_client = llm_client
|
|
self.unsplash_client = unsplash_client
|
|
self.model = self._create_model()
|
|
|
|
def _create_model(self):
|
|
"""Create the Anki note model"""
|
|
return genanki.Model(
|
|
random.randrange(1 << 30, 1 << 31),
|
|
'German Vocabulary Model',
|
|
fields=[
|
|
{'name': 'German_Word'},
|
|
{'name': 'Part_of_Speech'},
|
|
{'name': 'Source'},
|
|
{'name': 'English_Meaning'},
|
|
{'name': 'Article'},
|
|
{'name': 'Plural_Form'},
|
|
{'name': 'Example_Sentence'},
|
|
{'name': 'Sentence_Translation'},
|
|
{'name': 'Usage_Notes'},
|
|
{'name': 'Related_Words'},
|
|
{'name': 'Image'},
|
|
{'name': 'Image_Credit'},
|
|
{'name': 'Tags'}
|
|
],
|
|
templates=[
|
|
{
|
|
'name': 'German Vocabulary Card',
|
|
'qfmt': '''
|
|
<div style="font-size: 24px;">{{German_Word}}</div>
|
|
<div style="font-size: 18px;">Part of speech: {{Part_of_Speech}}</div>
|
|
<div style="font-size: 14px;">Source: {{Source}}</div>
|
|
{{#Image}}<div><img src="{{text:Image}}" style="max-width: 300px; max-height: 200px;"></div>{{/Image}}
|
|
''',
|
|
'afmt': '''
|
|
{{FrontSide}}
|
|
<hr id="answer">
|
|
<div><b>English meaning:</b> {{English_Meaning}}</div>
|
|
<div><b>Article:</b> {{Article}}</div>
|
|
<div><b>Plural Form:</b> {{Plural_Form}}</div>
|
|
<div><b>Example:</b> {{Example_Sentence}}</div>
|
|
<div><b>Translation:</b> {{Sentence_Translation}}</div>
|
|
<div><b>Usage notes:</b> {{Usage_Notes}}</div>
|
|
<div><b>Related words:</b> {{Related_Words}}</div>
|
|
{{#Image_Credit}}<div><small>Photo: {{Image_Credit}}</small></div>{{/Image_Credit}}
|
|
<div><small>Tags: {{Tags}}</small></div>
|
|
'''
|
|
}
|
|
],
|
|
css='''
|
|
.card {
|
|
font-family: arial;
|
|
font-size: 16px;
|
|
text-align: center;
|
|
color: black;
|
|
background-color: white;
|
|
}
|
|
img {
|
|
margin: 20px auto;
|
|
border-radius: 5px;
|
|
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
|
}
|
|
'''
|
|
)
|
|
|
|
def create_deck(self, word_list: List[Tuple[str, str]], deck_name: str = "German Vocabulary") -> Tuple[genanki.Deck, List[str]]:
|
|
"""Create an Anki deck from a list of words"""
|
|
deck_id = random.randrange(1 << 30, 1 << 31)
|
|
deck = genanki.Deck(deck_id, deck_name)
|
|
media_files = []
|
|
|
|
for word, source in word_list:
|
|
try:
|
|
card_info_dict = self.llm_client.get_card_info(word, source)
|
|
card_info = GermanWord(**card_info_dict)
|
|
|
|
# Get image
|
|
image_filename = f"media/{word.lower().replace(' ', '_')}.jpg"
|
|
image = self.unsplash_client.get_image(
|
|
card_info.image_search_term,
|
|
image_filename
|
|
)
|
|
|
|
if image and image.local_path:
|
|
media_files.append(image.local_path)
|
|
image_filename = os.path.basename(image.local_path)
|
|
else:
|
|
image_filename = ""
|
|
|
|
# Create note
|
|
note = genanki.Note(
|
|
model=self.model,
|
|
fields=[
|
|
card_info.german_word,
|
|
card_info.part_of_speech,
|
|
source,
|
|
card_info.english_meaning,
|
|
card_info.article or "",
|
|
card_info.plural_form,
|
|
card_info.example_sentence,
|
|
card_info.sentence_translation,
|
|
card_info.usage_notes,
|
|
card_info.related_words,
|
|
image_filename,
|
|
image.photographer if image else "",
|
|
' '.join(card_info.tags)
|
|
]
|
|
)
|
|
deck.add_note(note)
|
|
print(f"Added card for: {word}")
|
|
|
|
except Exception as e:
|
|
print(f"Error creating card for {word}: {str(e)}")
|
|
|
|
return deck, media_files
|