banao-tech commited on
Commit
0838b93
Β·
verified Β·
1 Parent(s): 279e214

Update AdminDashboard.py

Browse files
Files changed (1) hide show
  1. AdminDashboard.py +280 -36
AdminDashboard.py CHANGED
@@ -1,65 +1,309 @@
1
  import streamlit as st
2
  import boto3
3
- import os
4
  from datetime import datetime
 
5
 
6
- # AWS DynamoDB config
7
- dynamodb = boto3.client(
8
- "dynamodb",
9
- region_name=os.getenv("DYNAMODB_REGION", "ap-south-1"),
10
- aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
11
- aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
12
- )
13
-
14
- TABLE_NAME = os.getenv("SESSION_TABLE", "SessionTracking")
15
 
16
- # Page Config
17
- st.set_page_config(
18
- page_title="πŸ“Š Admin Dashboard - Base Videos",
19
- layout="wide",
20
- initial_sidebar_state="expanded",
21
- )
22
 
23
- st.markdown("<h1 style='color:#4F97FF;text-align:center;'>πŸ“Š Base Video Generation Dashboard</h1>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- # Fetch all sessions
26
  def fetch_sessions():
 
27
  try:
28
- response = dynamodb.scan(TableName=TABLE_NAME)
 
 
 
 
 
 
 
29
  items = response.get("Items", [])
30
  sessions = []
31
  for item in items:
32
  sessions.append({
33
  "session_id": item.get("session_id", {}).get("S"),
 
 
 
34
  "status": item.get("status", {}).get("S", "unknown"),
35
  "node": item.get("node", {}).get("S", "-"),
 
36
  "updated_at": item.get("updated_at", {}).get("S", "-"),
37
- "video_url": item.get("video_url", {}).get("S", None), # optional if final node adds it
38
  })
39
  return sessions
40
  except Exception as e:
41
  st.error(f"⚠️ Error fetching sessions: {e}")
42
  return []
43
 
44
- # Auto-refresh every 15 seconds
45
- st_autorefresh = st.empty()
46
- sessions = fetch_sessions()
 
47
 
48
- if not sessions:
49
- st.warning("No sessions found yet.")
50
- else:
51
- for s in sessions:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  with st.container():
53
  st.markdown(f"""
54
- <div style="background:#2D2D2D;padding:1rem;border-radius:10px;margin-bottom:1rem;border:1px solid #444;">
55
- <b>πŸ†” Session:</b> <code>{s['session_id']}</code><br>
56
- <b>πŸ”„ Current Node:</b> {s['node']}<br>
57
- <b>πŸ“Œ Status:</b> <span style="color:{'lime' if s['status']=='completed' else 'orange'};">{s['status']}</span><br>
58
- <b>⏱️ Last Update:</b> {s['updated_at']}<br>
59
- {"<b>πŸŽ₯ Final Video:</b> <a href='"+s['video_url']+"' target='_blank'>Watch here</a>" if s['video_url'] else ""}
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  </div>
61
  """, unsafe_allow_html=True)
62
 
63
- # Manual refresh button
64
- if st.button("πŸ”„ Refresh Now"):
65
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.")