Spaces:
Sleeping
Sleeping
File size: 6,674 Bytes
582bf6b |
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 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# 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
|