Pavaas commited on
Commit
783a18c
Β·
verified Β·
1 Parent(s): ca5d184

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +145 -12
app.py CHANGED
@@ -1,20 +1,153 @@
1
  import streamlit as st
2
  import os
3
  import tempfile
4
- import fitz
5
- from config import (
6
- process_pdf,
7
- process_text,
8
- process_image,
9
- process_audio,
10
- process_youtube,
11
- generate_flashcards,
12
- export_to_apkg,
13
- export_to_csv,
14
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  st.set_page_config(page_title="BatAnki AI", layout="wide")
17
- st.title("πŸ“š BatAnki – AI Flashcard Generator")
18
 
19
  st.sidebar.markdown("πŸ“ Input Options")
20
  uploaded_file = st.sidebar.file_uploader("Upload file", type=["pdf", "txt", "docx", "jpg", "png", "mp3", "wav"])
 
1
  import streamlit as st
2
  import os
3
  import tempfile
4
+ import uuid
5
+ import fitz # PyMuPDF
6
+ import easyocr
7
+ import whisper
8
+ import docx
9
+ import yt_dlp
10
+ import csv
11
+ import genanki
12
+ from transformers import pipeline
13
+
14
+ # === Helper Functions ===
15
+
16
+ def process_pdf(path):
17
+ text = ""
18
+ doc = fitz.open(path)
19
+ reader = easyocr.Reader(['en'], gpu=False)
20
+ for page in doc:
21
+ t = page.get_text()
22
+ if t.strip():
23
+ text += t
24
+ else:
25
+ pix = page.get_pixmap()
26
+ img_path = f"/tmp/{uuid.uuid4()}.png"
27
+ pix.save(img_path)
28
+ result = reader.readtext(img_path, detail=0)
29
+ text += "\n".join(result)
30
+ return text
31
+
32
+ def process_image(path):
33
+ reader = easyocr.Reader(['en'], gpu=False)
34
+ result = reader.readtext(path, detail=0)
35
+ return "\n".join(result)
36
+
37
+ def process_audio(path):
38
+ model = whisper.load_model("base")
39
+ result = model.transcribe(path)
40
+ return result["text"]
41
+
42
+ def process_text(path):
43
+ if path.endswith(".txt"):
44
+ with open(path, "r", encoding="utf-8") as f:
45
+ return f.read()
46
+ elif path.endswith(".docx"):
47
+ doc = docx.Document(path)
48
+ return "\n".join([para.text for para in doc.paragraphs])
49
+ return ""
50
+
51
+ def process_youtube(url):
52
+ temp_dir = tempfile.gettempdir()
53
+ audio_path = os.path.join(temp_dir, f"{uuid.uuid4()}.mp3")
54
+ ydl_opts = {
55
+ 'format': 'bestaudio/best',
56
+ 'outtmpl': audio_path,
57
+ 'postprocessors': [{
58
+ 'key': 'FFmpegExtractAudio',
59
+ 'preferredcodec': 'mp3',
60
+ 'preferredquality': '192',
61
+ }],
62
+ 'quiet': True,
63
+ }
64
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
65
+ ydl.download([url])
66
+ return process_audio(audio_path)
67
+
68
+ @st.cache_resource
69
+ def load_llm_swarm():
70
+ return {
71
+ "fast": pipeline("text2text-generation", model="google/flan-t5-small"),
72
+ "bio": pipeline("text2text-generation", model="mrm8488/t5-base-finetuned-question-generation-ap"),
73
+ "deep": pipeline("text2text-generation", model="google/flan-t5-base"),
74
+ "mistral": pipeline("text2text-generation", model="google/flan-t5-large"),
75
+ "fallback": pipeline("text2text-generation", model="MBZUAI/LaMini-Flan-T5-248M")
76
+ }
77
+
78
+ def generate_flashcards(text, types=["Q&A"], max_cards=100):
79
+ from random import choice
80
+ llm_swarm = load_llm_swarm()
81
+ chunks = [text[i:i + 400] for i in range(0, len(text), 400)][:max_cards]
82
+ prompts, tags = [], []
83
+
84
+ for chunk in chunks:
85
+ if "Q&A" in types:
86
+ prompts.append(f"Generate a question and answer:\n{chunk}")
87
+ tags.append("Q&A")
88
+ if "Cloze" in types:
89
+ prompts.append(f"Make a cloze deletion from:\n{chunk}")
90
+ tags.append("Cloze")
91
+ if "MCQ" in types:
92
+ prompts.append(f"Generate a multiple choice question:\n{chunk}")
93
+ tags.append("MCQ")
94
+ if "Reverse" in types:
95
+ prompts.append(f"Generate a question and answer:\n{chunk}")
96
+ tags.append("Reverse")
97
+
98
+ cards = []
99
+ for i, prompt in enumerate(prompts):
100
+ engine_name = choice(list(llm_swarm.keys()))
101
+ engine = llm_swarm[engine_name]
102
+ tag = tags[i]
103
+ try:
104
+ output = engine(prompt, max_length=128)[0]["generated_text"]
105
+ except:
106
+ output = llm_swarm["fallback"](prompt, max_length=64)[0]["generated_text"]
107
+
108
+ if tag in ["Q&A", "Reverse"]:
109
+ q, a = (output.split(":", 1) + [""])[:2]
110
+ if tag == "Reverse":
111
+ q, a = a.strip(), q.strip()
112
+ cards.append({"question": q.strip(), "answer": a.strip(), "tag": tag})
113
+ elif tag == "Cloze":
114
+ cards.append({"question": output.strip(), "answer": "[...]", "tag": tag})
115
+ elif tag == "MCQ":
116
+ cards.append({"question": output.strip(), "answer": "Choose best option", "tag": tag})
117
+
118
+ return cards
119
+
120
+ def export_to_csv(cards, filename="batanki_cards.csv"):
121
+ with open(filename, "w", newline="", encoding="utf-8") as f:
122
+ writer = csv.writer(f)
123
+ writer.writerow(["Question", "Answer", "Type"])
124
+ for card in cards:
125
+ writer.writerow([card["question"], card["answer"], card["tag"]])
126
+
127
+ def export_to_apkg(cards, deck_name="BatAnkiDeck"):
128
+ deck_id = int(uuid.uuid4()) >> 64
129
+ model = genanki.Model(
130
+ 1607392319,
131
+ "BatAnkiModel",
132
+ fields=[{"name": "Question"}, {"name": "Answer"}],
133
+ templates=[{
134
+ "name": "Card 1",
135
+ "qfmt": "{{Question}}",
136
+ "afmt": "{{FrontSide}}<hr id='answer'>{{Answer}}",
137
+ }]
138
+ )
139
+ deck = genanki.Deck(deck_id, deck_name)
140
+ for card in cards:
141
+ note = genanki.Note(model=model, fields=[card["question"], card["answer"]])
142
+ deck.add_note(note)
143
+ output_path = f"{deck_name}.apkg"
144
+ genanki.Package(deck).write_to_file(output_path)
145
+ return output_path
146
+
147
+ # === Streamlit UI ===
148
 
149
  st.set_page_config(page_title="BatAnki AI", layout="wide")
150
+ st.title("πŸ¦‡ BatAnki – AI Flashcard Generator")
151
 
152
  st.sidebar.markdown("πŸ“ Input Options")
153
  uploaded_file = st.sidebar.file_uploader("Upload file", type=["pdf", "txt", "docx", "jpg", "png", "mp3", "wav"])