Untitled
unknown
python
8 months ago
24 kB
5
Indexable
import json import re from pathlib import Path import streamlit as st from langchain_groq import ChatGroq from langchain.prompts import PromptTemplate from langchain.chains import LLMChain import os import json import streamlit as st import reveal_slides as rs # Import the reveal_slides module import sys DATA_DIR = Path(__file__).parent / "data" DATA_DIR.mkdir(exist_ok=True) def load_json(file_path): if file_path.exists(): with open(file_path, 'r') as f: return json.load(f) return {} def save_json(data, file_path): with open(file_path, 'w') as f: json.dump(data, f, indent=2) def get_subject_file_paths(subject): base_name = subject.lower().replace(' ', '_') return { 'content': DATA_DIR / f"{base_name}_lesson_content.json", 'config': DATA_DIR / f"{base_name}_slide_config.json" } class SlideComponent: def __init__(self, content: str, config: dict, initial_state: dict): self.content = content self.config = config self.initial_state = initial_state def render(self): return rs.slides( self.content, # height=self.config.get('height', 600), height=self.config.get('height', '100%'), theme=self.config.get('theme', 'black'), config=self.config, initial_state=self.initial_state, markdown_props={"data-separator-vertical": "^--$"}, key=f"slides_{self.config.get('base_font_size', 24)}_{self.config.get('heading_scale', 1.5)}" ) class LessonPresenter: def __init__(self, content: dict): self.content = content self.set_subject_content(None) self.callback_func = None # Callback function placeholder def set_subject_content(self, subject: str): if subject is None: self.subject = {"subject": "Unknown", "lessons": []} return for s in self.content.get("subjects", []): if s.get("subject", "").lower() == subject.lower(): self.subject = s return print(f"Warning: Subject '{subject}' not found in JSON data") self.subject = {"subject": "Unknown", "lessons": []} def json_to_slides(self) -> str: subject_name = self.subject.get('subject', 'Unknown Subject') slides = f"# Subject Matter: {subject_name}\nPresentation By ClassPilot\n---\n" for lesson in self.subject.get("lessons", []): slides += f"## {lesson.get('title', 'Untitled Lesson')}\n---\n" for page in lesson.get("pages", []): slides += f"\n### {page.get('title', 'Untitled Page')}\n" for subtitle in page.get("subtitles", []): slides += f"#### {subtitle}\n" slides += f"{page.get('content', '')}\n---\n" if not self.subject.get("lessons"): slides += "## No lessons available for this subject\n---\n" slides += "# End\n## Good Luck!" return slides def update_page(self, selected_lesson: str, selected_page: str, new_title: str, new_subtitles: str, new_content: str) -> bool: lesson_data = next((l for l in self.subject.get("lessons", []) if l["title"] == selected_lesson), None) if lesson_data: page_data = next((p for p in lesson_data.get("pages", []) if p["title"] == selected_page), None) if page_data: page_data["title"] = new_title page_data["subtitles"] = new_subtitles.split("\n") page_data["content"] = new_content # Save the updated content # if self.save_content(self.content.get("subjects")[0].get("subject")): if self.save_content("python"): # Call the callback function if provided if self.callback_func: self.callback_func(self.content) return True return False def set_callback_func(self, func): self.callback_func = func def create_slide_component(self, content: str, config: dict, initial_state: dict): full_content = self.generate_custom_css(config.get('base_font_size', 24), config.get('heading_scale', 1.5)) + content return SlideComponent(full_content, config, initial_state) def generate_custom_css(self, base_font_size: int, heading_scale: float) -> str: return f""" <style> .reveal .slides section {{ height: 100%; overflow-y: auto !important; overflow-x: hidden !important; }} .reveal {{ height: 100% !important; font-size: {base_font_size}px !important; }} .reveal .slides {{ height: 100% !important; }} .reveal h1 {{ font-size: {base_font_size * heading_scale}px !important; }} .reveal h2 {{ font-size: {base_font_size * (heading_scale * 0.9)}px !important; }} .reveal h3 {{ font-size: {base_font_size * (heading_scale * 0.8)}px !important; }} .reveal p, .reveal li {{ font-size: {base_font_size}px !important; }} .reveal .slides section {{ color: #f0f0f0 !important; }} .reveal pre {{ box-shadow: none !important; background-color: rgba(0,0,0,0.3) !important; margin: 15px 0 !important; padding: 10px !important; border-radius: 5px !important; }} .reveal pre code {{ max-height: 400px !important; font-size: {base_font_size * 0.7}px !important; line-height: 1.3em !important; padding: 0 !important; }} .hljs {{ background: transparent !important; }} .reveal.white pre, .reveal.beige pre {{ background-color: rgba(0,0,0,0.05) !important; }} .reveal.white pre code, .reveal.beige pre code {{ color: #333 !important; }} .reveal.black pre, .reveal.league pre, .reveal.night pre {{ background-color: rgba(255,255,255,0.1) !important; }} .reveal.black pre code, .reveal.league pre code, .reveal.night pre code {{ color: #f0f0f0 !important; }} </style> """ def save_content(self,name): path = DATA_DIR / f"{name}_lesson_content.json", save_json(self.content,path) current_dir = os.path.dirname(os.path.abspath(__file__)) json_path = os.path.join(current_dir,'data', f'{name}_lesson_content.json') print(json_path) json_path = os.path.normpath(json_path) try: with open(json_path, 'w') as f: json.dump(self.content, f, indent=2) print("Content saved successfully") return True except Exception as e: print(f"Error saving content: {str(e)}") return False def load_content(name): current_dir = os.path.dirname(os.path.abspath(__file__)) json_path = os.path.join(current_dir, 'data', f'{name}_lesson_content.json') json_path = os.path.normpath(json_path) try: with open(json_path, 'r') as f: content = json.load(f) print("JSON file loaded successfully") except FileNotFoundError: print(f"Error: Could not find the file at {json_path}") content = {"subjects": []} except json.JSONDecodeError: print(f"Error: The file at {json_path} is not a valid JSON file") content = {"subjects": []} return content def save_config(config): config_data = { "config": config, } current_dir = os.path.dirname(os.path.abspath(__file__)) config_path = os.path.join(current_dir,'data', 'slide_config.json') config_path = os.path.normpath(config_path) with open(config_path, 'w') as f: json.dump(config_data, f, indent=2) def create_editor_screen(presenter: LessonPresenter): st.title("Lesson Presentation System") subjects = [s["subject"] for s in presenter.content["subjects"]] selected_subject = st.sidebar.selectbox("Select Subject", subjects) presenter.set_subject_content(selected_subject) with st.expander("Edit Page Content", expanded=False): lessons = [l["title"] for l in presenter.subject.get("lessons", [])] selected_lesson = st.selectbox("Select Lesson", lessons) selected_lesson_data = next((l for l in presenter.subject.get("lessons", []) if l["title"] == selected_lesson), None) if selected_lesson_data: pages = [p["title"] for p in selected_lesson_data.get("pages", [])] selected_page = st.selectbox("Select Page", pages) selected_page_data = next((p for p in selected_lesson_data.get("pages", []) if p["title"] == selected_page), None) if selected_page_data: new_title = st.text_input("Edit Page Title", selected_page_data["title"]) new_subtitles = st.text_area("Edit Subtitles (one per line)", "\n".join(selected_page_data.get("subtitles", []))) new_content = st.text_area("Edit Page Content", selected_page_data["content"], height=300) if st.button("Update Page"): if presenter.update_page(selected_lesson, selected_page, new_title, new_subtitles, new_content): st.success("Page updated and saved successfully!") st.rerun() else: st.error("Failed to update and save page. Please check your input.") st.sidebar.header("Slide Design") config = { "base_font_size": st.sidebar.number_input("Base Font Size (px)", value=24, min_value=10, max_value=50, step=1), "heading_scale": st.sidebar.slider("Heading Scale", min_value=1.0, max_value=3.0, value=1.5, step=0.1), "theme": st.sidebar.selectbox("Theme", ["black", "white", "league", "beige", "sky", "night", "serif", "simple", "solarized"], index=0), "height": st.sidebar.number_input("Container Height", value=600, step=50), "scrollable": True, } if st.sidebar.button("Finish"): save_config(config) presenter.save_content("zibi") st.sidebar.success("Configuration saved successfully!") st.rerun() slides_component = presenter.create_slide_component(presenter.json_to_slides(), config, initial_state={}) slides_component.render() class LessonContentGenerator: def __init__(self, api_key): self.llm = ChatGroq(api_key=api_key) def generate_content(self, subject, num_lessons, pages_per_lesson, subtitles_per_page, sentences_per_page): prompt_template = PromptTemplate( input_variables=["subject", "num_lessons", "pages_per_lesson", "subtitles_per_page", "sentences_per_page"], template=""" Create a detailed lesson plan on the subject of {subject}. Provide EXACTLY the following: 1. {num_lessons} lesson titles 2. For each lesson, EXACTLY {pages_per_lesson} page titles 3. For each page, EXACTLY {subtitles_per_page} subtitles 4. For each page, a content summary of EXACTLY {sentences_per_page} sentences Format your response as a JSON structure with the following format: {{ "subject": "{subject}", "lessons": [ {{ "title": "Lesson Title", "pages": [ {{ "title": "Page Title", "subtitles": ["Subtitle 1", "Subtitle 2", ...], "content": "Detailed content for the page (EXACTLY {sentences_per_page} sentences)" }}, ... ] }}, ... ] }} Ensure the content is informative, relevant to the subject of {subject}, and follows this exact JSON structure. Do not use any backslashes in the JSON keys. IMPORTANT: Strictly adhere to the numbers provided: {num_lessons} lessons, {pages_per_lesson} pages per lesson, {subtitles_per_page} subtitles per page, and {sentences_per_page} sentences per page. """ ) chain = LLMChain(llm=self.llm, prompt=prompt_template) result = chain.invoke({ "subject": subject, "num_lessons": num_lessons, "pages_per_lesson": pages_per_lesson, "subtitles_per_page": subtitles_per_page, "sentences_per_page": sentences_per_page }) return self._process_content(result['text'], subject, num_lessons, pages_per_lesson, subtitles_per_page, sentences_per_page) def _process_content(self, content, subject, num_lessons, pages_per_lesson, subtitles_per_page, sentences_per_page): try: content = json.loads(content) processed_content = { "subject": subject, "lessons": [] } for i in range(num_lessons): lesson = content['lessons'][i] if i < len(content['lessons']) else {"title": f"Lesson {i+1}", "pages": []} processed_lesson = { "title": lesson['title'], "pages": [] } for j in range(pages_per_lesson): page = lesson['pages'][j] if j < len(lesson.get('pages', [])) else {"title": f"Page {j+1}", "subtitles": [], "content": ""} processed_page = { "title": page['title'], "subtitles": page.get('subtitles', [])[:subtitles_per_page], "content": '. '.join(page.get('content', '').split('.')[:sentences_per_page]) + '.' } while len(processed_page['subtitles']) < subtitles_per_page: processed_page['subtitles'].append(f"Subtitle {len(processed_page['subtitles'])+1}") processed_lesson['pages'].append(processed_page) processed_content['lessons'].append(processed_lesson) return json.dumps(processed_content, indent=2) except json.JSONDecodeError: raise ValueError("The generated content is not valid JSON.") except Exception as e: raise Exception(f"An error occurred while processing the content: {str(e)}") class ContentEditor: def __init__(self, llm): self.llm = llm def expand_content(self, subtitle, page_title, content): prompt = PromptTemplate( input_variables=["subtitle", "page_title", "content"], template="Expand on '{subtitle}' in the context of {page_title}. Original content: {content}" ) chain = LLMChain(llm=self.llm, prompt=prompt) expanded_content = chain.invoke({ "subtitle": subtitle, "page_title": page_title, "content": content }) return expanded_content['text'] def summarize_content(self, page_title, summary_length, content): prompt = PromptTemplate( input_variables=["page_title", "summary_length", "content"], template="Summarize the content about {page_title} in a {summary_length} summary. Original content: {content}" ) chain = LLMChain(llm=self.llm, prompt=prompt) summarized_content = chain.invoke({ "page_title": page_title, "summary_length": summary_length.lower(), "content": content }) return summarized_content['text'] class App: def __init__(self, api_key,isTeacher=True): self.generator = LessonContentGenerator(api_key) self.editor = ContentEditor(ChatGroq(api_key=api_key)) self.lessonPresentor = None def run(self,isTeacher=True): st.set_page_config(layout="wide") if 'current_page' not in st.session_state: st.session_state.current_page = "home" if st.session_state.current_page == "home": self._show_home_page() elif st.session_state.current_page == "edit": content = load_content("python") self.lessonPresentor = LessonPresenter(content) create_editor_screen(self.lessonPresentor) else: self._show_editor_page() def _show_home_page(self): st.title("Home Page") st.write("Welcome to the Interactive Lesson Content Editor!") if st.button("Create New Lesson Content"): st.session_state.current_page = "editor" st.session_state.content_generated = False st.session_state.json_content = {"subject": "", "lessons": []} st.rerun() # Display saved lesson contents saved_files = Path('.').glob('*_lesson_content.json') for file in saved_files: if st.button(f"Edit {file.stem}"): with open(file, 'r') as f: st.session_state.json_content = json.load(f) st.session_state.content_generated = True st.session_state.current_page = "editor" st.rerun() def _show_editor_page(self): st.markdown(""" <div style="display: flex; justify-content: space-between; align-items: center;"> <h1 style="margin: 0;">Interactive Lesson Content Editor</h1> <div id="restart-button-container"></div> </div> """, unsafe_allow_html=True) if 'json_content' not in st.session_state: st.session_state.json_content = {"subject": "", "lessons": []} if 'content_generated' not in st.session_state: st.session_state.content_generated = False if st.session_state.content_generated: self._show_restart_button() self._handle_content_editing() else: self._handle_content_generation() def _show_restart_button(self): if st.button("Restart and select a new subject"): st.session_state.clear() st.rerun() def _handle_content_generation(self): subject = st.text_input("Enter the subject for the lesson content:") col1, col2 = st.columns(2) with col1: num_lessons = st.number_input("Number of lesson titles:", min_value=1, value=2, step=1) pages_per_lesson = st.number_input("Pages per lesson:", min_value=1, value=2, step=1) with col2: subtitles_per_page = st.number_input("Subtitles per page:", min_value=1, value=3, step=1) sentences_per_page = st.number_input("Sentences per page:", min_value=1, value=3, step=1) if subject and subject != st.session_state.json_content.get("subject", ""): if st.button("Generate Content"): with st.spinner("Generating lesson content..."): try: generated_content = self.generator.generate_content( subject, num_lessons, pages_per_lesson, subtitles_per_page, sentences_per_page ) st.session_state.json_content = json.loads(generated_content) st.session_state.content_generated = True st.success("Lesson content generated successfully!") st.rerun() except Exception as e: st.error(f"An error occurred: {str(e)}. Please try again.") def _handle_content_editing(self): if not st.session_state.json_content.get("lessons"): st.warning("No lessons available. Please generate content first.") return st.header("Lesson Content Editor") _, _, page_title, page = self._select_lesson_and_page() st.subheader(f"📝 Content: {page_title}") with st.expander("View original content", expanded=True): st.write(page.get("content", "No content available.")) with st.expander("View subtitles"): for subtitle in page.get("subtitles", []): st.write(f"• {subtitle}") st.subheader("🛠️ Edit Content") action = st.radio("What would you like to do with the content?", ["Accept", "Expand", "Summarize"]) if action == "Accept": st.success("Content accepted without changes.") elif action == "Expand": self._handle_expand_action(page, page_title) elif action == "Summarize": self._handle_summarize_action(page, page_title) col1, col2 = st.columns(2) with col1: if st.button("Save Changes"): self._save_changes() with col2: if st.button("Next Step"): self._save_changes() st.session_state.current_page = "edit" st.rerun() def _select_lesson_and_page(self): col1, col2 = st.columns(2) with col1: lesson_titles = [lesson.get("title", f"Lesson {i+1}") for i, lesson in enumerate(st.session_state.json_content["lessons"])] lesson_title = st.selectbox("📚 Select a lesson", lesson_titles) lesson_index = lesson_titles.index(lesson_title) lesson = st.session_state.json_content["lessons"][lesson_index] with col2: if not lesson.get("pages"): st.warning(f"No pages available for lesson: {lesson_title}") return None, None, None, None page_titles = [page.get("title", f"Page {i+1}") for i, page in enumerate(lesson["pages"])] page_title = st.selectbox("📄 Select a page", page_titles) page_index = page_titles.index(page_title) page = lesson["pages"][page_index] return lesson_title, lesson, page_title, page def _handle_expand_action(self, page, page_title): subtitles = page.get("subtitles", []) if subtitles: subtitle = st.selectbox("Select a subtitle to expand", subtitles) if st.button("Expand Content"): with st.spinner("Expanding content..."): expanded_content = self.editor.expand_content(subtitle, page_title, page.get("content", "")) st.subheader("Expanded Content") st.write(expanded_content) page["content"] = expanded_content st.success("Content expanded successfully!") else: st.warning("No subtitles available for expansion.") def _handle_summarize_action(self, page, page_title): summary_length = st.select_slider("Select summary length", options=["Short", "Medium", "Long"]) if st.button("Summarize Content"): with st.spinner("Summarizing content..."): summarized_content = self.editor.summarize_content(page_title, summary_length, page.get("content", "")) st.subheader("Summarized Content") st.write(summarized_content) page["content"] = summarized_content st.success("Content summarized successfully!") def _save_changes(self): filename = f'{st.session_state.json_content["subject"].replace(" ", "_").lower()}_lesson_content.json' with open(filename, 'w') as f: json.dump(st.session_state.json_content, f, indent=2) st.success(f"Changes saved successfully to {filename}!") if __name__ == "__main__": api_key = "" #debug app = App(api_key,True) app.run()
Editor is loading...
Leave a Comment