Spaces:
Runtime error
Runtime error
| 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() | |