campus-Me / utils /material_upload_ui.py
Mithun-999's picture
Add v5.0 Complete: Material Upload UI Helpers + Integration Guide
58320a5
"""
Material Upload UI Integration - Functions for Gradio interface
Handles material upload, analysis, and content generation
"""
from typing import List, Tuple, Optional, Dict, Any
import json
import logging
logger = logging.getLogger(__name__)
def analyze_uploaded_materials(files: List[Any]) -> Tuple[str, str, str, str, str, str, str, str]:
"""
Analyze uploaded lecture materials.
Args:
files: List of uploaded file objects from Gradio
Returns:
Tuple of (concepts, objectives, definitions, structure, themes, difficulty, summary, focus_areas)
"""
if not files:
return ("No files uploaded", "", "", "", "", "", "", "")
try:
from src.ai_engine import MaterialAnalyzer, MaterialProcessor, FileManager
# Initialize
processor = MaterialProcessor()
file_manager = FileManager()
all_analyses = []
error_messages = []
# Process each file
for file_obj in files:
try:
# Upload to manager
success, result = file_manager.upload_file(str(file_obj.name) if hasattr(file_obj, 'name') else file_obj)
if success:
file_id = result
# Get file path
file_path = file_manager.get_file_path(file_id)
if file_path:
# Process and analyze
analysis, content = processor.process_material(file_path)
all_analyses.append(analysis)
# Mark for cleanup (delete in 30 seconds)
file_manager.mark_processed(file_id, delete_after=30)
else:
error_messages.append(f"Error uploading: {result}")
except Exception as e:
error_messages.append(f"Error processing file: {str(e)[:100]}")
logger.error(f"Material processing error: {str(e)}")
continue
if not all_analyses:
return (
"❌ " + " | ".join(error_messages),
"",
"",
"",
"",
"",
"",
""
)
# If multiple materials, combine analysis (use first one for simplicity, or combine)
combined = all_analyses[0]
if len(all_analyses) > 1:
combined = combine_multiple_analyses(all_analyses)
# Format outputs for display
concepts_str = format_concepts(combined.get("key_concepts", []))
objectives_str = format_objectives(combined.get("learning_objectives", []))
definitions_str = format_definitions(combined.get("key_definitions", []))
structure_str = format_structure(combined.get("structure", {}))
themes_str = format_themes(combined.get("main_themes", []))
difficulty_str = combined.get("difficulty_level", "Unknown")
summary_str = combined.get("summary", "No summary available")
focus_str = format_focus_areas(combined.get("focus_areas", []))
return (
concepts_str,
objectives_str,
definitions_str,
structure_str,
themes_str,
difficulty_str,
summary_str,
focus_str
)
except Exception as e:
logger.error(f"Material analysis error: {str(e)}")
return (
f"❌ Analysis error: {str(e)[:200]}",
"",
"",
"",
"",
"",
"",
""
)
def generate_from_material_analysis(
concepts: str,
objectives: str,
definitions: str,
document_type: str,
output_formats: List[str],
content_generator
) -> str:
"""
Generate documents based on material analysis.
Args:
concepts: Extracted concepts
objectives: Learning objectives
definitions: Key definitions
document_type: Type of document to generate
output_formats: List of output formats
content_generator: ContentGenerator instance
Returns:
Status message
"""
try:
from utils import FileHandler
# Parse extracted information
status_messages = []
# Build prompt from analysis
prompt = build_prompt_from_analysis(
concepts, objectives, definitions, document_type
)
# Generate content using content_generator
generated_content = content_generator.generate_section(
title=f"{document_type} - Study Material",
context=concepts + "\n" + objectives,
topic=document_type,
word_count=1000,
style="academic"
)
status_messages.append(f"βœ“ Generated content ({len(generated_content)} chars)")
# Could generate multiple formats here if needed
if "pdf" in output_formats or "docx" in output_formats:
status_messages.append("βœ“ Ready for export to PDF/Word")
return "\n".join(status_messages)
except Exception as e:
logger.error(f"Document generation error: {str(e)}")
return f"❌ Generation error: {str(e)[:200]}"
def format_concepts(concepts: List[Dict[str, Any]]) -> str:
"""Format concepts for display."""
if not concepts:
return "No concepts extracted"
lines = ["**Key Concepts Extracted:**\n"]
for i, concept in enumerate(concepts[:15], 1):
importance = concept.get("importance", 0)
concept_name = concept.get("concept", "Unknown")
bar = "β–ˆ" * (importance // 10) + "β–‘" * (10 - importance // 10)
lines.append(f"{i}. **{concept_name}** [{bar}] {importance}%")
return "\n".join(lines)
def format_objectives(objectives: List[str]) -> str:
"""Format learning objectives for display."""
if not objectives:
return "No learning objectives found"
lines = ["**Learning Objectives:**\n"]
for i, obj in enumerate(objectives, 1):
lines.append(f"{i}. {obj}")
return "\n".join(lines)
def format_definitions(definitions: List[Dict[str, str]]) -> str:
"""Format definitions for display."""
if not definitions:
return "No definitions extracted"
lines = ["**Key Definitions:**\n"]
for i, d in enumerate(definitions[:10], 1):
term = d.get("term", "Unknown")
definition = d.get("definition", "")[:150] + ("..." if len(d.get("definition", "")) > 150 else "")
lines.append(f"**{i}. {term}:** {definition}")
return "\n".join(lines)
def format_structure(structure: Dict[str, Any]) -> str:
"""Format document structure for display."""
if not structure:
return "No structure information"
lines = [
f"**Document Structure Analysis:**",
f"- Total Lines: {structure.get('total_lines', 0)}",
f"- Paragraphs: {structure.get('total_paragraphs', 0)}",
f"- Sections: {structure.get('estimated_sections', 0)}",
f"- Avg Paragraph Length: {structure.get('average_paragraph_length', 0)} chars",
f"- Has Lists: {'Yes' if structure.get('has_lists') else 'No'}",
f"- Has Numbering: {'Yes' if structure.get('has_numbering') else 'No'}",
]
return "\n".join(lines)
def format_themes(themes: List[Dict[str, Any]]) -> str:
"""Format main themes for display."""
if not themes:
return "No themes identified"
lines = ["**Main Themes:**\n"]
for i, theme in enumerate(themes[:10], 1):
theme_name = theme.get("theme", "Unknown")
importance = theme.get("importance", 0)
mentions = theme.get("mentions", 0)
lines.append(f"{i}. **{theme_name}** - Mentions: {mentions}, Importance: {importance}%")
return "\n".join(lines)
def format_focus_areas(focus_areas: List[str]) -> str:
"""Format focus areas for display."""
if not focus_areas:
return "No focus areas identified"
lines = ["**Suggested Focus Areas:**\n"]
for i, area in enumerate(focus_areas[:5], 1):
lines.append(f"{i}. {area}")
return "\n".join(lines)
def build_prompt_from_analysis(
concepts: str,
objectives: str,
definitions: str,
document_type: str
) -> str:
"""Build a generation prompt from extracted material analysis."""
prompt = f"""
Based on the following extracted material analysis, generate a {document_type}:
Key Concepts:
{concepts}
Learning Objectives:
{objectives}
Key Definitions:
{definitions}
Task: Create a comprehensive {document_type} that covers all the above material,
organized by learning objectives, with clear explanations of each concept and definition.
Ensure the output is:
- Well-structured and easy to understand
- Academic in tone
- Comprehensive but concise
- Includes all key concepts and definitions
"""
return prompt
def combine_multiple_analyses(analyses: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
Combine multiple material analyses into one comprehensive analysis.
Args:
analyses: List of individual material analyses
Returns:
Combined analysis
"""
if not analyses:
return {}
if len(analyses) == 1:
return analyses[0]
# Combine concepts (remove duplicates, keep highest importance)
all_concepts = {}
for analysis in analyses:
for concept in analysis.get("key_concepts", []):
concept_name = concept.get("concept", "Unknown").lower()
if concept_name not in all_concepts:
all_concepts[concept_name] = concept
else:
# Keep the one with higher importance
if concept.get("importance", 0) > all_concepts[concept_name].get("importance", 0):
all_concepts[concept_name] = concept
# Combine objectives
all_objectives = []
seen_objectives = set()
for analysis in analyses:
for obj in analysis.get("learning_objectives", []):
if obj not in seen_objectives:
all_objectives.append(obj)
seen_objectives.add(obj)
# Combine definitions
all_definitions = []
seen_terms = set()
for analysis in analyses:
for definition in analysis.get("key_definitions", []):
term = definition.get("term", "").lower()
if term not in seen_terms:
all_definitions.append(definition)
seen_terms.add(term)
# Use first analysis as base, then override with combined data
combined = analyses[0].copy()
combined["key_concepts"] = list(all_concepts.values())[:20]
combined["learning_objectives"] = all_objectives
combined["key_definitions"] = all_definitions
combined["material_count"] = len(analyses)
return combined
def get_material_upload_instructions() -> str:
"""Get instructions for material upload tab."""
return """
### πŸ“š Upload & Analyze Lecture Materials
This tool allows you to upload your lecture materials (PDFs, Word documents, PowerPoint slides, etc.)
and automatically extract:
- **Key Concepts** - Important topics and terms with importance scores
- **Learning Objectives** - What you're expected to learn
- **Key Definitions** - Important terms and their definitions
- **Document Structure** - How the material is organized
- **Main Themes** - Primary topics and their relationships
- **Difficulty Level** - Whether this is beginner, intermediate, or advanced material
- **Focus Areas** - Where you should concentrate your study efforts
#### Supported File Formats:
- πŸ“„ PDF files (.pdf)
- πŸ“Š PowerPoint presentations (.pptx, .ppt)
- πŸ“ Word documents (.docx, .doc)
- πŸ“‹ Text files (.txt)
- πŸ–ŠοΈ Markdown files (.md)
#### How to Use:
1. Click "Upload Lecture Materials" and select one or more files
2. Click "Analyze Materials" to extract insights
3. Review the analysis results
4. Use "Generate Documents Based on Analysis" to create study materials
5. Files are automatically deleted after processing (privacy protected)
#### Privacy Note:
- Files are processed temporarily and automatically deleted
- No data is stored permanently
- Analysis happens locally without external storage
"""