Spaces:
Build error
Build error
| import streamlit as st | |
| from datetime import datetime | |
| from typing import Dict, Optional, Any | |
| import json | |
| import base64 | |
| from pathlib import Path | |
| import logging | |
| from logging.handlers import RotatingFileHandler | |
| import yaml | |
| import os | |
| from PIL import Image | |
| import io | |
| class AppUtils: | |
| """Core utilities class for the EduAI platform.""" | |
| def set_page_config(): | |
| """Configure Streamlit page settings with enhanced options.""" | |
| st.set_page_config( | |
| page_title="EduAI Platform", | |
| page_icon="🎓", | |
| layout="wide", | |
| initial_sidebar_state="expanded", | |
| menu_items={ | |
| 'Get Help': 'https://docs.eduai-platform.com', | |
| 'Report a bug': 'https://github.com/eduai-platform/issues', | |
| 'About': '### EduAI Learning Platform\nEmpowering education through AI.' | |
| } | |
| ) | |
| def apply_custom_css(): | |
| """Apply enhanced custom CSS styling.""" | |
| st.markdown(""" | |
| <style> | |
| /* Main app container */ | |
| .stApp { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| background-color: #f8f9fa; | |
| } | |
| /* Header styling */ | |
| .stHeader { | |
| background-color: white; | |
| padding: 1rem; | |
| border-bottom: 1px solid #e9ecef; | |
| } | |
| /* Sidebar enhancements */ | |
| .css-1d391kg { | |
| background-color: white; | |
| padding: 2rem 1rem; | |
| } | |
| /* Card layouts */ | |
| .card { | |
| background-color: white; | |
| border-radius: 0.5rem; | |
| padding: 1.5rem; | |
| margin: 1rem 0; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| /* Button styling */ | |
| .stButton button { | |
| width: 100%; | |
| border-radius: 0.25rem; | |
| transition: all 0.2s ease; | |
| } | |
| .stButton button:hover { | |
| transform: translateY(-1px); | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| } | |
| /* Progress bars */ | |
| .stProgress > div > div { | |
| background-color: #007bff; | |
| } | |
| /* Code editor */ | |
| .stCodeEditor { | |
| border-radius: 0.5rem; | |
| overflow: hidden; | |
| } | |
| /* Chat interface */ | |
| .chat-message { | |
| padding: 1rem; | |
| margin: 0.5rem 0; | |
| border-radius: 0.5rem; | |
| } | |
| .user-message { | |
| background-color: #e7f1ff; | |
| margin-left: 2rem; | |
| } | |
| .assistant-message { | |
| background-color: #f8f9fa; | |
| margin-right: 2rem; | |
| } | |
| /* Responsive adjustments */ | |
| @media (max-width: 768px) { | |
| .stApp { | |
| padding: 1rem; | |
| } | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| def setup_logging(log_dir: str = "logs"): | |
| """Set up application logging with rotation.""" | |
| log_dir = Path(log_dir) | |
| log_dir.mkdir(exist_ok=True) | |
| log_file = log_dir / "eduai.log" | |
| formatter = logging.Formatter( | |
| '%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| ) | |
| handler = RotatingFileHandler( | |
| log_file, | |
| maxBytes=10485760, # 10MB | |
| backupCount=5 | |
| ) | |
| handler.setFormatter(formatter) | |
| logger = logging.getLogger("eduai") | |
| logger.setLevel(logging.INFO) | |
| logger.addHandler(handler) | |
| return logger | |
| def load_config(config_path: str = "config.yaml") -> Dict: | |
| """Load application configuration from YAML.""" | |
| try: | |
| with open(config_path) as f: | |
| config = yaml.safe_load(f) | |
| return config | |
| except Exception as e: | |
| st.error(f"Error loading configuration: {str(e)}") | |
| return {} | |
| def initialize_session_state(cls): | |
| """Initialize all required session state variables.""" | |
| default_state = { | |
| 'messages': [], | |
| 'user_progress': { | |
| 'current_path': None, | |
| 'completed_modules': [], | |
| 'achievements': [] | |
| }, | |
| 'artifacts': [], | |
| 'notifications': [], | |
| 'theme': 'light', | |
| 'language': 'en' | |
| } | |
| for key, value in default_state.items(): | |
| if key not in st.session_state: | |
| st.session_state[key] = value | |
| def save_uploaded_file(uploaded_file) -> Optional[Path]: | |
| """Save uploaded file and return the path.""" | |
| if uploaded_file is None: | |
| return None | |
| # Create uploads directory if it doesn't exist | |
| upload_dir = Path("uploads") | |
| upload_dir.mkdir(exist_ok=True) | |
| # Generate unique filename | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| safe_filename = "".join(c for c in uploaded_file.name if c.isalnum() or c in "._-") | |
| unique_filename = f"{timestamp}_{safe_filename}" | |
| file_path = upload_dir / unique_filename | |
| # Save the file | |
| try: | |
| with open(file_path, "wb") as f: | |
| f.write(uploaded_file.getbuffer()) | |
| return file_path | |
| except Exception as e: | |
| st.error(f"Error saving file: {str(e)}") | |
| return None | |
| def format_time(seconds: int) -> str: | |
| """Format time duration in a human-readable format.""" | |
| if seconds < 60: | |
| return f"{seconds} seconds" | |
| elif seconds < 3600: | |
| minutes = seconds // 60 | |
| return f"{minutes} {'minute' if minutes == 1 else 'minutes'}" | |
| else: | |
| hours = seconds // 3600 | |
| minutes = (seconds % 3600) // 60 | |
| return f"{hours}h {minutes}m" | |
| def encode_image(image_path: str) -> str: | |
| """Encode image to base64 string.""" | |
| with open(image_path, "rb") as image_file: | |
| return base64.b64encode(image_file.read()).decode() | |
| def create_thumbnail(image_path: str, size: tuple = (100, 100)) -> str: | |
| """Create thumbnail from image and return as base64 string.""" | |
| with Image.open(image_path) as img: | |
| img.thumbnail(size) | |
| buffered = io.BytesIO() | |
| img.save(buffered, format=img.format) | |
| return base64.b64encode(buffered.getvalue()).decode() | |
| def display_notification(message: str, type: str = "info"): | |
| """Display notification message with specified type.""" | |
| notification_funcs = { | |
| "info": st.info, | |
| "success": st.success, | |
| "warning": st.warning, | |
| "error": st.error | |
| } | |
| if type in notification_funcs: | |
| notification_funcs[type](message) | |
| # Store in notifications history | |
| if 'notifications' in st.session_state: | |
| st.session_state.notifications.append({ | |
| 'message': message, | |
| 'type': type, | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| def format_code(code: str, language: str = "python") -> str: | |
| """Format code with syntax highlighting.""" | |
| return f"```{language}\n{code}\n```" | |
| def track_analytics(cls, event_type: str, event_data: Dict[str, Any]): | |
| """Track user analytics events.""" | |
| analytics_data = { | |
| 'timestamp': datetime.now().isoformat(), | |
| 'event_type': event_type, | |
| 'event_data': event_data, | |
| 'session_id': st.session_state.get('session_id'), | |
| 'user_id': st.session_state.get('user_id') | |
| } | |
| # In a real application, this would send data to an analytics service | |
| logger = cls.setup_logging() | |
| logger.info(f"Analytics event: {json.dumps(analytics_data)}") | |
| def get_theme_colors(theme: str = "light") -> Dict[str, str]: | |
| """Get color scheme based on theme.""" | |
| themes = { | |
| "light": { | |
| "primary": "#007bff", | |
| "secondary": "#6c757d", | |
| "background": "#f8f9fa", | |
| "text": "#212529", | |
| "border": "#dee2e6" | |
| }, | |
| "dark": { | |
| "primary": "#0d6efd", | |
| "secondary": "#6c757d", | |
| "background": "#212529", | |
| "text": "#f8f9fa", | |
| "border": "#495057" | |
| } | |
| } | |
| return themes.get(theme, themes["light"]) | |
| def get_translation(text: str, language: str = "en") -> str: | |
| """Get translated text based on language setting.""" | |
| # This is a simple placeholder - in a real app, use a proper i18n system | |
| translations = { | |
| "en": {"welcome": "Welcome", "start": "Start Learning"}, | |
| "hi": {"welcome": "स्वागत है", "start": "सीखना शुरू करें"}, | |
| "es": {"welcome": "Bienvenido", "start": "Empezar a aprender"} | |
| } | |
| lang_dict = translations.get(language, translations["en"]) | |
| return lang_dict.get(text, text) | |
| # Initialize environment variables and global settings | |
| def init_environment(): | |
| """Initialize environment variables and settings.""" | |
| os.environ.setdefault("STREAMLIT_THEME", "light") | |
| os.environ.setdefault("STREAMLIT_LOG_LEVEL", "INFO") | |
| # Set up basic security headers | |
| headers = { | |
| 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains', | |
| 'X-Content-Type-Options': 'nosniff', | |
| 'X-Frame-Options': 'SAMEORIGIN', | |
| 'X-XSS-Protection': '1; mode=block' | |
| } | |
| return headers |