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.""" @staticmethod 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.' } ) @staticmethod def apply_custom_css(): """Apply enhanced custom CSS styling.""" st.markdown(""" """, unsafe_allow_html=True) @staticmethod 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 @staticmethod 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 {} @classmethod 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 @staticmethod 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 @staticmethod 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" @staticmethod 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() @staticmethod 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() @staticmethod 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() }) @staticmethod def format_code(code: str, language: str = "python") -> str: """Format code with syntax highlighting.""" return f"```{language}\n{code}\n```" @classmethod 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)}") @staticmethod 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"]) @staticmethod 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