kashyaparun commited on
Commit
b2e8832
·
verified ·
1 Parent(s): 82aa43b

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py.py +1316 -0
  2. requirements.txt +10 -0
app.py.py ADDED
@@ -0,0 +1,1316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ import re
4
+ import tempfile
5
+ import logging
6
+ import time
7
+ import base64
8
+ import json
9
+ from typing import List, Dict, Any, Union, Optional, Tuple
10
+ from dotenv import load_dotenv
11
+ import io
12
+
13
+ # Document processing libraries
14
+ from docx import Document
15
+ from docx.shared import Inches, Pt
16
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
17
+ from docx.enum.style import WD_STYLE_TYPE
18
+ import PyPDF2
19
+ from pptx import Presentation
20
+ import docx2txt
21
+ from docx.oxml import OxmlElement
22
+ from docx.oxml.ns import qn
23
+
24
+ # CrewAI imports
25
+ from crewai import Agent, Task, Crew, Process, LLM
26
+ from crewai.tasks import TaskOutput
27
+ from crewai.tools import BaseTool, tool
28
+ from pydantic import BaseModel, Field
29
+ import litellm
30
+ from langchain.tools import Tool
31
+
32
+ # Configure logging
33
+ logging.basicConfig(level=logging.INFO)
34
+ logger = logging.getLogger(__name__)
35
+
36
+ # Set page configuration
37
+ st.set_page_config(
38
+ page_title="Case Study Analysis Suite",
39
+ page_icon="📚",
40
+ layout="wide"
41
+ )
42
+
43
+ # Initialize session state variables
44
+ if 'active_tab' not in st.session_state:
45
+ st.session_state.active_tab = "Case Breakdown"
46
+ if 'uploaded_files' not in st.session_state:
47
+ st.session_state.uploaded_files = None
48
+ if 'extracted_text' not in st.session_state:
49
+ st.session_state.extracted_text = ""
50
+ if 'first_file_text' not in st.session_state:
51
+ st.session_state.first_file_text = ""
52
+ if 'breakdown_generated' not in st.session_state:
53
+ st.session_state.breakdown_generated = False
54
+ if 'teaching_plan_generated' not in st.session_state:
55
+ st.session_state.teaching_plan_generated = False
56
+ if 'board_plan_generated' not in st.session_state:
57
+ st.session_state.board_plan_generated = False
58
+
59
+ # Load environment variables
60
+ load_dotenv()
61
+
62
+ # Page title and description
63
+ st.title("📚 Case Study Analysis Suite")
64
+ st.subheader("Generate comprehensive Teaching Notes, Teaching Plans, and Board Plans")
65
+ st.write("Developed for BIA 568 (Business Intelligence and Analytics) -- Management of A.I. at Stevens Institute of Technology")
66
+
67
+ st.write("---")
68
+
69
+ # Sidebar for API key configuration
70
+ with st.sidebar:
71
+ st.title("⚙️ Configuration")
72
+ api_key_source = st.radio("Select API Key Provider:",
73
+ ["Google (Gemini)", "OpenAI"],
74
+ help="Choose which AI provider to use")
75
+
76
+ if api_key_source == "Google (Gemini)":
77
+ api_key = st.text_input("Enter your Gemini API Key", type="password",
78
+ help="Required for the AI model to function")
79
+ if api_key:
80
+ os.environ["GEMINI_API_KEY"] = api_key
81
+ os.environ["GOOGLE_API_KEY"] = api_key
82
+ else:
83
+ api_key = st.text_input("Enter your OpenAI API Key", type="password",
84
+ help="Required for the AI model to function")
85
+ if api_key:
86
+ os.environ["OPENAI_API_KEY"] = api_key
87
+
88
+ st.divider()
89
+
90
+ # Reset button
91
+ if st.button("🔄 Reset Session", use_container_width=True):
92
+ for key in list(st.session_state.keys()):
93
+ del st.session_state[key]
94
+ st.rerun()
95
+
96
+
97
+ #---------------------------- Utility Functions ----------------------------#
98
+
99
+ def extract_text_from_pdf(file):
100
+ """Extract text content from PDF file"""
101
+ if isinstance(file, bytes):
102
+ file = io.BytesIO(file)
103
+ pdf_reader = PyPDF2.PdfReader(file)
104
+ text = ""
105
+ for page in pdf_reader.pages:
106
+ text += page.extract_text()
107
+ return text
108
+
109
+ def extract_text_from_docx(file):
110
+ """Extract text content from DOCX file"""
111
+ if isinstance(file, bytes):
112
+ file = io.BytesIO(file)
113
+ return docx2txt.process(file)
114
+
115
+ def extract_text_from_pptx(file):
116
+ """Extract text content from PPTX file"""
117
+ if isinstance(file, bytes):
118
+ file = io.BytesIO(file)
119
+ prs = Presentation(file)
120
+ text = ""
121
+ for slide in prs.slides:
122
+ for shape in slide.shapes:
123
+ if hasattr(shape, "text"):
124
+ text += shape.text + "\n"
125
+ return text
126
+
127
+ def extract_text_from_any_file(file):
128
+ """Extract text based on file type"""
129
+ if file.name.endswith('.pdf'):
130
+ return extract_text_from_pdf(file)
131
+ elif file.name.endswith('.docx'):
132
+ return extract_text_from_docx(file)
133
+ elif file.name.endswith(('.pptx', '.ppt')):
134
+ return extract_text_from_pptx(file)
135
+ else:
136
+ return "Unsupported file format"
137
+
138
+ def create_download_link(content, filename):
139
+ """Create a download link for text content"""
140
+ b64 = base64.b64encode(content.encode()).decode()
141
+ href = f'<a href="data:text/plain;base64,{b64}" download="{filename}" class="download-button">Download {filename}</a>'
142
+ return href
143
+
144
+ def process_files(uploaded_files):
145
+ """Process uploaded files and extract text"""
146
+ combined_text = ""
147
+ first_file_text = ""
148
+
149
+ temp_file_paths = []
150
+
151
+ for file in uploaded_files:
152
+ file.seek(0) # Reset file pointer
153
+
154
+ # Extract text based on file type
155
+ if file.name.endswith('.pdf'):
156
+ text = extract_text_from_pdf(file)
157
+ elif file.name.endswith('.docx'):
158
+ text = extract_text_from_docx(file)
159
+ elif file.name.endswith(('.pptx', '.ppt')):
160
+ text = extract_text_from_pptx(file)
161
+ else:
162
+ continue
163
+
164
+ # Save first file's text for metadata extraction
165
+ if not first_file_text:
166
+ first_file_text = text
167
+
168
+ combined_text += text + "\n\n"
169
+
170
+ # Create temporary file for each uploaded file
171
+ with tempfile.NamedTemporaryFile(delete=False, suffix=f".{file.name.split('.')[-1]}") as tmp_file:
172
+ file.seek(0)
173
+ tmp_file.write(file.getvalue())
174
+ temp_file_paths.append(tmp_file.name)
175
+
176
+ return combined_text, first_file_text, temp_file_paths
177
+
178
+ #---------------------------- Document Generator (Case Breakdown) ----------------------------#
179
+
180
+ class DocumentGenerator:
181
+ def __init__(self):
182
+ self.bullet_counter = 1
183
+
184
+ def add_toc(self, doc):
185
+ """Add native Word table of contents"""
186
+ paragraph = doc.add_paragraph()
187
+ run = paragraph.add_run()
188
+
189
+ # Start the TOC field
190
+ fldChar1 = create_element('w:fldChar')
191
+ create_attribute(fldChar1, 'w:fldCharType', 'begin')
192
+ run._r.append(fldChar1)
193
+
194
+ # Add TOC instruction text
195
+ instrText = create_element('w:instrText')
196
+ create_attribute(instrText, 'xml:space', 'preserve')
197
+ instrText.text = 'TOC \\o "1-3" \\h \\z \\u'
198
+ run._r.append(instrText)
199
+
200
+ # End the TOC field
201
+ fldChar2 = create_element('w:fldChar')
202
+ create_attribute(fldChar2, 'w:fldCharType', 'end')
203
+ run._r.append(fldChar2)
204
+
205
+ def setup_document_styles(self, doc):
206
+ """Set up custom styles for the document"""
207
+ styles = doc.styles
208
+
209
+ # Heading styles
210
+ for level in range(1, 4):
211
+ style_name = f'Heading {level}'
212
+ if style_name not in styles:
213
+ style = styles.add_style(style_name, WD_STYLE_TYPE.PARAGRAPH)
214
+ style.base_style = styles['Normal']
215
+ style.font.size = Pt(16 - (level * 2))
216
+ style.font.bold = True
217
+
218
+ # Custom bullet point style
219
+ if 'Bullet Point' not in styles:
220
+ bullet_style = styles.add_style('Bullet Point', WD_STYLE_TYPE.PARAGRAPH)
221
+ bullet_style.base_style = styles['Normal']
222
+ bullet_style.font.size = Pt(11)
223
+ bullet_style.paragraph_format.left_indent = Inches(0.25)
224
+ bullet_style.paragraph_format.first_line_indent = Inches(-0.25)
225
+
226
+ def add_formatted_text(self, paragraph, text):
227
+ """Add text to paragraph with proper formatting"""
228
+ # First handle double asterisks
229
+ parts = re.split(r'(\*\*.*?\*\*)', text)
230
+
231
+ for part in parts:
232
+ if part.startswith('**') and part.endswith('**'):
233
+ # Handle bold text (surrounded by double asterisks)
234
+ run = paragraph.add_run(part[2:-2])
235
+ run.bold = True
236
+ else:
237
+ # Handle single asterisks within the remaining text
238
+ # Split by single asterisks
239
+ subparts = re.split(r'(\*[^\*]+\*)', part)
240
+ for subpart in subparts:
241
+ if subpart.startswith('*') and subpart.endswith('*') and len(subpart) > 2:
242
+ # It's a bold text marked with single asterisks
243
+ run = paragraph.add_run(subpart[1:-1])
244
+ run.bold = True
245
+ else:
246
+ # Regular text
247
+ if subpart.strip():
248
+ paragraph.add_run(subpart)
249
+
250
+ def clean_content(self, text):
251
+ """Clean content and formatting"""
252
+ # Remove HTML tags
253
+ text = re.sub(r'<[^>]+>', '', text)
254
+
255
+ # Remove markdown headers while preserving content
256
+ text = re.sub(r'^#+\s*(.+)$', r'\1', text, flags=re.MULTILINE)
257
+
258
+ # Remove duplicate section headers
259
+ text = re.sub(r'(?i)^(.*?)\n\*\*\1\*\*', r'\1', text, flags=re.MULTILINE)
260
+
261
+ # Clean up <br> tags
262
+ text = re.sub(r'<br\s*/?>', '\n', text)
263
+
264
+ # Remove excessive newlines
265
+ text = re.sub(r'\n\s*\n', '\n\n', text)
266
+
267
+ # Process line by line to handle bullet points and bold text
268
+ lines = text.split('\n')
269
+ cleaned_lines = []
270
+
271
+ for line in lines:
272
+ line = line.strip()
273
+ if not line:
274
+ cleaned_lines.append(line)
275
+ continue
276
+
277
+ # Check if line starts with a single asterisk (potential bullet point)
278
+ if line.lstrip().startswith('*'):
279
+ # Skip if it starts with double asterisks
280
+ if line.lstrip().startswith('**'):
281
+ cleaned_lines.append(line)
282
+ continue
283
+
284
+ # Count asterisks that are not part of bold text markers
285
+ # First, temporarily replace bold text markers
286
+ temp_line = re.sub(r'\*\*.*?\*\*', '', line) # Remove double-asterisk patterns
287
+ temp_line = re.sub(r'\*[^\*]+\*', '', temp_line) # Remove single-asterisk patterns
288
+
289
+ # If there's exactly one asterisk left, it's a bullet point
290
+ if temp_line.count('*') == 1:
291
+ content = line.replace('*', '', 1).strip()
292
+ cleaned_lines.append(f'* {content}')
293
+ else:
294
+ cleaned_lines.append(line)
295
+ else:
296
+ cleaned_lines.append(line)
297
+
298
+ return '\n'.join(cleaned_lines).strip()
299
+
300
+ def add_section_content(self, doc, section_name, content):
301
+ """Add a section with proper formatting"""
302
+ # Add section heading
303
+ heading = doc.add_heading(section_name.upper(), level=1)
304
+ heading.alignment = WD_ALIGN_PARAGRAPH.LEFT
305
+
306
+ # Clean and process the content
307
+ content = self.clean_content(content)
308
+ lines = content.split('\n')
309
+
310
+ for line in lines:
311
+ line = line.strip()
312
+ if not line:
313
+ continue
314
+
315
+ # Check for bullet point line
316
+ is_bullet = line.startswith('* ') and not line.startswith('** ')
317
+
318
+ if is_bullet:
319
+ # Create bullet point paragraph
320
+ bullet_paragraph = doc.add_paragraph(style='Bullet Point')
321
+ bullet_paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
322
+ bullet_paragraph.paragraph_format.left_indent = Inches(0.5)
323
+ bullet_paragraph.paragraph_format.first_line_indent = Inches(-0.25)
324
+
325
+ # Add bullet character
326
+ bullet_paragraph.add_run('• ')
327
+
328
+ # Add the rest of the line with formatting
329
+ content = line[2:].strip()
330
+ self.add_formatted_text(bullet_paragraph, content)
331
+ else:
332
+ # Regular paragraph
333
+ p = doc.add_paragraph()
334
+ p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
335
+ self.add_formatted_text(p, line)
336
+
337
+ def create_word_document(self, title, author, sections_content):
338
+ """Create and return a formatted Word document"""
339
+ doc = Document()
340
+ self.setup_document_styles(doc)
341
+
342
+ # Title section
343
+ title_paragraph = doc.add_paragraph()
344
+ title_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
345
+ title_run = title_paragraph.add_run("CASE BREAKDOWN")
346
+ title_run.font.size = Pt(16)
347
+ title_run.bold = True
348
+
349
+ # Case study name and author
350
+ case_name_paragraph = doc.add_paragraph()
351
+ case_name_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
352
+ case_name_run = case_name_paragraph.add_run(title)
353
+ case_name_run.font.size = Pt(14)
354
+ case_name_run.italic = True
355
+
356
+ author_paragraph = doc.add_paragraph()
357
+ author_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
358
+ author_run = author_paragraph.add_run(author)
359
+ author_run.font.size = Pt(12)
360
+
361
+ # Separator line
362
+ separator = doc.add_paragraph()
363
+ separator.alignment = WD_ALIGN_PARAGRAPH.CENTER
364
+ separator_run = separator.add_run('_' * 50)
365
+
366
+ # Add TOC
367
+ toc_heading = doc.add_heading('TABLE OF CONTENTS', level=1)
368
+ toc_heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
369
+ self.add_toc(doc)
370
+ doc.add_page_break()
371
+
372
+ # Add sections
373
+ for section_name, content in sections_content.items():
374
+ self.add_section_content(doc, section_name, content)
375
+ doc.add_paragraph() # Add spacing between sections
376
+
377
+ return doc
378
+
379
+ # Helper functions for Word document
380
+ def create_element(name):
381
+ return OxmlElement(name)
382
+
383
+ def create_attribute(element, name, value):
384
+ element.set(qn(name), value)
385
+
386
+ #---------------------------- Case Breakdown Generator ----------------------------#
387
+
388
+ class CaseMetadata(BaseModel):
389
+ title: str = Field(description="The title of the case study")
390
+ author: str = Field(description="The author(s) of the case study")
391
+
392
+ class SectionContent(BaseModel):
393
+ content: str = Field(description="Generated content for the section")
394
+ review: str = Field(description="Review of the content with score and feedback")
395
+
396
+ class CaseBreakdownCrew:
397
+ def __init__(self, api_key):
398
+ self.api_key = api_key
399
+
400
+ def create_metadata_agent(self):
401
+ return Agent(
402
+ role="Metadata Analyzer",
403
+ goal="Extract title and author information from document content",
404
+ backstory="""You specialize in analyzing document content to identify key metadata
405
+ such as titles, authors, and other publication information. You have a keen eye for
406
+ identifying the most important and relevant document metadata, even when it's not
407
+ explicitly labeled.""",
408
+ verbose=True
409
+ )
410
+
411
+ def create_content_generator_agent(self):
412
+ return Agent(
413
+ role="Case Study Content Generator",
414
+ goal="Generate comprehensive case analysis content based on section requirements",
415
+ backstory="""You are an expert business analyst specializing in case study analysis.
416
+ You excel at breaking down complex business cases into structured, insightful content
417
+ that highlights key learning points, strategies, and insights. You have extensive experience
418
+ in business education and know how to create content that is valuable for teaching and learning.""",
419
+ verbose=True
420
+ )
421
+
422
+ def create_content_reviewer_agent(self):
423
+ return Agent(
424
+ role="Content Quality Reviewer",
425
+ goal="Evaluate and score content for quality, relevance, and depth",
426
+ backstory="""You are a seasoned academic reviewer with years of experience evaluating
427
+ business case studies and educational content. You have a strong understanding of what makes
428
+ effective case study material and can provide constructive feedback to improve content quality.
429
+ You carefully analyze content for relevance, clarity, depth, and educational value.""",
430
+ verbose=True
431
+ )
432
+
433
+ def create_metadata_task(self, text):
434
+ return Task(
435
+ description=f"""
436
+ Analyze the extracted text and identify the case study title and author(s).
437
+
438
+ Look for information typically found at the beginning of a document, such as:
439
+ 1. The case study or article title
440
+ 2. The author name(s)
441
+
442
+ Return the identified title and author in the following format:
443
+
444
+ Title: [the title]
445
+ Author: [the author(s)]
446
+
447
+ If you cannot find this information with certainty, use "Untitled Case Study" for the title
448
+ and "Unknown Author" for the author.
449
+
450
+ Text to analyze:
451
+ {text[:2000]}
452
+ """,
453
+ expected_output="Extracted metadata with title and author information",
454
+ agent=self.create_metadata_agent()
455
+ )
456
+
457
+ def create_section_task(self, section_name, prompt, text):
458
+ return Task(
459
+ description=f"""
460
+ Generate content for the '{section_name}' section of the case breakdown.
461
+
462
+ {prompt}
463
+
464
+ Formatting Requirements:
465
+ 1. Use **bold** for important terms or concepts
466
+ 2. For bullet points, start each line with "* " (asterisk followed by space)
467
+ 3. For numbered lists, use "1. ", "2. " etc.
468
+ 4. Use line breaks between paragraphs
469
+ 5. Keep paragraphs focused and concise
470
+
471
+ Ensure the content is structured, well-organized, and follows proper formatting.
472
+
473
+ Text to analyze:
474
+ {text[:5000]}
475
+ """,
476
+ expected_output=f"A well-formatted {section_name} section",
477
+ agent=self.create_content_generator_agent()
478
+ )
479
+
480
+ def create_review_task(self, section_name, content):
481
+ return Task(
482
+ description=f"""
483
+ Review the content for the '{section_name}' section.
484
+
485
+ Score it from 1-10 based on:
486
+ 1. Relevance to the section (0-3)
487
+ 2. Clarity and coherence (0-3)
488
+ 3. Depth of analysis (0-4)
489
+
490
+ Provide a structured review with:
491
+ 1. Numerical score
492
+ 2. Specific strengths
493
+ 3. Areas for improvement
494
+
495
+ Content to review:
496
+ {content}
497
+ """,
498
+ expected_output=f"A review of the {section_name} section",
499
+ agent=self.create_content_reviewer_agent()
500
+ )
501
+
502
+ def extract_metadata(self, text):
503
+ metadata_task = self.create_metadata_task(text)
504
+ crew = Crew(
505
+ agents=[self.create_metadata_agent()],
506
+ tasks=[metadata_task],
507
+ process=Process.sequential,
508
+ verbose=False
509
+ )
510
+ result = crew.kickoff()
511
+
512
+ title = "Untitled Case Study"
513
+ author = "Unknown Author"
514
+
515
+ for line in result.raw.split('\n'):
516
+ if line.startswith('Title:'):
517
+ extracted_title = line.replace('Title:', '').strip()
518
+ if extracted_title and extracted_title != "[Untitled Case Study]":
519
+ title = extracted_title
520
+ elif line.startswith('Author:'):
521
+ extracted_author = line.replace('Author:', '').strip()
522
+ if extracted_author and extracted_author != "[Unknown Author]":
523
+ author = extracted_author
524
+
525
+ return title, author
526
+
527
+ def generate_section_content(self, text, section_name, section_prompt):
528
+ section_task = self.create_section_task(section_name, section_prompt, text)
529
+ crew = Crew(
530
+ agents=[self.create_content_generator_agent()],
531
+ tasks=[section_task],
532
+ process=Process.sequential,
533
+ verbose=False
534
+ )
535
+ result = crew.kickoff()
536
+ return result.raw
537
+
538
+ def review_content(self, content, section_name):
539
+ review_task = self.create_review_task(section_name, content)
540
+ crew = Crew(
541
+ agents=[self.create_content_reviewer_agent()],
542
+ tasks=[review_task],
543
+ process=Process.sequential,
544
+ verbose=False
545
+ )
546
+ result = crew.kickoff()
547
+ return result.raw
548
+
549
+ #---------------------------- Teaching Plan Generator ----------------------------#
550
+
551
+ class AgentTracker:
552
+ def __init__(self):
553
+ self.current_agent = ""
554
+ self.placeholder = None
555
+
556
+ def set_placeholder(self, placeholder):
557
+ self.placeholder = placeholder
558
+
559
+ def update_agent(self, agent_name):
560
+ self.current_agent = agent_name
561
+ if self.placeholder:
562
+ with self.placeholder:
563
+ st.write(f"🤖 Agent in action: **{self.current_agent}**")
564
+
565
+ @tool
566
+ def extract_text(file_path: str) -> str:
567
+ """
568
+ Extract text from a file based on its extension.
569
+
570
+ Args:
571
+ file_path (str): Path to the file
572
+
573
+ Returns:
574
+ str: Extracted text content
575
+ """
576
+ try:
577
+ file_extension = file_path.split('.')[-1].lower()
578
+
579
+ if file_extension == 'pdf':
580
+ reader = PyPDF2.PdfReader(file_path)
581
+ text = ""
582
+ for page in reader.pages:
583
+ text += page.extract_text()
584
+ return text
585
+
586
+ elif file_extension == 'docx':
587
+ return docx2txt.process(file_path)
588
+
589
+ elif file_extension in ['ppt', 'pptx']:
590
+ presentation = Presentation(file_path)
591
+ text = ""
592
+ for slide in presentation.slides:
593
+ for shape in slide.shapes:
594
+ if hasattr(shape, "text"):
595
+ text += shape.text + "\n"
596
+ return text
597
+
598
+ else:
599
+ return f"Unsupported file format: {file_extension}"
600
+
601
+ except Exception as e:
602
+ return f"Error extracting text: {str(e)}"
603
+
604
+ def create_teaching_plan_crew(file_paths, llm_provider="gemini"):
605
+ # Initialize the agent tracker
606
+ tracker = AgentTracker()
607
+ tracker.set_placeholder(st.empty())
608
+
609
+ # Initialize LLM based on provider
610
+ if llm_provider == "gemini":
611
+ my_llm = LLM(
612
+ model='gemini/gemini-2.0-flash',
613
+ api_key=os.environ.get("GEMINI_API_KEY")
614
+ )
615
+ else:
616
+ my_llm = LLM(
617
+ model='gpt-4-turbo',
618
+ api_key=os.environ.get("OPENAI_API_KEY")
619
+ )
620
+
621
+ # Define agents with callbacks for UI updates
622
+ pdf_analyzer = Agent(
623
+ role='Case Study Analyzer',
624
+ goal='Extract key concepts, objectives, and data from case study files',
625
+ backstory='You are an expert in analyzing business case studies. Identify core themes, data points, and learning objectives.',
626
+ llm=my_llm,
627
+ tools=[extract_text],
628
+ verbose=True,
629
+ step_callback=lambda *args, **kwargs: tracker.update_agent("Case Study Analyzer")
630
+ )
631
+
632
+ plan_generator = Agent(
633
+ role='Teaching Plan Designer',
634
+ goal='Create a 2-hour lesson plan based on analyzed case study data',
635
+ backstory='You are an educator designing a structured lesson plan. Use the extracted case study data to outline activities, discussions, and assessments.',
636
+ llm=my_llm,
637
+ verbose=True,
638
+ step_callback=lambda *args, **kwargs: tracker.update_agent("Teaching Plan Designer")
639
+ )
640
+
641
+ reviewer = Agent(
642
+ role='Plan Reviewer',
643
+ goal='Ensure the lesson plan is engaging and aligned with learning objectives',
644
+ backstory='You are a curriculum reviewer. Verify the plan\'s clarity, alignment with objectives, and engagement level.',
645
+ llm=my_llm,
646
+ verbose=True,
647
+ step_callback=lambda *args, **kwargs: tracker.update_agent("Plan Reviewer")
648
+ )
649
+
650
+ final_reporter = Agent(
651
+ role='Teaching Plan Reporter',
652
+ goal='Ensure to incorporate the feedback from the reviewer agent and finalize the content for the teaching plan',
653
+ backstory=""" You are a Harvard Business School Teaching Plan Reporter with 20 years of Experience in teaching and curriculum development.
654
+ You are responsible for finalizing the content for the teaching plan and ensuring it is engaging and aligned with learning objectives.
655
+ You will use the feedback from the reviewer agent to make necessary revisions and finalize the content for the teaching plan.""",
656
+ llm=my_llm,
657
+ verbose=True,
658
+ step_callback=lambda *args, **kwargs: tracker.update_agent("Teaching Plan Reporter")
659
+ )
660
+
661
+ # Combine all file contents into one text
662
+ combined_file_path = file_paths[0] # Use the first file path for the analyzer to begin
663
+
664
+ # Define tasks
665
+ analyze_pdf = Task(
666
+ description=f"Extract and analyze the files at {', '.join(file_paths)} for key concepts and learning objectives.",
667
+ config={"file_path": combined_file_path},
668
+ expected_output="A summary of key concepts and learning objectives extracted from the files.",
669
+ agent=pdf_analyzer
670
+ )
671
+
672
+ generate_plan = Task(
673
+ description="Design a 2-hour lesson plan with introduction, analysis, group activity, and assessment.",
674
+ expected_output="A detailed 2-hour lesson plan with clear sections and activities.",
675
+ agent=plan_generator
676
+ )
677
+
678
+ review_plan = Task(
679
+ description="Review the lesson plan for clarity, alignment with objectives, and student engagement.",
680
+ expected_output="Feedback on the lesson plan's clarity, alignment, and engagement level.",
681
+ agent=reviewer
682
+ )
683
+
684
+ final_plan = Task(
685
+ description="Generate the final lesson plan based on the feedback.",
686
+ expected_output="""
687
+ You are also responsible for ensuring the plan is clear and concise.
688
+ - It should have the overall objective to start with.
689
+ - It should have a clear introduction
690
+ - It should have detailed lesson breakdowns with the time to be spent on each section and the title of the section Highlighting all the important concepts to be taught (you can use tables to highlight the concepts in a structured way)
691
+ - It should have one powerful visual aid which can be a table That can help in better understanding for the students
692
+ - It can have simple assessmentsthat enables class participation engagement in brainstorming activities and such that can help in better understanding of the concepts.
693
+ - It should have a clear conclusion that ties back to the overall objective.
694
+ - Overall Plan should be around 1300 - 1500 words.""",
695
+ agent=final_reporter
696
+ )
697
+
698
+ # Create crew
699
+ crew = Crew(
700
+ agents=[pdf_analyzer, plan_generator, reviewer, final_reporter],
701
+ tasks=[analyze_pdf, generate_plan, review_plan, final_plan],
702
+ process=Process.sequential,
703
+ verbose=True
704
+ )
705
+
706
+ return crew
707
+
708
+ #---------------------------- Board Plan Generator ----------------------------#
709
+
710
+ class BoardPlanAnalyzer:
711
+ def __init__(self, llm_provider="gemini"):
712
+ if llm_provider == "gemini":
713
+ api_key = os.environ.get('GEMINI_API_KEY')
714
+ self.model = "gemini/gemini-2.0-flash"
715
+ else:
716
+ api_key = os.environ.get('OPENAI_API_KEY')
717
+ self.model = "gpt-4-turbo"
718
+
719
+ if not api_key:
720
+ raise ValueError(f"{llm_provider.capitalize()} API key not found")
721
+
722
+ if llm_provider == "gemini":
723
+ os.environ['GEMINI_API_KEY'] = api_key
724
+ else:
725
+ os.environ['OPENAI_API_KEY'] = api_key
726
+
727
+ litellm.set_verbose = True
728
+
729
+ # Create agents
730
+ self.create_agents()
731
+
732
+ def create_agents(self):
733
+ """Create specialized agents for different tasks"""
734
+
735
+ # PDF Processing Agent
736
+ self.pdf_processor = Agent(
737
+ role='PDF Processor',
738
+ goal='Extract and clean text content from PDF case studies',
739
+ backstory="""You are an expert at processing PDF documents and extracting
740
+ meaningful content. You ensure the text is properly formatted and ready
741
+ for analysis.""",
742
+ tools=[Tool(
743
+ name="extract_text",
744
+ func=self.extract_text_from_pdf,
745
+ description="Extracts text content from PDF files"
746
+ )],
747
+ allow_delegation=False,
748
+ verbose=True
749
+ )
750
+
751
+ # Content Analysis Agent
752
+ self.analyzer = Agent(
753
+ role='Case Study Analyzer',
754
+ goal='Analyze case studies and identify key points for board plan',
755
+ backstory="""You are an expert business analyst skilled at analyzing case
756
+ studies and identifying crucial elements. You create clear, structured
757
+ analyses that highlight key insights.""",
758
+ tools=[Tool(
759
+ name="analyze_content",
760
+ func=self.analyze_case_study,
761
+ description="Analyzes case study and creates structured board plan"
762
+ )],
763
+ allow_delegation=False,
764
+ verbose=True
765
+ )
766
+
767
+ def extract_text_from_pdf(self, pdf_content: bytes) -> str:
768
+ """Extract text from PDF using PyPDF2"""
769
+ try:
770
+ # Create PDF reader object
771
+ pdf_file = io.BytesIO(pdf_content)
772
+ pdf_reader = PyPDF2.PdfReader(pdf_file)
773
+
774
+ # Extract text from all pages
775
+ text_content = []
776
+ for page in pdf_reader.pages:
777
+ text_content.append(page.extract_text())
778
+
779
+ # Combine all pages with proper spacing
780
+ return "\n\n".join(text_content)
781
+
782
+ except Exception as e:
783
+ raise Exception(f"PDF extraction error: {str(e)}")
784
+
785
+ def analyze_case_study(self, text: str) -> dict:
786
+ """Analyze case study content using litellm with JSON response"""
787
+ messages = [
788
+ {
789
+ "role": "user",
790
+ "content": f"""Analyze this case study and create a structured board plan.
791
+
792
+ Case study: {text}
793
+
794
+ Create a detailed analysis with these exact sections:
795
+ 1. Main concept/technology being discussed
796
+ 2. Industry suitability and context
797
+ 3. Benefits and impact
798
+ 4. Key roles and perspectives
799
+ 5. Implementation details
800
+ - Data requirements
801
+ - Scaling considerations
802
+ - Performance metrics
803
+ 6. Next steps and recommendations
804
+
805
+ Also include risk assessment.
806
+
807
+ Format the response as a JSON object with this structure:
808
+ {{
809
+ "boards": [
810
+ {{
811
+ "title": "BOARD 1: [Title]",
812
+ "points": ["point 1", "point 2", "point 3"]
813
+ }},
814
+ // ... other boards
815
+ ],
816
+ "risk_assessment": {{
817
+ "title": "WHAT IS THE RISK OF FAILURE?",
818
+ "points": ["risk 1", "risk 2"]
819
+ }}
820
+ }}"""
821
+ }
822
+ ]
823
+
824
+ try:
825
+ response = litellm.completion(
826
+ model=self.model,
827
+ messages=messages,
828
+ response_format={"type": "json_object"}
829
+ )
830
+
831
+ # Extract and parse the JSON response
832
+ content = response.choices[0].message.content
833
+ return json.loads(content)
834
+
835
+ except Exception as e:
836
+ st.error(f"Analysis error: {str(e)}")
837
+ if 'response' in locals():
838
+ st.error("Raw response:")
839
+ st.code(response.choices[0].message.content)
840
+ raise
841
+
842
+ def process_case_study(self, pdf_content: bytes) -> dict:
843
+ """Process the case study using Crew AI agents"""
844
+ try:
845
+ # First extract text
846
+ progress_text = st.empty()
847
+ progress_text.text("Extracting text from PDF...")
848
+ text_content = self.extract_text_from_pdf(pdf_content)
849
+
850
+ # Show extracted text for verification
851
+ with st.expander("View extracted text"):
852
+ st.text(text_content[:500] + "...")
853
+
854
+ # Then analyze content
855
+ progress_text.text("Analyzing content...")
856
+ result = self.analyze_case_study(text_content)
857
+
858
+ return result
859
+
860
+ except Exception as e:
861
+ raise Exception(f"Processing error: {str(e)}")
862
+
863
+ #---------------------------- Main App Interface ----------------------------#
864
+
865
+ # File upload section - shared across all generators
866
+ if st.session_state.uploaded_files is None:
867
+ uploaded_files = st.file_uploader(
868
+ "Upload case study files (PDF, DOCX, PPT, PPTX)",
869
+ accept_multiple_files=True,
870
+ type=['pdf', 'docx', 'ppt', 'pptx']
871
+ )
872
+
873
+ if uploaded_files:
874
+ st.session_state.uploaded_files = uploaded_files
875
+
876
+ # Process files only once and store results
877
+ with st.spinner("Processing uploaded files..."):
878
+ combined_text, first_file_text, temp_file_paths = process_files(uploaded_files)
879
+ st.session_state.combined_text = combined_text
880
+ st.session_state.first_file_text = first_file_text
881
+ st.session_state.temp_file_paths = temp_file_paths
882
+
883
+ # Show file upload summary
884
+ st.success(f"✅ {len(uploaded_files)} file(s) uploaded and processed successfully")
885
+ for file in uploaded_files:
886
+ st.write(f"- {file.name}")
887
+
888
+ # Tabs for different generators
889
+ if st.session_state.uploaded_files:
890
+ # Use native streamlit tabs (no styling overrides)
891
+ tab1, tab2, tab3 = st.tabs([
892
+ "Case Breakdown Generator",
893
+ "Teaching Plan Generator",
894
+ "Board Plan Generator"
895
+ ])
896
+
897
+ #------------------ Case Breakdown Generator Tab ------------------#
898
+ with tab1:
899
+ st.header("Case Breakdown Generator")
900
+ st.write("Generate a comprehensive breakdown of the case study with sections for teaching purposes.")
901
+
902
+ if not st.session_state.breakdown_generated:
903
+ if not api_key:
904
+ st.warning("⚠️ Please enter an API key in the sidebar before proceeding.")
905
+ else:
906
+ # Initialize the breakdown generator
907
+ crew_manager = CaseBreakdownCrew(api_key)
908
+
909
+ # Extract metadata
910
+ with st.spinner("Extracting document metadata..."):
911
+ title, author = crew_manager.extract_metadata(st.session_state.first_file_text)
912
+
913
+ # Display extracted metadata with option to edit
914
+ st.subheader("Extracted Document Information")
915
+ title = st.text_input("Case Study Title:", title)
916
+ author = st.text_input("Author:", author)
917
+
918
+ # Initialize sections based on template
919
+ sections = {
920
+ "Case Synopsis": """ Summarize the case in 3–4 paragraphs of a total 300 words. Explain the company's background, the industry it operates in,
921
+ and the key challenge or strategic decision it faces. Discuss any turning points or dilemmas. Why is this case relevant for
922
+ business students?
923
+ Position in Course: In 1–2 sentences, describe which type of course this case is best suited for
924
+ (e.g., Operations Strategy, AI in Business, Global Supply Chain). What key topics does it help students understand""",
925
+
926
+
927
+ "Learning Objectives": """List 8–10 learning objectives for this case. A total of 300 words. What should students understand after analyzing this case? Focus on
928
+ leadership decisions, operational insights, AI adoption, or ethical concerns""",
929
+
930
+ "Teaching Strategies":
931
+ """
932
+ Describe 8–10 ways an instructor can effectively teach this case. Should they use role-playing? A total of 350 words.
933
+ A structured debate? Analyzing real-world examples? How can students actively engage with the material? 1st point should be Approach and second point should be Objective
934
+ """,
935
+ "Suggested Teaching Plan":
936
+ """
937
+ Outline a structured teaching plan for this case. What should be covered first? A total of 350 words.
938
+ When should student activities be introduced? How should the case discussion conclude?
939
+ """,
940
+
941
+ "Key Points and Insights":
942
+ """
943
+ List 10 key insights students should take from this case. What makes this case unique? A total of 300 words.
944
+ What are the most important strategic, operational, or ethical considerations?
945
+ """,
946
+
947
+ "Further Insights":
948
+ """
949
+ Provide additional insights that go beyond the case. How does this case relate to
950
+ larger business trends? What external factors (e.g., regulation, innovation, geopolitical forces) could impact the situation? A total of 400 words.
951
+ """,
952
+
953
+ "Discussion Questions & Answers":
954
+ """
955
+ Create 8–10 discussion questions about the case. What are the key strategic dilemmas?
956
+ Where do trade-offs exist? How can students critically analyze the company's decisions? Provide concise answers. A total of 450 words.
957
+ """,
958
+
959
+ "Assignment Exercises":
960
+ """
961
+ List 8–10 in-depth assignments that encourage strategic thinking, data analysis,
962
+ or real-world application. Include a mix of strategy proposals, financial analysis,
963
+ role-playing exercises, and ethical debates. Define the expected format
964
+ (e.g., business report, presentation, comparative analysis) and objective of each exercise. A total of 550- 600 words.
965
+ """,
966
+
967
+ "Automated Conversation":
968
+ """
969
+ Write an AI-generated conversation between three personas relevant to the case. A total of 650- 700 words.
970
+ One should be an executive making a strategic decision, another should be an expert,
971
+ and the third should be skeptical or resistant. The conversation should explore the challenges, risks, and strategic importance of the case.
972
+ """,
973
+
974
+ "Case Suggestions":
975
+ """
976
+ List 10 ways to make this case more interactive. Should students role-play as executives?
977
+ Simulate a crisis response? Conduct a competitive market analysis?
978
+ Suggest unique, hands-on ways to engage with the case. A total of 550 words.
979
+ """
980
+ }
981
+
982
+ # Generate button
983
+ if st.button("Generate Case Breakdown", key="breakdown_button"):
984
+ # Create tabs for content and review
985
+ content_tab, review_tab = st.tabs(["Generated Content", "Content Review"])
986
+
987
+ # Generate and display content
988
+ sections_content = {}
989
+ reviews = {}
990
+
991
+ with st.spinner("Generating content... This may take several minutes."):
992
+ progress_bar = st.progress(0)
993
+
994
+ with content_tab:
995
+ for i, (section_name, prompt) in enumerate(sections.items()):
996
+ # Generate content using the CrewAI agents
997
+ st.info(f"Generating {section_name}...")
998
+ content = crew_manager.generate_section_content(st.session_state.combined_text, section_name, prompt)
999
+ sections_content[section_name] = content
1000
+
1001
+ # Generate review
1002
+ review = crew_manager.review_content(content, section_name)
1003
+ reviews[section_name] = review
1004
+
1005
+ # Display content in preview
1006
+ st.subheader(section_name)
1007
+ st.markdown(content)
1008
+
1009
+ # Update progress
1010
+ progress_bar.progress((i + 1) / len(sections))
1011
+
1012
+ # Store generated content in session state
1013
+ st.session_state.sections_content = sections_content
1014
+ st.session_state.reviews = reviews
1015
+ st.session_state.title = title
1016
+ st.session_state.author = author
1017
+ st.session_state.breakdown_generated = True
1018
+
1019
+ # Display reviews in review tab
1020
+ with review_tab:
1021
+ for section_name, review in reviews.items():
1022
+ st.subheader(f"{section_name} Review")
1023
+ st.markdown(review)
1024
+
1025
+ # Generate and store the document for later
1026
+ doc_generator = DocumentGenerator()
1027
+ doc = doc_generator.create_word_document(
1028
+ title=title,
1029
+ author=author,
1030
+ sections_content=sections_content
1031
+ )
1032
+
1033
+ # Save document to temporary file
1034
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.docx') as tmp_file:
1035
+ doc.save(tmp_file.name)
1036
+ st.session_state.doc_path = tmp_file.name
1037
+
1038
+ # Allow the user to download the document
1039
+ with open(st.session_state.doc_path, 'rb') as file:
1040
+ st.download_button(
1041
+ label="📥 Download Case Breakdown (DOCX)",
1042
+ data=file,
1043
+ file_name=f"{title.lower().replace(' ', '_')}_case_breakdown.docx",
1044
+ mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1045
+ key="download_docx_button"
1046
+ )
1047
+
1048
+ else:
1049
+ # If we already have generated content, display it without regenerating
1050
+ st.subheader("Extracted Document Information")
1051
+ st.text_input("Case Study Title:", value=st.session_state.title, key="title_display", disabled=True)
1052
+ st.text_input("Author:", value=st.session_state.author, key="author_display", disabled=True)
1053
+
1054
+ # Display the generated content in tabs
1055
+ content_tab, review_tab = st.tabs(["Generated Content", "Content Review"])
1056
+
1057
+ with content_tab:
1058
+ for section_name, content in st.session_state.sections_content.items():
1059
+ st.subheader(section_name)
1060
+ st.markdown(content)
1061
+
1062
+ with review_tab:
1063
+ for section_name, review in st.session_state.reviews.items():
1064
+ st.subheader(f"{section_name} Review")
1065
+ st.markdown(review)
1066
+
1067
+ # Allow the user to download the document without regenerating
1068
+ with open(st.session_state.doc_path, 'rb') as file:
1069
+ st.download_button(
1070
+ label="📥 Download Case Breakdown (DOCX)",
1071
+ data=file,
1072
+ file_name=f"{st.session_state.title.lower().replace(' ', '_')}_case_breakdown.docx",
1073
+ mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1074
+ key="download_docx_button"
1075
+ )
1076
+
1077
+ #------------------ Teaching Plan Generator Tab ------------------#
1078
+ with tab2:
1079
+ st.header("Teaching Plan Generator")
1080
+ st.write("Generate a comprehensive 2-hour teaching plan for the case study.")
1081
+
1082
+ if not st.session_state.teaching_plan_generated:
1083
+ if not api_key:
1084
+ st.warning("⚠️ Please enter an API key in the sidebar before proceeding.")
1085
+ else:
1086
+ # Create a button to start generation
1087
+ if st.button("Generate Teaching Plan", key="teaching_plan_button"):
1088
+ try:
1089
+ # Create placeholders for UI updates
1090
+ progress_placeholder = st.empty()
1091
+ agent_status_placeholder = st.empty()
1092
+
1093
+ # Create a tracker and set its placeholder
1094
+ tracker = AgentTracker()
1095
+ tracker.set_placeholder(agent_status_placeholder)
1096
+
1097
+ # Initialize progress bar
1098
+ progress_bar = progress_placeholder.progress(0)
1099
+
1100
+ # Select LLM provider
1101
+ llm_provider = "gemini" if api_key_source == "Google (Gemini)" else "openai"
1102
+
1103
+ # Update progress
1104
+ progress_bar.progress(10)
1105
+ st.info("🔍 Initializing crew and analyzing documents...")
1106
+
1107
+ # Create crew
1108
+ crew = create_teaching_plan_crew(st.session_state.temp_file_paths, llm_provider)
1109
+
1110
+ # Update progress
1111
+ progress_bar.progress(30)
1112
+ st.info("📖 Document analysis in progress...")
1113
+
1114
+ # Start the execution
1115
+ start_time = time.time()
1116
+ result = crew.kickoff(inputs={"file_path": st.session_state.temp_file_paths[0]})
1117
+ result_text = str(result) # Convert CrewOutput to string
1118
+
1119
+ # Update progress
1120
+ progress_bar.progress(90)
1121
+ st.info("✏️ Finalizing teaching plan...")
1122
+
1123
+ # Calculate execution time
1124
+ execution_time = time.time() - start_time
1125
+
1126
+ # Complete progress
1127
+ progress_bar.progress(100)
1128
+ agent_status_placeholder.empty() # Clear the agent status
1129
+
1130
+ # Save results to session state
1131
+ st.session_state.teaching_plan_execution_time = execution_time
1132
+ st.session_state.teaching_plan_result = result_text
1133
+ st.session_state.teaching_plan_generated = True
1134
+
1135
+ # Display the result
1136
+ st.success(f"✅ Teaching plan generated successfully in {execution_time:.2f} seconds!")
1137
+ st.subheader("📝 Generated Teaching Plan")
1138
+ st.markdown(result_text)
1139
+
1140
+ # Provide download option
1141
+ st.download_button(
1142
+ label="📥 Download Teaching Plan (Markdown)",
1143
+ data=result_text,
1144
+ file_name="teaching_plan.md",
1145
+ mime="text/markdown",
1146
+ )
1147
+
1148
+ except Exception as e:
1149
+ st.error(f"❌ An error occurred during processing: {str(e)}")
1150
+
1151
+ # Show instructions
1152
+ with st.expander("ℹ️ How it works"):
1153
+ st.write("""
1154
+ The Teaching Plan Generator creates a comprehensive 2-hour lesson plan with:
1155
+
1156
+ 1. **Clear learning objectives** tied to the case study
1157
+ 2. **Structured timeline** with time allocations for each activity
1158
+ 3. **Engaging activities** for effective student learning
1159
+ 4. **Discussion questions** to promote critical thinking
1160
+ 5. **Assessment strategies** to measure understanding
1161
+
1162
+ The AI uses a team of specialized agents to analyze your case study, create a draft plan,
1163
+ review it for quality, and finalize it into a polished teaching resource.
1164
+ """)
1165
+ else:
1166
+ # Display the previously generated content
1167
+ st.success(f"✅ Teaching plan generated successfully in {st.session_state.teaching_plan_execution_time:.2f} seconds!")
1168
+ st.subheader("📝 Generated Teaching Plan")
1169
+ st.markdown(st.session_state.teaching_plan_result)
1170
+
1171
+ # Provide download option
1172
+ st.download_button(
1173
+ label="📥 Download Teaching Plan (Markdown)",
1174
+ data=st.session_state.teaching_plan_result,
1175
+ file_name="teaching_plan.md",
1176
+ mime="text/markdown",
1177
+ )
1178
+
1179
+ #------------------ Board Plan Generator Tab ------------------#
1180
+ with tab3:
1181
+ st.header("Board Plan Generator")
1182
+ st.write("Generate a structured board plan analysis for the case study.")
1183
+
1184
+ if not st.session_state.board_plan_generated:
1185
+ if not api_key:
1186
+ st.warning("⚠️ Please enter an API key in the sidebar before proceeding.")
1187
+ else:
1188
+ # Create a button to start generation
1189
+ if st.button("Generate Board Plan", key="board_plan_button"):
1190
+ try:
1191
+ # Select LLM provider
1192
+ llm_provider = "gemini" if api_key_source == "Google (Gemini)" else "openai"
1193
+
1194
+ # Initialize the board plan analyzer
1195
+ analyzer = BoardPlanAnalyzer(llm_provider=llm_provider)
1196
+
1197
+ with st.spinner("Analyzing case study to generate board plan..."):
1198
+ # Get content from first uploaded file
1199
+ file = st.session_state.uploaded_files[0]
1200
+ pdf_content = file.getvalue()
1201
+
1202
+ # Process the case study
1203
+ analysis_result = analyzer.process_case_study(pdf_content)
1204
+
1205
+ # Store in session state
1206
+ st.session_state.board_plan_result = analysis_result
1207
+ st.session_state.board_plan_generated = True
1208
+
1209
+ # Generate markdown for download
1210
+ markdown_content = "# Board Plan Analysis\n\n"
1211
+ for board in analysis_result['boards']:
1212
+ markdown_content += f"## {board['title']}\n\n"
1213
+ for point in board['points']:
1214
+ markdown_content += f"* {point}\n"
1215
+ markdown_content += "\n"
1216
+
1217
+ if 'risk_assessment' in analysis_result:
1218
+ markdown_content += f"## {analysis_result['risk_assessment']['title']}\n\n"
1219
+ for point in analysis_result['risk_assessment']['points']:
1220
+ markdown_content += f"* {point}\n"
1221
+
1222
+ st.session_state.board_plan_markdown = markdown_content
1223
+
1224
+ st.success("Board plan generated successfully!")
1225
+
1226
+ # Display the analysis results
1227
+ st.subheader("Board Plan Analysis")
1228
+ for board in analysis_result['boards']:
1229
+ with st.expander(board['title'], expanded=True):
1230
+ for point in board['points']:
1231
+ st.markdown(f"• {point}")
1232
+
1233
+ if 'risk_assessment' in analysis_result:
1234
+ st.subheader("Risk Assessment")
1235
+ for point in analysis_result['risk_assessment']['points']:
1236
+ st.markdown(f"• {point}")
1237
+
1238
+ # Provide download option
1239
+ st.download_button(
1240
+ label="📥 Download Board Plan (Markdown)",
1241
+ data=st.session_state.board_plan_markdown,
1242
+ file_name="board_plan.md",
1243
+ mime="text/markdown",
1244
+ )
1245
+
1246
+ except Exception as e:
1247
+ st.error(f"An error occurred: {str(e)}")
1248
+ st.error("Please ensure the file is not encrypted and contains extractable text.")
1249
+
1250
+ # Show instructions
1251
+ with st.expander("ℹ️ How it works"):
1252
+ st.write("""
1253
+ The Board Plan Generator creates a structured analysis with these components:
1254
+
1255
+ 1. **Main concept/technology** being discussed in the case study
1256
+ 2. **Industry suitability and context** for application
1257
+ 3. **Benefits and impact** of implementing the solution
1258
+ 4. **Key roles and perspectives** from stakeholders
1259
+ 5. **Implementation details** including data requirements, scaling, and metrics
1260
+ 6. **Next steps and recommendations** for moving forward
1261
+ 7. **Risk assessment** to identify potential challenges
1262
+
1263
+ The generator provides a structured framework perfect for teaching, presentations,
1264
+ or boardroom discussions.
1265
+ """)
1266
+ else:
1267
+ # Display the previously generated content
1268
+ st.success("Board plan generated successfully!")
1269
+
1270
+ # Display the analysis results
1271
+ st.subheader("Board Plan Analysis")
1272
+ for board in st.session_state.board_plan_result['boards']:
1273
+ with st.expander(board['title'], expanded=True):
1274
+ for point in board['points']:
1275
+ st.markdown(f"• {point}")
1276
+
1277
+ if 'risk_assessment' in st.session_state.board_plan_result:
1278
+ st.subheader("Risk Assessment")
1279
+ for point in st.session_state.board_plan_result['risk_assessment']['points']:
1280
+ st.markdown(f"• {point}")
1281
+
1282
+ # Provide download option
1283
+ st.download_button(
1284
+ label="📥 Download Board Plan (Markdown)",
1285
+ data=st.session_state.board_plan_markdown,
1286
+ file_name="board_plan.md",
1287
+ mime="text/markdown",
1288
+ )
1289
+
1290
+ else:
1291
+ # Display welcome message and instructions when no files are uploaded
1292
+ st.markdown("""
1293
+ ## Welcome to the Case Study Analysis Suite
1294
+
1295
+ This application provides three powerful tools for analyzing case studies:
1296
+
1297
+ 1. **Case Breakdown Generator**: Creates a comprehensive breakdown of the case with sections for teaching purposes, formatted as a DOCX document.
1298
+
1299
+ 2. **Teaching Plan Generator**: Develops a 2-hour lesson plan with activities, discussions, and assessments based on the case study.
1300
+
1301
+ 3. **Board Plan Generator**: Produces a structured board plan analysis with key insights and implementation details.
1302
+
1303
+ ### Getting Started
1304
+
1305
+ 1. Choose your API provider in the sidebar (Google Gemini or OpenAI)
1306
+ 2. Enter your API key
1307
+ 3. Upload one or more case study files in PDF, DOCX, PPT, or PPTX format
1308
+ 4. Select the generator tab you want to use
1309
+ 5. Click the generate button and wait for the results
1310
+
1311
+ Each generator produces downloadable content in an appropriate format for your use.
1312
+ """)
1313
+
1314
+ # Footer
1315
+ st.divider()
1316
+ st.caption("Created with CrewAI, Streamlit, and Gemini • Built by Arun Kashyap • © 2025")
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ crewai==0.108.0
2
+ docx2txt==0.8
3
+ langchain==0.3.20
4
+ litellm==1.63.11
5
+ pydantic==2.10.6
6
+ PyPDF2==3.0.1
7
+ python-dotenv==1.0.1
8
+ python_docx==1.1.2
9
+ python_pptx==1.0.2
10
+ streamlit==1.41.1