Spaces:
Running
Running
| """ | |
| π¨ UI Components for Course Creator AI | |
| Beautiful, modern Gradio components with custom styling and interactions. | |
| """ | |
| import gradio as gr | |
| import json | |
| from typing import Dict, List, Any, Optional, Tuple | |
| from dataclasses import asdict | |
| import logging | |
| from ..types import Course, Lesson, Flashcard, Quiz, ImageAsset | |
| logger = logging.getLogger(__name__) | |
| class CourseGenerationForm: | |
| """Main course generation form component""" | |
| def __init__(self): | |
| self.current_course = None | |
| self.generation_progress = 0 | |
| def create_input_form(self) -> gr.Group: | |
| """Create the main input form for course generation""" | |
| with gr.Group() as form: | |
| gr.HTML(""" | |
| <div class="header-section"> | |
| <h1>π Course Creater AI</h1> | |
| <p class="tagline">Transform any topic into an engaging course with AI</p> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| # Main topic input | |
| topic_input = gr.Textbox( | |
| label="π Course Topic", | |
| placeholder="e.g., Introduction to Machine Learning, Python for Beginners, Digital Marketing Basics", | |
| lines=2, | |
| elem_id="topic-input" | |
| ) | |
| # Course configuration | |
| with gr.Row(): | |
| difficulty_level = gr.Dropdown( | |
| choices=["Beginner", "Intermediate", "Advanced"], | |
| value="Intermediate", | |
| label="π― Difficulty Level", | |
| elem_id="difficulty-select" | |
| ) | |
| duration = gr.Slider( | |
| minimum=0.5, | |
| maximum=8.0, | |
| value=2.0, | |
| step=0.5, | |
| label="β±οΈ Duration (hours)", | |
| elem_id="duration-slider" | |
| ) | |
| with gr.Row(): | |
| num_lessons = gr.Slider( | |
| minimum=3, | |
| maximum=12, | |
| value=6, | |
| step=1, | |
| label="π Number of Lessons", | |
| elem_id="lessons-slider" | |
| ) | |
| target_audience = gr.Dropdown( | |
| choices=["Students", "Professionals", "Hobbyists", "General Public"], | |
| value="General Public", | |
| label="π₯ Target Audience", | |
| elem_id="audience-select" | |
| ) | |
| with gr.Column(scale=1): | |
| # Advanced options | |
| gr.HTML("<h3>π§ Advanced Options</h3>") | |
| include_images = gr.Checkbox( | |
| value=True, | |
| label="π¨ Generate Images", | |
| elem_id="images-checkbox" | |
| ) | |
| include_quizzes = gr.Checkbox( | |
| value=True, | |
| label="π― Include Quizzes", | |
| elem_id="quizzes-checkbox" | |
| ) | |
| include_flashcards = gr.Checkbox( | |
| value=True, | |
| label="π Create Flashcards", | |
| elem_id="flashcards-checkbox" | |
| ) | |
| content_style = gr.Dropdown( | |
| choices=["Conversational", "Technical", "Academic", "Casual"], | |
| value="Conversational", | |
| label="βοΈ Content Style", | |
| elem_id="style-select" | |
| ) | |
| # Generation button | |
| with gr.Row(): | |
| generate_btn = gr.Button( | |
| "π Generate Course", | |
| variant="primary", | |
| size="lg", | |
| elem_id="generate-button" | |
| ) | |
| clear_btn = gr.Button( | |
| "ποΈ Clear", | |
| variant="secondary", | |
| elem_id="clear-button" | |
| ) | |
| return form, { | |
| "topic_input": topic_input, | |
| "difficulty_level": difficulty_level, | |
| "duration": duration, | |
| "num_lessons": num_lessons, | |
| "target_audience": target_audience, | |
| "include_images": include_images, | |
| "include_quizzes": include_quizzes, | |
| "include_flashcards": include_flashcards, | |
| "content_style": content_style, | |
| "generate_btn": generate_btn, | |
| "clear_btn": clear_btn | |
| } | |
| class ProgressTracker: | |
| """Real-time progress tracking component""" | |
| def __init__(self): | |
| self.current_step = 0 | |
| self.total_steps = 6 | |
| self.step_names = [ | |
| "π Researching Topic", | |
| "π Planning Course Structure", | |
| "βοΈ Generating Content", | |
| "π― Creating Assessments", | |
| "π¨ Generating Images", | |
| "π¦ Finalizing Course" | |
| ] | |
| def create_progress_display(self) -> gr.Group: | |
| """Create progress tracking display""" | |
| with gr.Group() as progress_group: | |
| gr.HTML("<h3>π Generation Progress</h3>") | |
| # Progress bar | |
| progress_bar = gr.Progress() | |
| # Current step indicator | |
| current_step_display = gr.HTML( | |
| "<div class='step-indicator'>Ready to generate course</div>", | |
| elem_id="step-indicator" | |
| ) | |
| # Detailed progress log | |
| progress_log = gr.Textbox( | |
| label="π Progress Log", | |
| lines=8, | |
| max_lines=15, | |
| interactive=False, | |
| elem_id="progress-log" | |
| ) | |
| # Status indicators | |
| with gr.Row(): | |
| research_status = gr.HTML("β³ Research", elem_id="research-status") | |
| planning_status = gr.HTML("β³ Planning", elem_id="planning-status") | |
| content_status = gr.HTML("β³ Content", elem_id="content-status") | |
| assessment_status = gr.HTML("β³ Assessment", elem_id="assessment-status") | |
| images_status = gr.HTML("β³ Images", elem_id="images-status") | |
| finalize_status = gr.HTML("β³ Finalize", elem_id="finalize-status") | |
| return progress_group, { | |
| "progress_bar": progress_bar, | |
| "current_step_display": current_step_display, | |
| "progress_log": progress_log, | |
| "status_indicators": { | |
| "research": research_status, | |
| "planning": planning_status, | |
| "content": content_status, | |
| "assessment": assessment_status, | |
| "images": images_status, | |
| "finalize": finalize_status | |
| } | |
| } | |
| def update_progress(self, step: int, message: str, log_entry: str = "") -> Tuple[str, str]: | |
| """Update progress display""" | |
| self.current_step = step | |
| progress_percent = (step / self.total_steps) * 100 | |
| # Update step indicator | |
| if step < len(self.step_names): | |
| step_html = f""" | |
| <div class='step-indicator active'> | |
| <div class='step-icon'>{self.step_names[step].split()[0]}</div> | |
| <div class='step-text'>{self.step_names[step]}</div> | |
| <div class='step-message'>{message}</div> | |
| </div> | |
| """ | |
| else: | |
| step_html = "<div class='step-indicator complete'>β Course Generation Complete!</div>" | |
| return step_html, log_entry | |
| class CoursePreview: | |
| """Interactive course preview component""" | |
| def __init__(self): | |
| self.current_course = None | |
| def create_preview_tabs(self) -> gr.Tabs: | |
| """Create tabbed course preview interface""" | |
| with gr.Tabs() as preview_tabs: | |
| # Course Overview Tab | |
| with gr.Tab("π Course Overview", elem_id="overview-tab"): | |
| course_overview = self._create_overview_section() | |
| # Lessons Tab | |
| with gr.Tab("π Lessons", elem_id="lessons-tab"): | |
| lessons_section = self._create_lessons_section() | |
| # Flashcards Tab | |
| with gr.Tab("π Flashcards", elem_id="flashcards-tab"): | |
| flashcards_section = self._create_flashcards_section() | |
| # Quizzes Tab | |
| with gr.Tab("π― Quizzes", elem_id="quizzes-tab"): | |
| quizzes_section = self._create_quizzes_section() | |
| # Images Tab | |
| with gr.Tab("π¨ Images", elem_id="images-tab"): | |
| images_section = self._create_images_section() | |
| # Export Tab | |
| with gr.Tab("π€ Export", elem_id="export-tab"): | |
| export_section = self._create_export_section() | |
| return preview_tabs, { | |
| "course_overview": course_overview, | |
| "lessons_section": lessons_section, | |
| "flashcards_section": flashcards_section, | |
| "quizzes_section": quizzes_section, | |
| "images_section": images_section, | |
| "export_section": export_section | |
| } | |
| def _create_overview_section(self) -> Dict[str, Any]: | |
| """Create course overview section""" | |
| with gr.Group(): | |
| # Course header | |
| course_title = gr.HTML( | |
| "<h2>Course will appear here after generation</h2>", | |
| elem_id="course-title" | |
| ) | |
| course_metadata = gr.HTML( | |
| "<div class='course-metadata'>Generate a course to see details</div>", | |
| elem_id="course-metadata" | |
| ) | |
| # Course description | |
| course_description = gr.Markdown( | |
| "Course description will appear here...", | |
| elem_id="course-description" | |
| ) | |
| # Learning objectives | |
| learning_objectives = gr.HTML( | |
| "<div class='learning-objectives'>Learning objectives will appear here</div>", | |
| elem_id="learning-objectives" | |
| ) | |
| # Course structure | |
| course_structure = gr.HTML( | |
| "<div class='course-structure'>Course structure will appear here</div>", | |
| elem_id="course-structure" | |
| ) | |
| return { | |
| "course_title": course_title, | |
| "course_metadata": course_metadata, | |
| "course_description": course_description, | |
| "learning_objectives": learning_objectives, | |
| "course_structure": course_structure | |
| } | |
| def _create_lessons_section(self) -> Dict[str, Any]: | |
| """Create lessons preview section""" | |
| with gr.Group(): | |
| # Lesson selector | |
| lesson_selector = gr.Dropdown( | |
| choices=[], | |
| label="π Select Lesson", | |
| elem_id="lesson-selector" | |
| ) | |
| # Lesson content display | |
| lesson_title = gr.HTML( | |
| "<h3>Select a lesson to view content</h3>", | |
| elem_id="lesson-title" | |
| ) | |
| lesson_content = gr.Markdown( | |
| "Lesson content will appear here...", | |
| elem_id="lesson-content" | |
| ) | |
| # Lesson navigation | |
| with gr.Row(): | |
| prev_lesson_btn = gr.Button( | |
| "β¬ οΈ Previous", | |
| elem_id="prev-lesson-btn" | |
| ) | |
| next_lesson_btn = gr.Button( | |
| "β‘οΈ Next", | |
| elem_id="next-lesson-btn" | |
| ) | |
| return { | |
| "lesson_selector": lesson_selector, | |
| "lesson_title": lesson_title, | |
| "lesson_content": lesson_content, | |
| "prev_lesson_btn": prev_lesson_btn, | |
| "next_lesson_btn": next_lesson_btn | |
| } | |
| def _create_flashcards_section(self) -> Dict[str, Any]: | |
| """Create flashcards preview section""" | |
| with gr.Group(): | |
| # Flashcard display | |
| flashcard_display = gr.HTML( | |
| "<div class='flashcard-container'>Flashcards will appear here</div>", | |
| elem_id="flashcard-display" | |
| ) | |
| # Flashcard controls | |
| with gr.Row(): | |
| flip_card_btn = gr.Button( | |
| "π Flip Card", | |
| elem_id="flip-card-btn" | |
| ) | |
| prev_card_btn = gr.Button( | |
| "β¬ οΈ Previous", | |
| elem_id="prev-card-btn" | |
| ) | |
| next_card_btn = gr.Button( | |
| "β‘οΈ Next", | |
| elem_id="next-card-btn" | |
| ) | |
| # Flashcard progress | |
| flashcard_progress = gr.HTML( | |
| "<div class='flashcard-progress'>Card 1 of 0</div>", | |
| elem_id="flashcard-progress" | |
| ) | |
| return { | |
| "flashcard_display": flashcard_display, | |
| "flip_card_btn": flip_card_btn, | |
| "prev_card_btn": prev_card_btn, | |
| "next_card_btn": next_card_btn, | |
| "flashcard_progress": flashcard_progress | |
| } | |
| def _create_quizzes_section(self) -> Dict[str, Any]: | |
| """Create quizzes preview section""" | |
| with gr.Group(): | |
| # Quiz selector | |
| quiz_selector = gr.Dropdown( | |
| choices=[], | |
| label="π― Select Quiz", | |
| elem_id="quiz-selector" | |
| ) | |
| # Quiz display | |
| quiz_content = gr.HTML( | |
| "<div class='quiz-container'>Select a quiz to begin</div>", | |
| elem_id="quiz-content" | |
| ) | |
| # Quiz controls | |
| with gr.Row(): | |
| start_quiz_btn = gr.Button( | |
| "βΆοΈ Start Quiz", | |
| variant="primary", | |
| elem_id="start-quiz-btn" | |
| ) | |
| reset_quiz_btn = gr.Button( | |
| "π Reset", | |
| elem_id="reset-quiz-btn" | |
| ) | |
| return { | |
| "quiz_selector": quiz_selector, | |
| "quiz_content": quiz_content, | |
| "start_quiz_btn": start_quiz_btn, | |
| "reset_quiz_btn": reset_quiz_btn | |
| } | |
| def _create_images_section(self) -> Dict[str, Any]: | |
| """Create images gallery section""" | |
| with gr.Group(): | |
| # Image gallery | |
| image_gallery = gr.Gallery( | |
| label="π¨ Generated Images", | |
| show_label=True, | |
| elem_id="image-gallery", | |
| columns=3, | |
| rows=2, | |
| height="auto" | |
| ) | |
| # Image details | |
| image_details = gr.HTML( | |
| "<div class='image-details'>Select an image to view details</div>", | |
| elem_id="image-details" | |
| ) | |
| return { | |
| "image_gallery": image_gallery, | |
| "image_details": image_details | |
| } | |
| def _create_export_section(self) -> Dict[str, Any]: | |
| """Create export options section""" | |
| with gr.Group(): | |
| gr.HTML("<h3>π€ Export Your Course</h3>") | |
| # Export format selection | |
| with gr.Row(): | |
| export_pdf = gr.Checkbox( | |
| value=True, | |
| label="π PDF Course Book" | |
| ) | |
| export_json = gr.Checkbox( | |
| value=True, | |
| label="π JSON Data" | |
| ) | |
| export_anki = gr.Checkbox( | |
| value=False, | |
| label="π Anki Deck" | |
| ) | |
| with gr.Row(): | |
| export_notion = gr.Checkbox( | |
| value=False, | |
| label="π Notion Pages" | |
| ) | |
| export_github = gr.Checkbox( | |
| value=False, | |
| label="π GitHub Repository" | |
| ) | |
| export_drive = gr.Checkbox( | |
| value=False, | |
| label="βοΈ Google Drive" | |
| ) | |
| # Export button | |
| export_btn = gr.Button( | |
| "π¦ Export Course", | |
| variant="primary", | |
| size="lg", | |
| elem_id="export-btn" | |
| ) | |
| # Download links | |
| download_links = gr.HTML( | |
| "<div class='download-links'>Export files will appear here</div>", | |
| elem_id="download-links" | |
| ) | |
| return { | |
| "export_options": { | |
| "pdf": export_pdf, | |
| "json": export_json, | |
| "anki": export_anki, | |
| "notion": export_notion, | |
| "github": export_github, | |
| "drive": export_drive | |
| }, | |
| "export_btn": export_btn, | |
| "download_links": download_links | |
| } | |
| class FlashcardViewer: | |
| """Interactive flashcard viewer component""" | |
| def __init__(self): | |
| self.current_card_index = 0 | |
| self.show_back = False | |
| self.flashcards = [] | |
| def create_flashcard_interface(self, flashcards: List[Flashcard]) -> gr.Group: | |
| """Create interactive flashcard viewer""" | |
| self.flashcards = flashcards | |
| with gr.Group() as flashcard_group: | |
| gr.HTML("<h3>π Interactive Flashcards</h3>") | |
| if not flashcards: | |
| gr.HTML("<p>No flashcards available</p>") | |
| return flashcard_group, {} | |
| # Card counter | |
| card_counter = gr.HTML( | |
| f"<div class='card-counter'>Card 1 of {len(flashcards)}</div>", | |
| elem_id="card-counter" | |
| ) | |
| # Flashcard display | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| # Card content | |
| card_display = gr.HTML( | |
| self._format_flashcard_html(flashcards[0], show_back=False), | |
| elem_id="flashcard-display" | |
| ) | |
| # Flip button | |
| flip_btn = gr.Button( | |
| "π Flip Card", | |
| variant="secondary", | |
| elem_id="flip-button" | |
| ) | |
| # Navigation buttons | |
| with gr.Row(): | |
| prev_btn = gr.Button( | |
| "β¬ οΈ Previous", | |
| variant="secondary", | |
| interactive=False, | |
| elem_id="prev-button" | |
| ) | |
| next_btn = gr.Button( | |
| "β‘οΈ Next", | |
| variant="secondary", | |
| interactive=len(flashcards) > 1, | |
| elem_id="next-button" | |
| ) | |
| return flashcard_group, { | |
| "card_counter": card_counter, | |
| "card_display": card_display, | |
| "flip_btn": flip_btn, | |
| "prev_btn": prev_btn, | |
| "next_btn": next_btn | |
| } | |
| def _format_flashcard_html(self, flashcard: Flashcard, show_back: bool = False) -> str: | |
| """Format flashcard as HTML""" | |
| if show_back: | |
| content = f""" | |
| <div class="flashcard flashcard-back"> | |
| <div class="flashcard-header">Answer</div> | |
| <div class="flashcard-content">{flashcard.back}</div> | |
| <div class="flashcard-footer"> | |
| <span class="difficulty">{flashcard.difficulty}</span> | |
| <span class="tags">{', '.join(flashcard.tags) if flashcard.tags else ''}</span> | |
| </div> | |
| </div> | |
| """ | |
| else: | |
| content = f""" | |
| <div class="flashcard flashcard-front"> | |
| <div class="flashcard-header">Question</div> | |
| <div class="flashcard-content">{flashcard.front}</div> | |
| <div class="flashcard-footer"> | |
| <span class="difficulty">{flashcard.difficulty}</span> | |
| </div> | |
| </div> | |
| """ | |
| return content | |
| class UIHelpers: | |
| """Helper functions for UI components""" | |
| def format_course_metadata(course: Course) -> str: | |
| """Format course metadata for display""" | |
| metadata_html = f""" | |
| <div class="course-metadata"> | |
| <div class="metadata-item"> | |
| <span class="label">π― Difficulty:</span> | |
| <span class="value">{course.difficulty_level}</span> | |
| </div> | |
| <div class="metadata-item"> | |
| <span class="label">β±οΈ Duration:</span> | |
| <span class="value">{course.estimated_duration} hours</span> | |
| </div> | |
| <div class="metadata-item"> | |
| <span class="label">π Lessons:</span> | |
| <span class="value">{len(course.lessons)}</span> | |
| </div> | |
| <div class="metadata-item"> | |
| <span class="label">π₯ Audience:</span> | |
| <span class="value">{course.target_audience}</span> | |
| </div> | |
| <div class="metadata-item"> | |
| <span class="label">π·οΈ Tags:</span> | |
| <span class="value">{', '.join(course.tags)}</span> | |
| </div> | |
| </div> | |
| """ | |
| return metadata_html | |
| def format_learning_objectives(objectives: List[str]) -> str: | |
| """Format learning objectives for display""" | |
| objectives_html = """ | |
| <div class="learning-objectives"> | |
| <h4>π― Learning Objectives</h4> | |
| <ul> | |
| """ | |
| for objective in objectives: | |
| objectives_html += f"<li>{objective}</li>" | |
| objectives_html += """ | |
| </ul> | |
| </div> | |
| """ | |
| return objectives_html | |
| def format_flashcard(flashcard: Flashcard, show_back: bool = False) -> str: | |
| """Format flashcard for display""" | |
| card_class = "flashcard flipped" if show_back else "flashcard" | |
| content = flashcard.back if show_back else flashcard.front | |
| flashcard_html = f""" | |
| <div class="{card_class}"> | |
| <div class="flashcard-content"> | |
| <div class="flashcard-category">{flashcard.category}</div> | |
| <div class="flashcard-text">{content}</div> | |
| <div class="flashcard-difficulty">Difficulty: {flashcard.difficulty}/5</div> | |
| </div> | |
| </div> | |
| """ | |
| return flashcard_html | |
| def create_error_display(error_message: str) -> str: | |
| """Create error display HTML""" | |
| error_html = f""" | |
| <div class="error-display"> | |
| <div class="error-icon">β</div> | |
| <div class="error-message">{error_message}</div> | |
| <div class="error-suggestion">Please try again or contact support if the issue persists.</div> | |
| </div> | |
| """ | |
| return error_html | |
| def create_success_display(success_message: str) -> str: | |
| """Create success display HTML""" | |
| success_html = f""" | |
| <div class="success-display"> | |
| <div class="success-icon">β </div> | |
| <div class="success-message">{success_message}</div> | |
| </div> | |
| """ | |
| return success_html |