import gradio as gr import os import re import requests from io import BytesIO from PIL import Image from pdfminer.high_level import extract_text from groq import Groq from docx import Document from docx.shared import Inches import time def extract_text_with_pdfminer(pdf_path): return extract_text(pdf_path) def clean_text(text): text = text.replace('\f', ' ') text = re.sub(r'\s+\n', '\n', text) text = re.sub(r'\n+', '\n', text) return text.strip() def extract_sections(text): text = clean_text(text) pattern = r'^(Section\s+\d+:\s+.*)$' headings = [(m.start(), m.group(0).strip()) for m in re.finditer(pattern, text, re.MULTILINE)] sections = {} if headings: if headings[0][0] > 0: preamble = text[:headings[0][0]].strip() if preamble: sections["Title"] = preamble for i, (start, heading) in enumerate(headings): end = headings[i+1][0] if i+1 < len(headings) else len(text) section_text = text[start:end].strip() content = section_text[len(heading):].strip() sections[heading] = content else: sections["FullText"] = text return sections def generate_with_groq(prompt, groq_api_key): client = Groq(api_key=groq_api_key) response = client.chat.completions.create( model="llama-3.3-70b-versatile", messages=[{"role": "user", "content": prompt}], temperature=0.3, max_tokens=2000 ) return response.choices[0].message.content def google_image_search(query, api_key, cx, num=1): url = "https://www.googleapis.com/customsearch/v1" params = {"q": query, "key": api_key, "cx": cx, "searchType": "image", "num": num} try: response = requests.get(url, params=params, timeout=10) response.raise_for_status() results = response.json() if "items" in results and len(results["items"]) > 0: return results["items"][0]["link"] except Exception as e: print(f"Image search error: {str(e)}") return None def process_pdf(pdf_file, groq_api_key, google_api_key, cx, progress=gr.Progress()): if not pdf_file: return None, None, "❌ Please upload a PDF file!" if not groq_api_key or not google_api_key or not cx: return None, None, "❌ Please provide all API credentials!" try: progress(0.1, desc="📄 Extracting text from PDF...") pdf_text = extract_text_with_pdfminer(pdf_file.name) sections = extract_sections(pdf_text) formatted_sections = [(h, c) for h, c in sections.items()] if not formatted_sections: return None, None, "❌ No text extracted from PDF!" notes_per_section = {} images_per_section = {} total_sections = len(formatted_sections) for idx, (heading, content) in enumerate(formatted_sections, start=1): progress_val = 0.1 + (idx/total_sections) * 0.7 progress(progress_val, desc=f"âš™ī¸ Processing section {idx}/{total_sections}...") section = f"{heading}\n\n{content}" try: text_prompt = ( "Please transform the following text section into detailed, " "elaborated lecture notes for students.\n" "Format: Use '# ' for headings, '## ' for subheadings, '* ' for bullets.\n\n" f"Text:\n{section}\n" ) detailed_notes = generate_with_groq(text_prompt, groq_api_key) notes_per_section[idx] = detailed_notes time.sleep(2) image_prompt = f"Generate a 5-word image search query for:\n{section}" image_query = generate_with_groq(image_prompt, groq_api_key).strip().replace('"', '') image_url = google_image_search(image_query, google_api_key, cx) images_per_section[idx] = image_url except Exception as e: notes_per_section[idx] = f"# Section {idx}\n\nError: {str(e)}" images_per_section[idx] = None progress(0.85, desc="📝 Generating summary...") all_notes = "\n\n".join(notes_per_section[idx] for idx in sorted(notes_per_section.keys())) time.sleep(2) summary_prompt = f"Summarize the following lecture notes:\n\n{all_notes}" summary = generate_with_groq(summary_prompt, groq_api_key) progress(0.95, desc="📄 Creating documents...") summary_doc = Document() summary_doc.add_heading("Summary", level=1) for sentence in re.split(r'(?<=[.!?])\s+', summary.strip()): if sentence.strip(): summary_doc.add_paragraph(sentence.strip()) summary_doc.save("lecture_summary.docx") main_doc = Document() for idx in sorted(notes_per_section.keys()): section_text = notes_per_section[idx] lines = section_text.strip().split("\n") if lines: main_doc.add_heading(lines[0].lstrip("# ").strip(), level=1) for line in lines[1:]: if line.strip(): if line.strip().startswith("## "): main_doc.add_heading(line.strip().lstrip("## "), level=2) elif line.strip().startswith("* "): main_doc.add_paragraph(line.strip().lstrip("* "), style="List Bullet") else: main_doc.add_paragraph(line.strip()) image_url = images_per_section.get(idx) if image_url: try: img_response = requests.get(image_url, timeout=10) img_data = BytesIO(img_response.content) main_doc.add_picture(img_data, width=Inches(4)) except: main_doc.add_paragraph("[Image unavailable]") main_doc.add_page_break() main_doc.save("lecture_notes.docx") progress(1.0, desc="✅ Complete!") return ("lecture_notes.docx", "lecture_summary.docx", f"✅ Processed {total_sections} sections!") except Exception as e: return None, None, f"❌ Error: {str(e)}" # Create interface WITHOUT theme parameter (this was causing the error) demo = gr.Blocks(title="LectureForge") with demo: gr.Markdown(""" # 🎓 LectureForge - AI Lecture Notes Generator Transform textbook PDFs into detailed, illustrated lecture notes using AI. **How to use:** 1. Get free API keys: - [Groq API](https://console.groq.com) (6000 requests/day free) - [Google API Key](https://console.cloud.google.com) + [Search Engine CX](https://programmablesearchengine.google.com) 2. Upload your textbook PDF (text-based, not scanned) 3. Enter your API credentials 4. Click "Generate Notes" and wait 2-4 minutes 5. Download your notes! """) with gr.Row(): with gr.Column(): gr.Markdown("### 📤 Upload & Configure") pdf_input = gr.File(label="📄 Upload PDF Textbook", file_types=[".pdf"]) groq_key = gr.Textbox( label="🔑 Groq API Key", type="password", placeholder="gsk_..." ) google_key = gr.Textbox( label="🔑 Google API Key", type="password", placeholder="AIza..." ) cx_input = gr.Textbox( label="🔍 Google Custom Search CX", placeholder="Your search engine ID" ) generate_btn = gr.Button("🚀 Generate Notes", variant="primary") with gr.Column(): gr.Markdown("### đŸ“Ĩ Download Results") status_output = gr.Textbox(label="📊 Status", lines=8) notes_output = gr.File(label="📝 Lecture Notes (.docx)") summary_output = gr.File(label="📋 Summary (.docx)") gr.Markdown(""" --- ### â„šī¸ Tips - Works best with text-based PDFs (not scanned images) - Processing time: ~12-18 seconds per section - Free API limits: Groq (6000 req/day), Google (100 searches/day) --- **Created by:** Ruben Santosh, Vignesh R Nair, Arko Chakraborty **Institution:** Dayananda Sagar University, Bangalore, India """) generate_btn.click( fn=process_pdf, inputs=[pdf_input, groq_key, google_key, cx_input], outputs=[notes_output, summary_output, status_output] ) if __name__ == "__main__": demo.launch()