banao-tech commited on
Commit
e8f95c1
·
verified ·
1 Parent(s): 3eddd77

Update AdminDashboard.py

Browse files
Files changed (1) hide show
  1. AdminDashboard.py +264 -651
AdminDashboard.py CHANGED
@@ -1,696 +1,309 @@
1
  import streamlit as st
2
- import json
3
- from datetime import datetime
4
- import time
5
- import requests
6
  import boto3
7
- import uuid
 
8
  import os
9
- from AdminDashboard import render_admin_dashboard
10
-
11
- # Load configuration from environment variables (Hugging Face Spaces)
12
- API_ENDPOINT = os.getenv("API_ENDPOINT", "https://oau6sljd4l.execute-api.ap-south-1.amazonaws.com/production/process")
13
 
14
- # DynamoDB configuration for session tracking
15
  DYNAMODB_REGION = os.getenv("DYNAMODB_REGION", "ap-south-1")
16
  SESSION_TABLE = os.getenv("SESSION_TABLE", "SessionTracking")
 
17
 
18
  # AWS Configuration
19
  AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
20
  AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
21
- AWS_DEFAULT_REGION = os.getenv("AWS_DEFAULT_REGION", "ap-south-1")
22
-
23
- # Default values
24
- DEFAULT_USER_ID = int(os.getenv("DEFAULT_USER_ID", "30"))
25
- DEFAULT_PERSONALIZATION_ID = int(os.getenv("DEFAULT_PERSONALIZATION_ID", "100"))
26
- API_TIMEOUT = int(os.getenv("API_TIMEOUT", "30"))
27
 
