File size: 5,030 Bytes
de189a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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}}<hr id=answer>{{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}}<br>{{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()