cryogenic22 commited on
Commit
f69e8b5
·
verified ·
1 Parent(s): d29d7ce

Create utils.py

Browse files
Files changed (1) hide show
  1. src/utils.py +313 -0
src/utils.py ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from datetime import datetime
3
+ from typing import Dict, Optional, Any
4
+ import json
5
+ import base64
6
+ from pathlib import Path
7
+ import logging
8
+ from logging.handlers import RotatingFileHandler
9
+ import yaml
10
+ import os
11
+ from PIL import Image
12
+ import io
13
+
14
+ class AppUtils:
15
+ """Core utilities class for the EduAI platform."""
16
+
17
+ @staticmethod
18
+ def set_page_config():
19
+ """Configure Streamlit page settings with enhanced options."""
20
+ st.set_page_config(
21
+ page_title="EduAI Platform",
22
+ page_icon="🎓",
23
+ layout="wide",
24
+ initial_sidebar_state="expanded",
25
+ menu_items={
26
+ 'Get Help': 'https://docs.eduai-platform.com',
27
+ 'Report a bug': 'https://github.com/eduai-platform/issues',
28
+ 'About': '### EduAI Learning Platform\nEmpowering education through AI.'
29
+ }
30
+ )
31
+
32
+ @staticmethod
33
+ def apply_custom_css():
34
+ """Apply enhanced custom CSS styling."""
35
+ st.markdown("""
36
+ <style>
37
+ /* Main app container */
38
+ .stApp {
39
+ max-width: 1200px;
40
+ margin: 0 auto;
41
+ background-color: #f8f9fa;
42
+ }
43
+
44
+ /* Header styling */
45
+ .stHeader {
46
+ background-color: white;
47
+ padding: 1rem;
48
+ border-bottom: 1px solid #e9ecef;
49
+ }
50
+
51
+ /* Sidebar enhancements */
52
+ .css-1d391kg {
53
+ background-color: white;
54
+ padding: 2rem 1rem;
55
+ }
56
+
57
+ /* Card layouts */
58
+ .card {
59
+ background-color: white;
60
+ border-radius: 0.5rem;
61
+ padding: 1.5rem;
62
+ margin: 1rem 0;
63
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
64
+ }
65
+
66
+ /* Button styling */
67
+ .stButton button {
68
+ width: 100%;
69
+ border-radius: 0.25rem;
70
+ transition: all 0.2s ease;
71
+ }
72
+
73
+ .stButton button:hover {
74
+ transform: translateY(-1px);
75
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
76
+ }
77
+
78
+ /* Progress bars */
79
+ .stProgress > div > div {
80
+ background-color: #007bff;
81
+ }
82
+
83
+ /* Code editor */
84
+ .stCodeEditor {
85
+ border-radius: 0.5rem;
86
+ overflow: hidden;
87
+ }
88
+
89
+ /* Chat interface */
90
+ .chat-message {
91
+ padding: 1rem;
92
+ margin: 0.5rem 0;
93
+ border-radius: 0.5rem;
94
+ }
95
+
96
+ .user-message {
97
+ background-color: #e7f1ff;
98
+ margin-left: 2rem;
99
+ }
100
+
101
+ .assistant-message {
102
+ background-color: #f8f9fa;
103
+ margin-right: 2rem;
104
+ }
105
+
106
+ /* Responsive adjustments */
107
+ @media (max-width: 768px) {
108
+ .stApp {
109
+ padding: 1rem;
110
+ }
111
+ }
112
+ </style>
113
+ """, unsafe_allow_html=True)
114
+
115
+ @staticmethod
116
+ def setup_logging(log_dir: str = "logs"):
117
+ """Set up application logging with rotation."""
118
+ log_dir = Path(log_dir)
119
+ log_dir.mkdir(exist_ok=True)
120
+
121
+ log_file = log_dir / "eduai.log"
122
+ formatter = logging.Formatter(
123
+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
124
+ )
125
+
126
+ handler = RotatingFileHandler(
127
+ log_file,
128
+ maxBytes=10485760, # 10MB
129
+ backupCount=5
130
+ )
131
+ handler.setFormatter(formatter)
132
+
133
+ logger = logging.getLogger("eduai")
134
+ logger.setLevel(logging.INFO)
135
+ logger.addHandler(handler)
136
+
137
+ return logger
138
+
139
+ @staticmethod
140
+ def load_config(config_path: str = "config.yaml") -> Dict:
141
+ """Load application configuration from YAML."""
142
+ try:
143
+ with open(config_path) as f:
144
+ config = yaml.safe_load(f)
145
+ return config
146
+ except Exception as e:
147
+ st.error(f"Error loading configuration: {str(e)}")
148
+ return {}
149
+
150
+ @classmethod
151
+ def initialize_session_state(cls):
152
+ """Initialize all required session state variables."""
153
+ default_state = {
154
+ 'messages': [],
155
+ 'user_progress': {
156
+ 'current_path': None,
157
+ 'completed_modules': [],
158
+ 'achievements': []
159
+ },
160
+ 'artifacts': [],
161
+ 'notifications': [],
162
+ 'theme': 'light',
163
+ 'language': 'en'
164
+ }
165
+
166
+ for key, value in default_state.items():
167
+ if key not in st.session_state:
168
+ st.session_state[key] = value
169
+
170
+ @staticmethod
171
+ def save_uploaded_file(uploaded_file) -> Optional[Path]:
172
+ """Save uploaded file and return the path."""
173
+ if uploaded_file is None:
174
+ return None
175
+
176
+ # Create uploads directory if it doesn't exist
177
+ upload_dir = Path("uploads")
178
+ upload_dir.mkdir(exist_ok=True)
179
+
180
+ # Generate unique filename
181
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
182
+ safe_filename = "".join(c for c in uploaded_file.name if c.isalnum() or c in "._-")
183
+ unique_filename = f"{timestamp}_{safe_filename}"
184
+
185
+ file_path = upload_dir / unique_filename
186
+
187
+ # Save the file
188
+ try:
189
+ with open(file_path, "wb") as f:
190
+ f.write(uploaded_file.getbuffer())
191
+ return file_path
192
+ except Exception as e:
193
+ st.error(f"Error saving file: {str(e)}")
194
+ return None
195
+
196
+ @staticmethod
197
+ def format_time(seconds: int) -> str:
198
+ """Format time duration in a human-readable format."""
199
+ if seconds < 60:
200
+ return f"{seconds} seconds"
201
+ elif seconds < 3600:
202
+ minutes = seconds // 60
203
+ return f"{minutes} {'minute' if minutes == 1 else 'minutes'}"
204
+ else:
205
+ hours = seconds // 3600
206
+ minutes = (seconds % 3600) // 60
207
+ return f"{hours}h {minutes}m"
208
+
209
+ @staticmethod
210
+ def encode_image(image_path: str) -> str:
211
+ """Encode image to base64 string."""
212
+ with open(image_path, "rb") as image_file:
213
+ return base64.b64encode(image_file.read()).decode()
214
+
215
+ @staticmethod
216
+ def create_thumbnail(image_path: str, size: tuple = (100, 100)) -> str:
217
+ """Create thumbnail from image and return as base64 string."""
218
+ with Image.open(image_path) as img:
219
+ img.thumbnail(size)
220
+ buffered = io.BytesIO()
221
+ img.save(buffered, format=img.format)
222
+ return base64.b64encode(buffered.getvalue()).decode()
223
+
224
+ @staticmethod
225
+ def display_notification(message: str, type: str = "info"):
226
+ """Display notification message with specified type."""
227
+ notification_funcs = {
228
+ "info": st.info,
229
+ "success": st.success,
230
+ "warning": st.warning,
231
+ "error": st.error
232
+ }
233
+
234
+ if type in notification_funcs:
235
+ notification_funcs[type](message)
236
+
237
+ # Store in notifications history
238
+ if 'notifications' in st.session_state:
239
+ st.session_state.notifications.append({
240
+ 'message': message,
241
+ 'type': type,
242
+ 'timestamp': datetime.now().isoformat()
243
+ })
244
+
245
+ @staticmethod
246
+ def format_code(code: str, language: str = "python") -> str:
247
+ """Format code with syntax highlighting."""
248
+ return f"```{language}\n{code}\n```"
249
+
250
+ @classmethod
251
+ def track_analytics(cls, event_type: str, event_data: Dict[str, Any]):
252
+ """Track user analytics events."""
253
+ analytics_data = {
254
+ 'timestamp': datetime.now().isoformat(),
255
+ 'event_type': event_type,
256
+ 'event_data': event_data,
257
+ 'session_id': st.session_state.get('session_id'),
258
+ 'user_id': st.session_state.get('user_id')
259
+ }
260
+
261
+ # In a real application, this would send data to an analytics service
262
+ logger = cls.setup_logging()
263
+ logger.info(f"Analytics event: {json.dumps(analytics_data)}")
264
+
265
+ @staticmethod
266
+ def get_theme_colors(theme: str = "light") -> Dict[str, str]:
267
+ """Get color scheme based on theme."""
268
+ themes = {
269
+ "light": {
270
+ "primary": "#007bff",
271
+ "secondary": "#6c757d",
272
+ "background": "#f8f9fa",
273
+ "text": "#212529",
274
+ "border": "#dee2e6"
275
+ },
276
+ "dark": {
277
+ "primary": "#0d6efd",
278
+ "secondary": "#6c757d",
279
+ "background": "#212529",
280
+ "text": "#f8f9fa",
281
+ "border": "#495057"
282
+ }
283
+ }
284
+ return themes.get(theme, themes["light"])
285
+
286
+ @staticmethod
287
+ def get_translation(text: str, language: str = "en") -> str:
288
+ """Get translated text based on language setting."""
289
+ # This is a simple placeholder - in a real app, use a proper i18n system
290
+ translations = {
291
+ "en": {"welcome": "Welcome", "start": "Start Learning"},
292
+ "hi": {"welcome": "स्वागत है", "start": "सीखना शुरू करें"},
293
+ "es": {"welcome": "Bienvenido", "start": "Empezar a aprender"}
294
+ }
295
+
296
+ lang_dict = translations.get(language, translations["en"])
297
+ return lang_dict.get(text, text)
298
+
299
+ # Initialize environment variables and global settings
300
+ def init_environment():
301
+ """Initialize environment variables and settings."""
302
+ os.environ.setdefault("STREAMLIT_THEME", "light")
303
+ os.environ.setdefault("STREAMLIT_LOG_LEVEL", "INFO")
304
+
305
+ # Set up basic security headers
306
+ headers = {
307
+ 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
308
+ 'X-Content-Type-Options': 'nosniff',
309
+ 'X-Frame-Options': 'SAMEORIGIN',
310
+ 'X-XSS-Protection': '1; mode=block'
311
+ }
312
+
313
+ return headers