pranshu dhiman
Initial commit with Docker and Streamlit
46b701f
Raw
History Blame Contribute Delete
7.24 kB
from __future__ import annotations
import tempfile
from pathlib import Path
import streamlit as st
from src.flashcard_generator.exporters import flashcards_to_apkg, flashcards_to_csv
from src.flashcard_generator.extraction import extract_pdf_pages, extract_text
from src.flashcard_generator.models import GenerationSettings
from src.flashcard_generator.page_flashcards import format_page_flashcards, generate_page_flashcards
from src.flashcard_generator.pipeline import FlashcardPipeline
from src.flashcard_generator.text_processing import token_count
st.set_page_config(page_title="AI Flashcard Generator", layout="wide")
def main() -> None:
st.title("AI-Powered Flashcard Generator")
st.caption("Upload lecture notes, generate revision cards, and export an Anki-compatible deck.")
with st.sidebar:
st.header("Generation")
model_name = st.selectbox(
"Hugging Face model",
["google/flan-t5-small", "facebook/bart-large-cnn", "t5-small"],
index=0,
)
use_model = st.toggle("Use Hugging Face summariser", value=True)
generate_all_possible = st.toggle("Generate all possible Q/A", value=False)
if generate_all_possible:
st.caption("Uses every detected concept and every question style.")
concepts_per_chunk = 0
cards_per_concept = 10
else:
concepts_per_chunk = st.slider("Concepts per chunk", 3, 20, 8)
cards_per_concept = st.slider("Questions per concept", 1, 10, 3)
harden_questions = st.toggle("Rewrite with Bloom's Taxonomy", value=True)
uploaded = st.file_uploader(
"Upload a PDF, text file, or lecture-note image",
type=["pdf", "txt", "md", "png", "jpg", "jpeg", "webp", "tiff", "bmp"],
)
if uploaded is None:
st.info("Choose a lecture file to begin.")
return
suffix = Path(uploaded.name).suffix
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temporary:
temporary.write(uploaded.getbuffer())
temp_path = Path(temporary.name)
is_pdf = suffix.lower() == ".pdf"
page_texts: list[str] = []
try:
with st.spinner("Extracting text..."):
if is_pdf:
page_texts = extract_pdf_pages(temp_path)
text = "\n".join(page_texts)
else:
text = extract_text(temp_path)
except Exception as exc:
st.error(f"Could not extract text: {exc}")
return
finally:
temp_path.unlink(missing_ok=True)
if not text:
st.warning("No readable text was found in the uploaded file.")
return
st.subheader("Extracted Notes")
metric_left, metric_right = st.columns(2)
metric_left.metric("Words", token_count(text))
metric_right.metric("Pages" if is_pdf else "Characters", len(page_texts) if is_pdf else len(text))
with st.expander("Preview extracted text", expanded=False):
st.write(text[:5000])
if not st.button("Generate Flashcards", type="primary"):
return
if is_pdf:
with st.spinner("Building page-by-page flashcards..."):
page_sets = generate_page_flashcards(page_texts, questions_per_page=10)
formatted_output = format_page_flashcards(page_sets)
cards = [card for page_set in page_sets for card in page_set.cards]
if not cards:
st.warning("No flashcards could be generated from this PDF.")
return
st.success(f"Generated {len(cards)} flashcards from {len(page_sets)} page(s).")
tab_output, tab_cards, tab_export = st.tabs(["Formatted Output", "Flashcards", "Export"])
with tab_output:
st.text(formatted_output)
with tab_cards:
for page_set in page_sets:
st.markdown(f"**Page {page_set.page_number}**")
for index, card in enumerate(page_set.cards, start=1):
with st.container(border=True):
st.markdown(f"**Q{index}: {card.question}**")
st.write(f"A{index}: {card.short_answer}")
if page_set.insufficient_note:
st.caption(page_set.insufficient_note)
with tab_export:
st.download_button(
"Download TXT",
data=formatted_output,
file_name="page_flashcards.txt",
mime="text/plain",
)
csv_data = flashcards_to_csv(cards)
st.download_button(
"Download CSV for Anki",
data=csv_data,
file_name="flashcards.csv",
mime="text/csv",
)
try:
apkg_data = flashcards_to_apkg(cards)
st.download_button(
"Download APKG",
data=apkg_data,
file_name="flashcards.apkg",
mime="application/octet-stream",
)
except Exception as exc:
st.caption(f"APKG export unavailable: {exc}")
return
settings = GenerationSettings(
concepts_per_chunk=concepts_per_chunk,
cards_per_concept=cards_per_concept,
generate_all_possible=generate_all_possible,
harden_questions=harden_questions,
model_name=model_name,
use_model=use_model,
)
with st.spinner("Summarising notes and building flashcards..."):
pipeline = FlashcardPipeline(settings)
cards, summaries, concepts = pipeline.run(text)
if not cards:
st.warning("No flashcards could be generated from this document.")
return
st.success(f"Generated {len(cards)} flashcards from {len(summaries)} chunk(s).")
tab_cards, tab_summaries, tab_concepts, tab_export = st.tabs(
["Flashcards", "Summaries", "Concepts JSON", "Export"]
)
with tab_cards:
for index, card in enumerate(cards, start=1):
with st.container(border=True):
st.markdown(f"**{index}. {card.question}**")
st.markdown("**Short answer**")
st.write(card.short_answer)
with st.expander("Long answer", expanded=False):
st.write(card.long_answer)
st.caption(f"{card.concept} - {card.difficulty} - {card.bloom_level}")
with tab_summaries:
for index, summary in enumerate(summaries, start=1):
st.markdown(f"**Chunk {index}**")
st.write(summary)
with tab_concepts:
st.json(concepts)
with tab_export:
csv_data = flashcards_to_csv(cards)
st.download_button(
"Download CSV for Anki",
data=csv_data,
file_name="flashcards.csv",
mime="text/csv",
)
try:
apkg_data = flashcards_to_apkg(cards)
st.download_button(
"Download APKG",
data=apkg_data,
file_name="flashcards.apkg",
mime="application/octet-stream",
)
except Exception as exc:
st.caption(f"APKG export unavailable: {exc}")
if __name__ == "__main__":
main()