Trivia5 / modules /flashcard_generator.py
Bharath370's picture
Upload 60 files
44073c9 verified
# modules/flashcard_generator.py
"""Enhanced Flashcard Generator Module"""
from typing import Dict, List, Optional
from modules.api_utils import (
fetch_wikipedia_summary,
fetch_wikibooks_content,
fetch_wikipedia_categories,
fetch_related_topics,
)
import random
import hashlib # For generating unique IDs
def _generate_item_id(content: str) -> str:
"""Generates a unique ID for a flashcard based on its content."""
return hashlib.sha256(content.encode()).hexdigest()
def generate_flashcard_types(topic: str, difficulty: str) -> Dict:
"""Generate different types of flashcards based on difficulty"""
summary_data = fetch_wikipedia_summary(topic)
if not summary_data:
return {"error": "Topic not found", "status": False}
title = summary_data.get("title", topic)
extract = summary_data.get("extract", "")
description = summary_data.get("description", "")
# Also try to get Wikibooks content for educational material
wikibooks_content = fetch_wikibooks_content(topic)
flashcard_types = {
"Easy": {
"front": f"What is {title}?",
"back": extract[:200] + "..." if len(extract) > 200 else extract,
"type": "definition",
},
"Medium": {
"front": f"Explain the key concepts of {title}",
"back": extract[:300] + "..." if len(extract) > 300 else extract,
"type": "conceptual",
},
"Hard": {
"front": f"How does {title} relate to its field and what are its applications?",
"back": extract
+ (
f"\n\nAdditional context: {wikibooks_content[:200]}"
if wikibooks_content
else ""
),
"type": "analytical",
},
}
flashcard = flashcard_types.get(difficulty, flashcard_types["Easy"])
# Add categories as tags
categories = fetch_wikipedia_categories(title)
# Generate item_id
item_id = _generate_item_id(flashcard["front"] + flashcard["back"])
return {
"item_id": item_id, # Add unique ID
"front": flashcard["front"],
"back": flashcard["back"],
"hint": description,
"topic": title,
"difficulty": difficulty,
"type": flashcard["type"],
"tags": categories[:3] if categories else [],
"status": True,
}
def generate_flashcard(topic: str, difficulty: str = "Medium", adaptive_engine=None) -> Dict:
"""Generate an enhanced flashcard for a given topic, prioritizing review items."""
# First, check for items due for review if adaptive engine is provided
if adaptive_engine:
review_item_ids = adaptive_engine.get_items_due_for_review(topic=topic, limit=1)
if review_item_ids:
# If a review item is found, we need to reconstruct the flashcard data
# This assumes that the item_id is sufficient to retrieve the original flashcard content
# For simplicity, we'll generate a new one for that topic, ideally, you'd load it from storage
# For this implementation, we'll try to generate a flashcard for that specific item's topic
# A more robust solution would store flashcard content with the item_id in the AdaptiveEngine data
# For now, let's just use the topic associated with the item_id in adaptive engine
# This requires adaptive engine to store topic with item_id
item_data = adaptive_engine.data["item_performance"].get(review_item_ids[0])
if item_data and item_data.get("topic"):
return generate_flashcard_types(item_data["topic"], difficulty)
# If no review items, or if adaptive_engine is not provided, generate a new flashcard
return generate_flashcard_types(topic, difficulty)
def generate_flashcard_deck(
topics: List[str], difficulty: str = "Medium", adaptive_engine=None
) -> List[Dict]:
"""Generate a deck of flashcards, incorporating spaced repetition."""
deck = []
# Prioritize review items if adaptive engine is provided
if adaptive_engine:
review_item_ids = adaptive_engine.get_items_due_for_review(limit=len(topics) * 2) # Get more than needed
for item_id in review_item_ids:
item_data = adaptive_engine.data["item_performance"].get(item_id)
if item_data and item_data.get("topic"):
card = generate_flashcard_types(item_data["topic"], item_data.get("difficulty", "Medium"))
if card.get("status") and card["item_id"] == item_id: # Ensure the generated card matches the item_id
deck.append(card)
if len(deck) >= len(topics): # Fill up to requested deck size
break
# Fill the rest of the deck with new flashcards if needed
current_deck_size = len(deck)
if current_deck_size < len(topics):
new_topics_needed = len(topics) - current_deck_size
new_topics = random.sample(topics, min(new_topics_needed, len(topics))) # Pick random topics for new cards
for topic_name in new_topics:
card = generate_flashcard_types(topic_name, difficulty)
if card.get("status") and card not in deck: # Avoid duplicates
deck.append(card)
if len(deck) >= len(topics):
break
# Ensure all cards have a unique card_number and total_cards
for i, card in enumerate(deck):
card["card_number"] = i + 1
card["total_cards"] = len(deck)
random.shuffle(deck) # Shuffle the final deck
return deck
def generate_smart_deck(
main_topic: str, deck_size: int = 10, difficulty: str = "Medium", adaptive_engine=None
) -> List[Dict]:
"""Generate a smart deck with related topics, incorporating spaced repetition."""
# Start with the main topic
topics_pool = [main_topic]
# Add related topics
related = fetch_related_topics(main_topic, deck_size - 1)
topics_pool.extend(related)
# Add some random topics to ensure variety
topics_pool.extend(random.sample(topics_pool, min(5, len(topics_pool)))) # Add some random from pool
# Remove duplicates and shuffle
topics_pool = list(set(topics_pool))
random.shuffle(topics_pool)
# Generate the deck using the new generate_flashcard_deck
deck = generate_flashcard_deck(topics_pool[:deck_size], difficulty, adaptive_engine)
return deck