from __future__ import annotations import json import random import tempfile from pathlib import Path from typing import Optional import gradio as gr from agent import create_anki_deck try: import genanki # type: ignore GENANKI_AVAILABLE = True except ImportError: GENANKI_AVAILABLE = False # --------------------------------------------------------------------------- # generate .apkg or JSON) # --------------------------------------------------------------------------- def _build_package_file(deck_dict: dict, deck_name_override: str | None = None) -> str: # ← θΏ”ε›ž str deck_title = (deck_name_override or deck_dict.get("name") or "AI_Deck").strip() if GENANKI_AVAILABLE: # ---------- generate .apkg ---------- import genanki basic_model = genanki.Model( 1607392319, "AI Basic Model", fields=[{"name": "Front"}, {"name": "Back"}], templates=[{ "name": "Card 1", "qfmt": "{{Front}}", "afmt": "{{FrontSide}}
{{Back}}", }], ) cloze_model = genanki.Model( 1091735104, "AI Cloze Model", model_type=genanki.Model.CLOZE, fields=[{"name": "Text"}, {"name": "Back"}], templates=[{ "name": "Cloze Card", "qfmt": "{{cloze:Text}}", "afmt": "{{cloze:Text}}
{{Back}}", }], ) deck = genanki.Deck(random.getrandbits(32), deck_title) tags = deck_dict.get("tags", []) for card in deck_dict["cards"]: model = cloze_model if card["type"].lower().startswith("cloze") else basic_model note = genanki.Note(model=model, fields=[card["front"], card["back"]], tags=tags) deck.add_note(note) pkg = genanki.Package(deck) tmpf = tempfile.NamedTemporaryFile(delete=False, suffix=".apkg") pkg.write_to_file(tmpf.name) return tmpf.name # ---------- generate JSON ---------- with tempfile.NamedTemporaryFile(delete=False, suffix=".json", mode="w", encoding="utf-8") as tf: json.dump(deck_dict, tf, ensure_ascii=False, indent=2) return tf.name # --------------------------------------------------------------------------- # callback function to generate Anki deck # --------------------------------------------------------------------------- def generate_deck( uploaded_path: Optional[str], url_input: str, card_type_pref: str, deck_name_pref: str, tags_pref: str, user_req_pref: str, ): if not uploaded_path and not url_input.strip(): raise gr.Error("Please upload a file or enter a URL.") params: dict[str, object] = { "card_types": card_type_pref, "user_requirements": user_req_pref, } if uploaded_path: path = Path(uploaded_path) params["pdf_file" if path.suffix.lower() == ".pdf" else "img_file"] = path else: params["url"] = url_input.strip() # agent deck_dict = create_anki_deck(**params)["deck"] if deck_name_pref.strip(): deck_dict["name"] = deck_name_pref.strip() if tags_pref.strip(): deck_dict["tags"] = [t.strip() for t in tags_pref.split(",") if t.strip()] output_path = _build_package_file(deck_dict, deck_name_pref) return str(output_path) # --------------------------------------------------------------------------- # Gradio UI # --------------------------------------------------------------------------- demo = gr.Blocks(theme=gr.themes.Soft(), title="Anki Card Generator") with demo: gr.Markdown("# 🧠 Anki Card Generator") gr.Markdown("Upload an image or PDF, or enter a URL to generate Anki cards.") with gr.Row(): with gr.Column(scale=1): file_input = gr.File(label="πŸ“ Upload PDF or image", file_types=["image", ".pdf"], type="filepath") url_input = gr.Textbox(label="🌐 Or enter url", placeholder="https://example.com/article") user_requirements_input = gr.Textbox(label="🎯 Your requirements", lines=4) card_type_input = gr.Textbox(label="πŸ“‡ Card type", value="Basic,Cloze") deck_name_input = gr.Textbox(label="🏷️ Deck name", value="Animal") tags_input = gr.Textbox(label="✨ Tags (comma-separated)", placeholder="e.g. biology, mammals", value="biology,mammals") generate_button = gr.Button("Generate", variant="primary") with gr.Column(scale=1): anki_output_file = gr.File(label="πŸ“₯ Download (.apkg / .json)") generate_button.click( fn=generate_deck, inputs=[file_input, url_input, card_type_input, deck_name_input, tags_input, user_requirements_input], outputs=[anki_output_file], ) gr.Markdown("---") gr.Markdown("βœ… genanki :{}".format("Available" if GENANKI_AVAILABLE else "Not installed (will export JSON)")) if __name__ == "__main__": demo.launch()