28
- # Authentication credentials from environment variables
29
- VALID_USERNAME = os.getenv("APP_USERNAME")
30
- VALID_PASSWORD = os.getenv("APP_PASSWORD")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- # Course configurations
33
- COURSE_CONFIGS = {
34
- "AI Ready Course": {"course_id": 47, "chapter_id": 647},
35
- "MERN": {"course_id": 48, "chapter_id": 648},
36
- "Machine Learning": {"course_id": 49, "chapter_id": 649},
37
- "Data Science": {"course_id": 50, "chapter_id": 650},
38
- "Flask": {"course_id": 51, "chapter_id": 651},
39
- "Django": {"course_id": 52, "chapter_id": 652}
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- # Set page configuration
43
- st.set_page_config(
44
- page_title="Base Course Personalization",
45
- layout="wide",
46
- initial_sidebar_state="collapsed",
47
- page_icon="📚",
48
- menu_items={
49
- 'Get Help': None,
50
- 'Report a bug': None,
51
- 'About': None
52
- }
53
- )
54
 
55
- # Custom CSS for dark theme styling
56
- st.markdown("""
57
- <style>
58
- /* Dark theme colors */
59
- :root {
60
- --background-color: #1E1E1E;
61
- --card-background: #2D2D2D;
62
- --text-color: #E0E0E0;
63
- --accent-color: #4F97FF;
64
- --border-color: #444444;
65
- --header-color: #4F97FF;
66
- --subheader-color: #FFFFFF;
67
- --success-color: #10B981;
68
- --warning-color: #F59E0B;
69
- --error-color: #EF4444;
70
- }
71
-
72
- /* Main container styling */
73
- .stApp {
74
- background-color: var(--background-color);
75
- color: var(--text-color);
76
- }
77
-
78
- /* Headers */
79
- .main-header {
80
- font-size: 2.5rem;
81
- font-weight: 600;
82
- color: var(--header-color);
83
- margin-bottom: 1rem;
84
- text-align: center;
85
- text-shadow: 0 2px 4px rgba(79, 151, 255, 0.3);
86
- }
87
-
88
- .section-header {
89
- font-size: 1.5rem;
90
- font-weight: 500;
91
- color: var(--subheader-color);
92
- margin-top: 2rem;
93
- margin-bottom: 1rem;
94
- padding-bottom: 0.5rem;
95
- border-bottom: 2px solid var(--border-color);
96
- display: flex;
97
- align-items: center;
98
- gap: 0.5rem;
99
- }
100
-
101
- /* Form container */
102
- div[data-testid="stForm"] {
103
- background-color: var(--card-background);
104
- padding: 2rem;
105
- border-radius: 12px;
106
- box-shadow: 0 8px 25px rgba(0,0,0,0.4);
107
- border: 1px solid var(--border-color);
108
- margin-bottom: 1rem;
109
- }
110
-
111
- /* Button styling */
112
- .stButton>button {
113
- background: linear-gradient(135deg, var(--accent-color) 0%, #3B82F6 100%);
114
- color: white;
115
- border-radius: 8px;
116
- padding: 0.75rem 2rem;
117
- font-weight: 600;
118
- border: none;
119
- transition: all 0.3s ease;
120
- box-shadow: 0 4px 15px rgba(79, 151, 255, 0.3);
121
- }
122
 
123
- .stButton>button:hover {
124
- transform: translateY(-2px);
125
- box-shadow: 0 6px 20px rgba(79, 151, 255, 0.4);
126
- }
127
-
128
- /* Input fields styling */
129
- div[data-baseweb="select"] > div {
130
- background-color: var(--background-color);
131
- border: 2px solid var(--border-color);
132
- border-radius: 8px;
133
- transition: border-color 0.3s ease;
134
- }
135
-
136
- div[data-baseweb="select"] > div:focus-within {
137
- border-color: var(--accent-color);
138
- box-shadow: 0 0 0 3px rgba(79, 151, 255, 0.1);
139
- }
140
 
141
- .stTextInput > div > div > input,
142
- .stNumberInput > div > div > input,
143
- .stTextArea > div > div > textarea {
144
- background-color: var(--background-color);
145
- color: var(--text-color);
146
- border: 2px solid var(--border-color);
147
- border-radius: 8px;
148
- padding: 0.75rem;
149
- transition: border-color 0.3s ease;
150
- }
151
 
152
- .stTextInput > div > div > input:focus,
153
- .stNumberInput > div > div > input:focus,
154
- .stTextArea > div > div > textarea:focus {
155
- border-color: var(--accent-color);
156
- box-shadow: 0 0 0 3px rgba(79, 151, 255, 0.1);
157
- }
158
 
159
- /* Topic container styling */
160
- .topic-container {
161
- background-color: var(--card-background);
162
- padding: 1.5rem;
163
- border-radius: 10px;
164
- margin-bottom: 1rem;
165
- border: 1px solid var(--border-color);
166
- position: relative;
167
- }
168
 
169
- .topic-header {
170
- display: flex;
171
- align-items: center;
172
- gap: 0.5rem;
173
- margin-bottom: 1rem;
174
- font-weight: 600;
175
- color: var(--accent-color);
176
- }
177
 
178
- /* Radio buttons */
179
- .stRadio > div {
180
- background-color: transparent;
181
- gap: 1rem;
182
- }
 
 
 
183
 
184
- .stRadio > div > label > div {
185
- color: var(--text-color) !important;
186
- font-weight: 500;
187
- }
188
 
189
- /* Labels */
190
- .stSelectbox > label,
191
- .stTextInput > label,
192
- .stNumberInput > label,
193
- .stTextArea > label,
194
- .stRadio > label {
195
- color: var(--text-color) !important;
196
- font-weight: 500;
197
- margin-bottom: 0.5rem;
198
- }
199
 
200
- /* Toggle switch */
201
- .stToggle > label {
202
- color: var(--text-color) !important;
203
- font-weight: 500;
204
- }
205
 
206
- /* Spacing */
207
- div.block-container {
208
- padding-top: 2rem;
209
- max-width: 1200px;
210
- }
211
 
212
- hr {
213
- margin: 2rem 0;
214
- border: none;
215
- border-top: 1px solid var(--border-color);
216
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
 
218
- /* API Response Container */
219
- .api-response {
220
- background-color: #1A1A1A;
221
- border-radius: 10px;
222
- padding: 1.5rem;
223
- border-left: 4px solid var(--accent-color);
224
- margin: 1rem 0;
225
- }
226
 
227
- .session-info {
228
- background-color: var(--card-background);
229
- padding: 1.5rem;
230
- border-radius: 10px;
231
- margin: 1rem 0;
232
- border: 1px solid var(--border-color);
233
- box-shadow: 0 4px 12px rgba(0,0,0,0.2);
234
- }
235
 
236
- /* Action buttons container */
237
- .action-buttons {
238
- display: flex;
239
- gap: 1rem;
240
- margin: 1rem 0;
241
- justify-content: flex-start;
242
- }
243
 
244
- /* Small action buttons */
245
- .small-button {
246
- padding: 0.5rem 1rem;
247
- font-size: 0.875rem;
248
- }
249
 
250
- /* Success/Error styling */
251
- .stSuccess, .stError, .stWarning, .stInfo {
252
- border-radius: 8px;
253
- padding: 1rem;
254
- margin: 1rem 0;
255
- }
 
 
 
 
 
256
 
257
- /* Login container */
258
- .login-container {
259
- background-color: var(--card-background);
260
- padding: 3rem;
261
- border-radius: 15px;
262
- box-shadow: 0 10px 30px rgba(0,0,0,0.5);
263
- border: 1px solid var(--border-color);
264
- }
265
 
266
- /* Navigation tab styling */
267
- .nav-tabs {
268
- background-color: var(--card-background);
269
- padding: 1rem;
270
- border-radius: 10px;
271
- margin-bottom: 2rem;
272
- border: 1px solid var(--border-color);
273
- }
274
- </style>
275
- """, unsafe_allow_html=True)
276
-
277
- # Initialize session state for topics and authentication
278
- if 'topics_list' not in st.session_state:
279
- st.session_state.topics_list = [{"topic_title": "What is Flask", "chapter_title": "Introduction to Flask"}]
280
- if 'session_ids' not in st.session_state:
281
- st.session_state.session_ids = []
282
- if 'authenticated' not in st.session_state:
283
- st.session_state.authenticated = False
284
- if 'current_page' not in st.session_state:
285
- st.session_state.current_page = "Course Generation"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
- # Authentication check
288
- if not st.session_state.authenticated:
289
- st.markdown('<h1 class="main-header">🔐 Login to Course Management</h1>', unsafe_allow_html=True)
290
 
291
- # Check if credentials are configured
292
- if not VALID_USERNAME or not VALID_PASSWORD:
293
- st.error("❌ Authentication credentials not configured. Please contact administrator.")
294
- st.stop()
295
 
296
- col1, col2, col3 = st.columns([1, 2, 1])
297
- with col2:
298
- with st.container():
299
- st.markdown('<div class="login-container">', unsafe_allow_html=True)
300
-
301
- with st.form("login_form"):
302
- st.markdown('<div class="section-header">🔑 Authentication Required</div>', unsafe_allow_html=True)
303
-
304
- username = st.text_input("Username", placeholder="Enter username", key="login_username")
305
- password = st.text_input("Password", placeholder="Enter password", type="password", key="login_password")
306
-
307
- login_submitted = st.form_submit_button("🚀 Login", use_container_width=True)
308
-
309
- if login_submitted:
310
- if VALID_USERNAME and VALID_PASSWORD and username == VALID_USERNAME and password == VALID_PASSWORD:
311
- st.session_state.authenticated = True
312
- st.success("Login successful! Redirecting...")
313
- time.sleep(1)
314
- st.rerun()
315
- else:
316
- st.error("❌ Invalid username or password")
317
-
318
- st.markdown('</div>', unsafe_allow_html=True)
319
 
320
- st.markdown("---")
321
- st.markdown("""
322
- <div style="text-align: center; color: #888888;">
323
- <small>Please contact administrator for access credentials</small>
324
- </div>
325
- """, unsafe_allow_html=True)
326
- st.stop()
327
-
328
- # Navigation and Header (after login)
329
- col1, col2, col3, col4 = st.columns([3, 2, 2, 1])
330
- with col1:
331
- st.markdown('<h1 class="main-header">📚 Course Management System</h1>', unsafe_allow_html=True)
332
-
333
- with col2:
334
- page = st.selectbox(
335
- "📋 Navigate to:",
336
- ["Course Generation", "Admin Dashboard"],
337
- index=0 if st.session_state.current_page == "Course Generation" else 1,
338
- key="page_selector"
339
- )
340
- st.session_state.current_page = page
341
-
342
- with col4:
343
- if st.button("🔓 Logout", key="logout_btn"):
344
- st.session_state.authenticated = False
345
- st.session_state.topics_list = [{"topic_title": "What is Flask", "chapter_title": "Introduction to Flask"}]
346
- st.session_state.session_ids = []
347
- st.session_state.current_page = "Course Generation"
348
- st.rerun()
349
-
350
- # Page routing
351
- if st.session_state.current_page == "Admin Dashboard":
352
- render_admin_dashboard()
353
- else:
354
- # Course Generation Page (Original Content)
355
-
356
- # Course Selection Section
357
- st.markdown('<div class="section-header">📚 Course Selection</div>', unsafe_allow_html=True)
358
 
359
- col1, col2 = st.columns([2, 3])
360
- with col1:
361
- selected_course = st.selectbox(
362
- "Select Course Type",
363
- options=list(COURSE_CONFIGS.keys()),
364
- index=0,
365
- help="Choose the course you want to generate content for"
366
  )
367
-
368
- with col2:
369
- st.info(f"**Selected:** {selected_course} (Course ID: {COURSE_CONFIGS[selected_course]['course_id']})")
370
-
371
- # Topics Section (Outside form for dynamic interaction)
372
- st.markdown('<div class="section-header">📋 Topics Configuration</div>', unsafe_allow_html=True)
373
-
374
- # Display existing topics with better styling
375
- for i, topic in enumerate(st.session_state.topics_list):
376
- st.markdown(f'<div class="topic-container">', unsafe_allow_html=True)
377
- st.markdown(f'<div class="topic-header">📖 Topic {i+1}</div>', unsafe_allow_html=True)
378
 
379
- col1, col2, col3 = st.columns([3, 3, 1])
380
- with col1:
381
- topic_title = st.text_input(
382
- "Topic Title",
383
- value=topic["topic_title"],
384
- key=f"topic_title_{i}",
385
- help="Enter the topic title"
386
- )
387
- st.session_state.topics_list[i]["topic_title"] = topic_title
388
 
389
- with col2:
390
- chapter_title = st.text_input(
391
- "Chapter Title",
392
- value=topic["chapter_title"],
393
- key=f"chapter_title_{i}",
394
- help="Enter the chapter title"
395
- )
396
- st.session_state.topics_list[i]["chapter_title"] = chapter_title
 
 
 
397
 
398
- with col3:
399
- if len(st.session_state.topics_list) > 1:
400
- st.markdown("<br>", unsafe_allow_html=True) # Add spacing
401
- if st.button("🗑️", key=f"remove_{i}", help="Remove this topic"):
402
- st.session_state.topics_list.pop(i)
403
- st.rerun()
404
 
405
- st.markdown('</div>', unsafe_allow_html=True)
406
-
407
- # Add/Remove topic buttons outside the form
408
- st.markdown('<div class="action-buttons">', unsafe_allow_html=True)
409
- col1, col2, col3, col4 = st.columns([2, 2, 2, 4])
410
- with col1:
411
- if st.button("➕ Add Topic", key="add_topic", help="Add a new topic"):
412
- st.session_state.topics_list.append({
413
- "topic_title": f"Topic {len(st.session_state.topics_list) + 1}",
414
- "chapter_title": f"Chapter {len(st.session_state.topics_list) + 1}"
415
- })
416
- st.rerun()
417
- with col2:
418
- if st.button("🔄 Reset All", key="reset_topics", help="Reset to default topics"):
419
- st.session_state.topics_list = [{"topic_title": "What is Flask", "chapter_title": "Introduction to Flask"}]
420
- st.rerun()
421
- st.markdown('</div>', unsafe_allow_html=True)
422
-
423
- # Main Form (for settings and submission)
424
- with st.form("personalization_form", clear_on_submit=False):
425
- # Language & Voice Settings Section
426
- st.markdown('<div class="section-header">🗣️ Language & Voice Settings</div>', unsafe_allow_html=True)
427
-
428
- col1, col2, col3 = st.columns(3)
429
- with col1:
430
- target_language = st.selectbox(
431
- "Target Language",
432
- ["english", "hindi", "marathi", "kannada", "punjabi","gujarati"],
433
- index=0,
434
- format_func=lambda x: x.capitalize(),
435
- help="Select the target language for content generation"
436
- )
437
-
438
- with col2:
439
- tts_gender = st.selectbox(
440
- "Voice Gender",
441
- ["male", "female"],
442
- index=0,
443
- format_func=lambda x: x.capitalize(),
444
- help="Select the voice gender for text-to-speech"
445
- )
446
-
447
- with col3:
448
- tts_voice = st.selectbox(
449
- "Voice Style",
450
- ["alloy", "echo", "fable", "onyx", "nova", "shimmer"],
451
- index=3, # Default to "onyx"
452
- format_func=lambda x: x.capitalize(),
453
- help="Select the voice style for text-to-speech"
454
- )
455
-
456
- # Technical Settings Section
457
- st.markdown('<div class="section-header">💻 Technical Settings</div>', unsafe_allow_html=True)
458
-
459
- col1, col2 = st.columns(2)
460
- with col1:
461
- # Comprehensive list of programming languages, frameworks, and databases
462
- tech_options = [
463
- # Programming Languages
464
- "Python", "Java", "JavaScript", "TypeScript", "C++", "C#", "C", "Go", "Rust", "Swift",
465
- "Kotlin", "Scala", "Ruby", "PHP", "Perl", "R", "MATLAB", "Dart", "Objective-C", "Assembly",
466
- "Haskell", "Erlang", "Elixir", "F#", "Clojure", "Lua", "Julia", "Groovy", "VB.NET", "COBOL",
467
- "Fortran", "Pascal", "Delphi", "Ada", "Prolog", "Lisp", "Scheme", "OCaml", "ML",
468
-
469
- # Web Technologies
470
- "HTML", "CSS", "SASS", "LESS", "Bootstrap", "Tailwind CSS", "Material-UI",
471
-
472
- # Frontend Frameworks/Libraries
473
- "React", "Vue.js", "Angular", "Svelte", "Next.js", "Nuxt.js", "Gatsby", "Ember.js",
474
- "Backbone.js", "jQuery", "Alpine.js", "Lit", "Stencil", "Ionic", "React Native",
475
- "Flutter", "Xamarin", "Cordova", "PhoneGap",
476
-
477
- # Backend Frameworks
478
- "Node.js", "Express.js", "Nest.js", "Django", "Flask", "FastAPI", "Pyramid", "Tornado",
479
- "Spring Boot", "Spring MVC", "Struts", "Hibernate", "ASP.NET", "ASP.NET Core",
480
- "Ruby on Rails", "Sinatra", "Laravel", "Symfony", "CodeIgniter", "CakePHP", "Zend",
481
- "Gin", "Echo", "Fiber", "Actix", "Rocket", "Warp", "Axum",
482
-
483
- # Mobile Development
484
- "Android (Java)", "Android (Kotlin)", "iOS (Swift)", "iOS (Objective-C)",
485
-
486
- # Game Development
487
- "Unity", "Unreal Engine", "Godot", "GameMaker Studio", "Construct", "Phaser",
488
-
489
- # Database Technologies
490
- "MySQL", "PostgreSQL", "SQLite", "Microsoft SQL Server", "Oracle Database",
491
- "MongoDB", "Redis", "Cassandra", "DynamoDB", "Firebase", "Supabase",
492
- "CouchDB", "Neo4j", "InfluxDB", "TimescaleDB", "ClickHouse", "Apache Spark",
493
- "Elasticsearch", "Apache Solr", "Amazon RDS", "Google Cloud SQL",
494
-
495
- # Cloud & DevOps
496
- "AWS", "Google Cloud Platform", "Microsoft Azure", "Digital Ocean", "Heroku",
497
- "Docker", "Kubernetes", "Terraform", "Ansible", "Jenkins", "GitLab CI", "GitHub Actions",
498
- "Nginx", "Apache", "Linux", "Ubuntu", "CentOS", "Red Hat",
499
-
500
- # Data Science & AI/ML
501
- "TensorFlow", "PyTorch", "Scikit-learn", "Pandas", "NumPy", "Matplotlib", "Seaborn",
502
- "Jupyter", "Apache Airflow", "Apache Kafka", "Apache Flink", "Hadoop", "Spark",
503
- "Tableau", "Power BI", "D3.js", "Plotly", "OpenCV", "Keras", "XGBoost",
504
-
505
- # Testing Frameworks
506
- "Jest", "Mocha", "Chai", "Cypress", "Selenium", "Playwright", "Puppeteer",
507
- "JUnit", "TestNG", "Mockito", "PyTest", "unittest", "RSpec", "PHPUnit",
508
-
509
- # Other Technologies
510
- "GraphQL", "REST API", "gRPC", "WebSocket", "Apache Kafka", "RabbitMQ",
511
- "Blockchain", "Solidity", "Web3", "Ethereum", "Bitcoin", "Smart Contracts",
512
- "Microservices", "Serverless", "Lambda Functions", "API Gateway"
513
- ]
514
 
515
- # Sort the options alphabetically
516
- tech_options.sort()
 
 
 
 
517
 
518
- programming_language = st.selectbox(
519
- "Programming Language / Technology",
520
- tech_options,
521
- index=tech_options.index("Python") if "Python" in tech_options else 0,
522
- help="Select the primary programming language, framework, or technology for examples"
523
- )
524
- with col2:
525
- st.markdown("<br>", unsafe_allow_html=True) # Add spacing
526
- toggle_hinglish = st.toggle("Enable Hinglish", value=True, help="Enable mixing of Hindi and English")
527
-
528
- # Submit button
529
- st.markdown("<br>", unsafe_allow_html=True)
530
- col1, col2, col3 = st.columns([1, 2, 1])
531
- with col2:
532
- submitted = st.form_submit_button("🚀 Generate Course", use_container_width=True)
533
-
534
- # Handle submission
535
- if submitted:
536
- # Use the topics from session state directly
537
- topics_to_process = st.session_state.topics_list
538
-
539
- # Get course configuration
540
- course_config = COURSE_CONFIGS[selected_course]
541
- course_id = course_config["course_id"]
542
- chapter_id = course_config["chapter_id"]
543
-
544
- # Validate inputs
545
- valid_topics = []
546
- for topic in topics_to_process:
547
- if topic["topic_title"].strip() and topic["chapter_title"].strip():
548
- valid_topics.append(topic)
549
-
550
- if not valid_topics:
551
- st.error("❌ Please enter at least one topic with both topic title and chapter title")
552
- else:
553
- # Show loading state with better animation
554
- with st.spinner(f"🎬 Generating your {selected_course} course... This may take a few moments."):
555
- progress_bar = st.progress(0)
556
- for i in range(100):
557
- time.sleep(0.02) # Simulating progress
558
- progress_bar.progress(i + 1)
559
-
560
- # Hardcoded values
561
- user_id = DEFAULT_USER_ID
562
- personalization_id = DEFAULT_PERSONALIZATION_ID
563
-
564
- # Create user profile (hardcoded)
565
- user_profile = {
566
- "personalized": True,
567
- "user_name": "System User",
568
- "user_age": 25,
569
- "user_gender": "male",
570
- "user_tech_knowledge": "beginner",
571
- "user_preferred_activity": "coding, learning, technology",
572
- "user_food": "healthy food, vegetarian",
573
- "user_physical_activities": "walking, yoga",
574
- "learning_style": "visual",
575
- "target_language": target_language,
576
- "tts_gender": tts_gender,
577
- "tts_voice": tts_voice,
578
- "toggle_hinglish": toggle_hinglish,
579
- "run_visualization": False,
580
- "subtitle": "",
581
- "age_group": "18-25"
582
- }
583
-
584
- # Create settings
585
- settings = {
586
- "target_language": target_language,
587
- "tts_gender": tts_gender,
588
- "tts_voice": tts_voice,
589
- "toggle_hinglish": toggle_hinglish,
590
- "run_visualization": False,
591
- "subtitle": "",
592
- "programming_language": programming_language,
593
- "slide_colour": "blue",
594
- "video_type": "base_video"
595
- }
596
-
597
- # Generate topics data with user input for titles and course-specific values
598
- topics_data = []
599
- for i, topic in enumerate(valid_topics):
600
- topics_data.append({
601
- "topic_id": 10834 + i, # Hardcoded with increment
602
- "topic_title": topic["topic_title"].strip(),
603
- "chapter_id": chapter_id, # From selected course
604
- "chapter_title": topic["chapter_title"].strip(), # User input
605
- "course_id": course_id, # From selected course
606
- "video_url": f"https://techlearn-dev.s3.ap-south-1.amazonaws.com/course_videos/{course_id}/{chapter_id}/172906{4365+i*50}.mp4", # Course-specific
607
- "video_duration": 462 + i*20, # Hardcoded with variation
608
- "sequence_number": i + 1,
609
- })
610
 
611
- # Create payload (always multiple topics structure)
612
- payload = {
613
- "personalization_id": personalization_id,
614
- "user_id": user_id,
615
- "course_id": course_id,
616
- "total_videos": len(topics_data),
617
- "created_at": datetime.utcnow().isoformat(),
618
- "user_profile": user_profile,
619
- "topics": topics_data,
620
- "settings": settings
621
- }
622
-
623
- # Make API call
624
- try:
625
- headers = {
626
- 'Content-Type': 'application/json'
627
- }
628
-
629
- response = requests.post(API_ENDPOINT, json=payload, headers=headers, timeout=API_TIMEOUT)
630
-
631
- if response.status_code == 200:
632
- response_data = response.json()
633
- session_ids = response_data.get("session_ids", [])
634
-
635
- st.success(f"🎉 {selected_course} generation started successfully!")
636
-
637
- # Store session IDs in session state
638
- st.session_state.session_ids.extend(session_ids)
639
-
640
- # Display results in a clean format
641
- st.markdown("### 📊 Generation Summary")
642
-
643
- col1, col2 = st.columns(2)
644
- with col1:
645
- st.markdown(f"""
646
- **Course**: {selected_course}
647
- **Course ID**: {course_id}
648
- **Programming Language**: {programming_language.capitalize()}
649
- **Target Language**: {target_language.capitalize()}
650
- """)
651
- with col2:
652
- st.markdown(f"""
653
- **Voice**: {tts_voice.capitalize()} ({tts_gender.capitalize()})
654
- **Topics Count**: {len(valid_topics)}
655
- **Hinglish**: {"Enabled" if toggle_hinglish else "Disabled"}
656
- **Chapter ID**: {chapter_id}
657
- """)
658
-
659
- # Display session IDs
660
- if session_ids:
661
- st.markdown("### 🔍 Session Tracking IDs")
662
- for i, session_id in enumerate(session_ids, 1):
663
- st.markdown(f'<div class="session-info">📝 <strong>Session {i}:</strong> <code>{session_id}</code></div>', unsafe_allow_html=True)
664
-
665
- # Display API response
666
- with st.expander("📊 View Full API Response", expanded=False):
667
- st.markdown('<div class="api-response">', unsafe_allow_html=True)
668
- st.json(response_data)
669
- st.markdown('</div>', unsafe_allow_html=True)
670
-
671
- # Information about tracking
672
- st.info(f"""
673
- 💡 **Tracking Information**
674
- You can track the progress of your {selected_course} course generation using the session IDs above.
675
- The processing status will be updated in DynamoDB table: {SESSION_TABLE}
676
- Region: {DYNAMODB_REGION}
677
-
678
- 👉 **Next Steps**: Switch to the "Admin Dashboard" to monitor your session progress in real-time!
679
- """)
680
-
681
- else:
682
- st.error(f"❌ API Error: {response.status_code}")
683
- if response.text:
684
- st.error(f"**Error Details**: {response.text}")
685
-
686
- except requests.exceptions.Timeout:
687
- st.error("⏰ Request timed out. Please try again later.")
688
- except requests.exceptions.ConnectionError:
689
- st.error("🌐 Connection error. Please check your internet connection.")
690
- except Exception as e:
691
- st.error(f"❌ API call failed: {str(e)}")
692
-
693
- # Show payload for debugging
694
- with st.expander("🐛 Debug Information", expanded=False):
695
- st.warning("Request payload for debugging:")
696
- st.json(payload)
 
1
  import streamlit as st
 
 
 
 
2
  import boto3
3
+ import time
4
+ from datetime import datetime
5
  import os
 
 
 
 
6
 
7
+ # DynamoDB configuration
8
  DYNAMODB_REGION = os.getenv("DYNAMODB_REGION", "ap-south-1")
9
  SESSION_TABLE = os.getenv("SESSION_TABLE", "SessionTracking")
10
+ COURSE_TABLE = os.getenv("COURSE_TABLE", "CourseTracking")
11
 
12
  # AWS Configuration
13
  AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
14
  AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
 
 
 
 
 
 
15
 
16
+ def fetch_courses():
17
+ """Fetch all courses from DynamoDB"""
18
+ try:
19
+ dynamodb = boto3.client(
20
+ "dynamodb",
21
+ region_name=DYNAMODB_REGION,
22
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
23
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
24
+ )
25
+
26
+ response = dynamodb.scan(TableName=COURSE_TABLE)
27
+ items = response.get("Items", [])
28
+ courses = []
29
+ for item in items:
30
+ course_detail = item.get("course_detail", {}).get("M", {})
31
+ courses.append({
32
+ "course_id": item.get("course_id", {}).get("N"),
33
+ "course_name": item.get("course_name", {}).get("S"),
34
+ "total_videos": item.get("total_videos", {}).get("N", "0"),
35
+ "created_at": item.get("created_at", {}).get("S", "-"),
36
+ "topic_id": course_detail.get("topic_id", {}).get("N", "-"),
37
+ "chapter_id": course_detail.get("chapter_id", {}).get("N", "-"),
38
+ })
39
+ return courses
40
+ except Exception as e:
41
+ st.error(f"⚠️ Error fetching courses: {e}")
42
+ return []
43
 
44
+ def fetch_sessions():
45
+ """Fetch all sessions from DynamoDB"""
46
+ try:
47
+ dynamodb = boto3.client(
48
+ "dynamodb",
49
+ region_name=DYNAMODB_REGION,
50
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
51
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
52
+ )
53
+
54
+ response = dynamodb.scan(TableName=SESSION_TABLE)
55
+ items = response.get("Items", [])
56
+ sessions = []
57
+ for item in items:
58
+ sessions.append({
59
+ "session_id": item.get("session_id", {}).get("S"),
60
+ "course_id": item.get("course_id", {}).get("N"),
61
+ "topic_id": item.get("topic_id", {}).get("N"),
62
+ "topic_title": item.get("topic_title", {}).get("S", "-"),
63
+ "status": item.get("status", {}).get("S", "unknown"),
64
+ "node": item.get("node", {}).get("S", "-"),
65
+ "created_at": item.get("created_at", {}).get("S", "-"),
66
+ "updated_at": item.get("updated_at", {}).get("S", "-"),
67
+ "video_url": item.get("video_url", {}).get("S", None),
68
+ })
69
+ return sessions
70
+ except Exception as e:
71
+ st.error(f"⚠️ Error fetching sessions: {e}")
72
+ return []
73
 
74
+ def get_sessions_by_course(course_id):
75
+ """Get sessions for a specific course"""
76
+ all_sessions = fetch_sessions()
77
+ return [session for session in all_sessions if session['course_id'] == str(course_id)]
 
 
 
 
 
 
 
 
78
 
79
+ def render_admin_dashboard():
80
+ """Render the admin dashboard page"""
81
+ st.markdown("<h1 style='color:#4F97FF;text-align:center;'>📊 Admin Dashboard</h1>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
+ # Auto-refresh controls
84
+ col1, col2, col3 = st.columns([1, 1, 2])
85
+ with col1:
86
+ if st.button("🔄 Refresh Now", key="manual_refresh"):
87
+ st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
+ with col2:
90
+ auto_refresh = st.toggle("Auto Refresh (30s)", value=False)
 
 
 
 
 
 
 
 
91
 
92
+ # Tabs for different views
93
+ tab1, tab2, tab3 = st.tabs(["📚 Courses Overview", "🎬 Sessions Overview", "📈 Course Details"])
 
 
 
 
94
 
95
+ with tab1:
96
+ render_courses_overview()
 
 
 
 
 
 
 
97
 
98
+ with tab2:
99
+ render_sessions_overview()
100
+
101
+ with tab3:
102
+ render_course_details()
 
 
 
103
 
104
+ # Auto-refresh mechanism
105
+ if auto_refresh:
106
+ time.sleep(30)
107
+ st.rerun()
108
+
109
+ def render_courses_overview():
110
+ """Render courses overview tab"""
111
+ st.markdown("### 📚 All Courses")
112
 
113
+ courses = fetch_courses()
 
 
 
114
 
115
+ if not courses:
116
+ st.warning("No courses found yet.")
117
+ st.info("Courses will appear here once you create them from the Course Generation page.")
118
+ return
 
 
 
 
 
 
119
 
120
+ # Display course count
121
+ st.markdown(f"**Total Courses: {len(courses)}**")
 
 
 
122
 
123
+ # Sort courses by created_at (newest first)
124
+ try:
125
+ courses.sort(key=lambda x: x['created_at'] if x['created_at'] != '-' else '1970-01-01', reverse=True)
126
+ except:
127
+ pass
128
 
129
+ # Display courses in cards
130
+ for i, course in enumerate(courses):
131
+ with st.container():
132
+ st.markdown(f"""
133
+ <div style="background:#2D2D2D;padding:1.5rem;border-radius:12px;margin-bottom:1rem;border:1px solid #444;">
134
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
135
+ <h4 style="color:#4F97FF;margin:0;">📚 {course['course_name']}</h4>
136
+ <span style="background:#10B981;color:black;padding:0.3rem 0.8rem;border-radius:15px;font-size:0.8rem;font-weight:bold;">COURSE ID: {course['course_id']}</span>
137
+ </div>
138
+ <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem;">
139
+ <div>
140
+ <b>📹 Total Videos:</b><br><span style="color:#E0E0E0;">{course['total_videos']}</span>
141
+ </div>
142
+ <div>
143
+ <b>🎯 Topic ID:</b><br><span style="color:#E0E0E0;">{course['topic_id']}</span>
144
+ </div>
145
+ <div>
146
+ <b>📖 Chapter ID:</b><br><span style="color:#E0E0E0;">{course['chapter_id']}</span>
147
+ </div>
148
+ </div>
149
+ <div style="margin-top:1rem;">
150
+ <b>📅 Created:</b> <span style="color:#888;">{course['created_at'][:19] if course['created_at'] != '-' else '-'}</span>
151
+ </div>
152
+ </div>
153
+ """, unsafe_allow_html=True)
154
+
155
+ def render_sessions_overview():
156
+ """Render sessions overview tab"""
157
+ st.markdown("### 🎬 All Sessions")
158
 
159
+ sessions = fetch_sessions()
 
 
 
 
 
 
 
160
 
161
+ if not sessions:
162
+ st.warning("No sessions found yet.")
163
+ st.info("Sessions will appear here once you start generating courses.")
164
+ return
 
 
 
 
165
 
166
+ # Display session count and status summary
167
+ col1, col2, col3, col4 = st.columns(4)
 
 
 
 
 
168
 
169
+ status_counts = {}
170
+ for session in sessions:
171
+ status = session['status'].lower()
172
+ status_counts[status] = status_counts.get(status, 0) + 1
 
173
 
174
+ with col1:
175
+ st.metric("📊 Total Sessions", len(sessions))
176
+ with col2:
177
+ completed = status_counts.get('completed', 0)
178
+ st.metric("✅ Completed", completed)
179
+ with col3:
180
+ processing = status_counts.get('processing', 0) + status_counts.get('in_progress', 0) + status_counts.get('running', 0)
181
+ st.metric("⏳ Processing", processing)
182
+ with col4:
183
+ failed = status_counts.get('failed', 0) + status_counts.get('error', 0)
184
+ st.metric("❌ Failed", failed)
185
 
186
+ # Sort sessions by updated_at (newest first)
187
+ try:
188
+ sessions.sort(key=lambda x: x['updated_at'] if x['updated_at'] != '-' else '1970-01-01', reverse=True)
189
+ except:
190
+ pass
 
 
 
191
 
192
+ # Display sessions
193
+ for i, session in enumerate(sessions):
194
+ status_color = {
195
+ 'completed': '#10B981',
196
+ 'processing': '#F59E0B',
197
+ 'in_progress': '#F59E0B',
198
+ 'running': '#F59E0B',
199
+ 'failed': '#EF4444',
200
+ 'error': '#EF4444'
201
+ }.get(session['status'].lower(), '#6B7280')
202
+
203
+ with st.container():
204
+ st.markdown(f"""
205
+ <div style="background:#2D2D2D;padding:1.5rem;border-radius:12px;margin-bottom:1rem;border:1px solid #444;">
206
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
207
+ <h4 style="color:#4F97FF;margin:0;">🎬 {session['topic_title']}</h4>
208
+ <span style="background:{status_color};color:white;padding:0.3rem 0.8rem;border-radius:15px;font-size:0.8rem;font-weight:bold;">{session['status'].upper()}</span>
209
+ </div>
210
+ <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem;">
211
+ <div>
212
+ <b>🆔 Session ID:</b><br><code style="background:#1A1A1A;padding:0.2rem;border-radius:4px;font-size:0.8rem;">{session['session_id'][:20]}...</code>
213
+ </div>
214
+ <div>
215
+ <b>📚 Course ID:</b><br><span style="color:#E0E0E0;">{session['course_id']}</span>
216
+ </div>
217
+ <div>
218
+ <b>🎯 Topic ID:</b><br><span style="color:#E0E0E0;">{session['topic_id']}</span>
219
+ </div>
220
+ </div>
221
+ <div style="margin-top:1rem;display:flex;justify-content:space-between;">
222
+ <div><b>🔧 Current Node:</b> <span style="color:#E0E0E0;">{session['node']}</span></div>
223
+ <div><b>⏱️ Updated:</b> <span style="color:#888;">{session['updated_at'][:19] if session['updated_at'] != '-' else '-'}</span></div>
224
+ </div>
225
+ {f"<div style='margin-top:1rem;'><b>🎥 Video:</b> <a href='{session['video_url']}' target='_blank' style='color:#4F97FF;'>📺 Watch Video</a></div>" if session['video_url'] else ""}
226
+ </div>
227
+ """, unsafe_allow_html=True)
228
 
229
+ def render_course_details():
230
+ """Render detailed course view with sessions"""
231
+ st.markdown("### 📈 Detailed Course View")
232
 
233
+ courses = fetch_courses()
 
 
 
234
 
235
+ if not courses:
236
+ st.warning("No courses found.")
237
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
+ # Course selection
240
+ course_options = {f"{course['course_name']} (ID: {course['course_id']})": course['course_id'] for course in courses}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
 
242
+ if course_options:
243
+ selected_course_display = st.selectbox(
244
+ "Select a course to view details:",
245
+ options=list(course_options.keys()),
246
+ key="course_detail_selector"
 
 
247
  )
 
 
 
 
 
 
 
 
 
 
 
248
 
249
+ selected_course_id = course_options[selected_course_display]
250
+ selected_course = next(course for course in courses if course['course_id'] == str(selected_course_id))
 
 
 
 
 
 
 
251
 
252
+ # Display course information
253
+ st.markdown(f"""
254
+ <div style="background:#2D2D2D;padding:2rem;border-radius:12px;margin-bottom:2rem;border:1px solid #444;">
255
+ <h3 style="color:#4F97FF;margin-bottom:1rem;">📚 {selected_course['course_name']}</h3>
256
+ <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem;">
257
+ <div><b>🆔 Course ID:</b> {selected_course['course_id']}</div>
258
+ <div><b>📹 Total Videos:</b> {selected_course['total_videos']}</div>
259
+ <div><b>📅 Created:</b> {selected_course['created_at'][:19] if selected_course['created_at'] != '-' else '-'}</div>
260
+ </div>
261
+ </div>
262
+ """, unsafe_allow_html=True)
263
 
264
+ # Get sessions for this course
265
+ course_sessions = get_sessions_by_course(selected_course_id)
 
 
 
 
266
 
267
+ if course_sessions:
268
+ st.markdown(f"#### 🎬 Sessions for this Course ({len(course_sessions)} sessions)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
+ # Session status summary for this course
271
+ col1, col2, col3 = st.columns(3)
272
+ course_status_counts = {}
273
+ for session in course_sessions:
274
+ status = session['status'].lower()
275
+ course_status_counts[status] = course_status_counts.get(status, 0) + 1
276
 
277
+ with col1:
278
+ completed = course_status_counts.get('completed', 0)
279
+ st.metric("✅ Completed", completed)
280
+ with col2:
281
+ processing = course_status_counts.get('processing', 0) + course_status_counts.get('in_progress', 0) + course_status_counts.get('running', 0)
282
+ st.metric("⏳ Processing", processing)
283
+ with col3:
284
+ failed = course_status_counts.get('failed', 0) + course_status_counts.get('error', 0)
285
+ st.metric(" Failed", failed)
286
+
287
+ # Display sessions in a table format
288
+ for session in course_sessions:
289
+ status_color = {
290
+ 'completed': '#10B981',
291
+ 'processing': '#F59E0B',
292
+ 'in_progress': '#F59E0B',
293
+ 'running': '#F59E0B',
294
+ 'failed': '#EF4444',
295
+ 'error': '#EF4444'
296
+ }.get(session['status'].lower(), '#6B7280')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
+ st.markdown(f"""
299
+ <div style="background:#1E1E1E;padding:1rem;border-radius:8px;margin-bottom:0.5rem;border-left:4px solid {status_color};">
300
+ <div style="display:flex;justify-content:between;align-items:center;">
301
+ <div style="flex:2;"><b>{session['topic_title']}</b></div>
302
+ <div style="flex:1;text-align:center;"><span style="background:{status_color};color:white;padding:0.2rem 0.6rem;border-radius:10px;font-size:0.75rem;">{session['status'].upper()}</span></div>
303
+ <div style="flex:1;text-align:right;color:#888;font-size:0.8rem;">{session['node']}</div>
304
+ </div>
305
+ {f"<div style='margin-top:0.5rem;'><a href='{session['video_url']}' target='_blank' style='color:#4F97FF;font-size:0.8rem;'>🎥 Watch Video</a></div>" if session['video_url'] else ""}
306
+ </div>
307
+ """, unsafe_allow_html=True)
308
+ else:
309
+ st.info("No sessions found for this course yet.")