thanhkt commited on
Commit
b54a19e
Β·
1 Parent(s): 8fe40e4

Update gradio_app.py

Browse files
Files changed (1) hide show
  1. gradio_app.py +198 -39
gradio_app.py CHANGED
@@ -3,6 +3,8 @@ import gradio as gr
3
  import asyncio
4
  import uuid
5
  import threading
 
 
6
  from datetime import datetime
7
  import logging
8
  import traceback
@@ -71,7 +73,6 @@ def delete_job(job_id):
71
  try:
72
  # Remove the entire output directory for this job
73
  output_dir = os.path.dirname(job['output_file'])
74
- import shutil
75
  shutil.rmtree(output_dir, ignore_errors=True)
76
  except Exception as e:
77
  logger.error(f"Error removing output files: {e}")
@@ -722,29 +723,92 @@ with gr.Blocks(
722
  )
723
 
724
  with gr.Tab("πŸ“‚ Job History & Management"):
725
- with gr.Row():
726
- with gr.Column(scale=3):
727
- refresh_jobs_btn = gr.Button("πŸ”„ Refresh Job List", variant="secondary")
728
- with gr.Column(scale=1):
729
- clear_completed_btn = gr.Button("🧹 Clear Completed Jobs", variant="secondary")
730
- clear_all_btn = gr.Button("πŸ—‘οΈ Clear All Jobs", variant="stop")
731
-
732
  jobs_table = gr.Dataframe(
733
  headers=["ID", "Topic", "Status", "Progress (%)", "Start Time", "Message"],
734
  datatype=["str", "str", "str", "number", "str", "str"],
735
  interactive=False,
736
- label="πŸ“‹ Job History",
737
- wrap=True
 
738
  )
739
-
740
  with gr.Row():
741
- with gr.Column():
742
- select_job_btn = gr.Button("πŸ‘οΈ View Selected Job", variant="primary")
743
- selected_job_id = gr.Textbox(label="Selected Job ID", visible=False)
744
- with gr.Column():
745
- delete_job_btn = gr.Button("πŸ—‘οΈ Delete Selected Job", variant="stop")
746
- download_job_btn = gr.Button("πŸ’Ύ Download Job Results", variant="secondary")
747
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
748
  with gr.Tab("ℹ️ Help & Documentation"):
749
  gr.Markdown("""
750
  ## 🎯 How to Use Theory2Manim
@@ -1027,6 +1091,7 @@ with gr.Blocks(
1027
  )
1028
 
1029
  # Job history tab functions
 
1030
  def load_job_list():
1031
  jobs = get_job_list()
1032
  rows = []
@@ -1040,33 +1105,111 @@ with gr.Blocks(
1040
  formatted_time = start_time
1041
  else:
1042
  formatted_time = 'Unknown'
1043
-
1044
  rows.append([
1045
- job['id'][:8] + '...',
1046
- job['topic'][:50] + ('...' if len(job['topic']) > 50 else ''),
1047
- job['status'].title(),
1048
- job['progress'],
1049
  formatted_time,
1050
  job['message'][:100] + ('...' if len(job['message']) > 100 else '')
1051
  ])
1052
  return rows
1053
 
1054
  def select_job(evt: gr.EventData):
1055
- if not evt:
1056
- return "", "No job selected"
1057
-
1058
- selected_row = evt.index[0] if hasattr(evt, 'index') and evt.index else 0
1059
  jobs = get_job_list()
1060
  if selected_row < len(jobs):
1061
- return jobs[selected_row]['id'], f"Selected job: {jobs[selected_row]['topic']}"
1062
- return "", "No job selected"
 
1063
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1064
  def delete_selected_job(job_id):
1065
- if job_id:
1066
- result = delete_job(job_id)
1067
- return result, ""
1068
- return "No job selected", ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1069
 
 
1070
  refresh_jobs_btn.click(
1071
  fn=load_job_list,
1072
  outputs=[jobs_table]
@@ -1077,17 +1220,31 @@ with gr.Blocks(
1077
 
1078
  jobs_table.select(
1079
  fn=select_job,
1080
- outputs=[selected_job_id, result_text]
1081
  )
1082
 
1083
  select_job_btn.click(
1084
- fn=lambda x: gr.update(visible=True) if x else gr.update(visible=False),
1085
  inputs=[selected_job_id],
1086
- outputs=[status_container]
1087
- ).then(
1088
- fn=update_status_display,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1089
  inputs=[selected_job_id],
1090
- outputs=[status_text, video_output, video_output, thumbnail_preview, scene_gallery, processing_time_text, cancel_btn]
1091
  )
1092
 
1093
  delete_job_btn.click(
@@ -1145,6 +1302,8 @@ with gr.Blocks(
1145
  )
1146
 
1147
 
 
 
1148
  if __name__ == "__main__":
1149
  import os
1150
  app.queue().launch(
 
3
  import asyncio
4
  import uuid
5
  import threading
6
+ import subprocess
7
+ import shutil
8
  from datetime import datetime
9
  import logging
10
  import traceback
 
73
  try:
74
  # Remove the entire output directory for this job
75
  output_dir = os.path.dirname(job['output_file'])
 
76
  shutil.rmtree(output_dir, ignore_errors=True)
77
  except Exception as e:
78
  logger.error(f"Error removing output files: {e}")
 
723
  )
724
 
725
  with gr.Tab("πŸ“‚ Job History & Management"):
726
+ # Job list table (full width)
 
 
 
 
 
 
727
  jobs_table = gr.Dataframe(
728
  headers=["ID", "Topic", "Status", "Progress (%)", "Start Time", "Message"],
729
  datatype=["str", "str", "str", "number", "str", "str"],
730
  interactive=False,
731
+ label=None,
732
+ wrap=True,
733
+ elem_classes=["job-history-table"]
734
  )
735
+ # Action buttons (horizontal row, full width)
736
  with gr.Row():
737
+ select_job_btn = gr.Button("πŸ‘οΈ View Details", variant="primary", size="sm")
738
+ delete_job_btn = gr.Button("πŸ—‘οΈ Delete", variant="stop", size="sm")
739
+ download_job_btn = gr.Button("πŸ’Ύ Download", variant="secondary", size="sm")
740
+ refresh_jobs_btn = gr.Button("πŸ”„ Refresh List", variant="secondary", size="sm")
741
+ clear_completed_btn = gr.Button("🧹 Clear Completed", variant="secondary", size="sm")
742
+ clear_all_btn = gr.Button("πŸ—‘οΈ Clear All", variant="stop", size="sm")
743
+ selected_job_id = gr.Textbox(label="Selected Job ID", visible=False)
744
+ # Job details viewer (full width, below buttons)
745
+ with gr.Group(elem_classes=["job-details-panel"]):
746
+ gr.Markdown("""
747
+ <div style='font-size:1.2em; font-weight:600; margin-bottom:0.5em;'>
748
+ πŸ‘οΈ <span style='color:#3b82f6'>Job Details Viewer</span>
749
+ </div>
750
+ """)
751
+ close_details_btn = gr.Button("⬅️ Back to Job List", variant="secondary", size="sm", visible=False)
752
+ job_details_container = gr.Column(visible=False)
753
+ with job_details_container:
754
+ with gr.Row():
755
+ with gr.Column(scale=2):
756
+ job_topic_display = gr.Textbox(label="πŸ“š Topic", interactive=False)
757
+ job_description_display = gr.Textbox(label="πŸ“ Description", interactive=False, lines=3)
758
+ job_model_display = gr.Textbox(label="πŸ€– Model Used", interactive=False)
759
+ with gr.Column(scale=1):
760
+ job_status_display = gr.Textbox(label="πŸ“Š Status", interactive=False)
761
+ job_progress_display = gr.Number(label="πŸ“ˆ Progress (%)", interactive=False)
762
+ job_start_time_display = gr.Textbox(label="⏰ Start Time", interactive=False)
763
+ with gr.Row():
764
+ job_processing_time_display = gr.Textbox(label="⏱️ Processing Time", interactive=False)
765
+ job_message_display = gr.Textbox(label="πŸ’¬ Current Message", interactive=False)
766
+ with gr.Column(visible=False) as job_video_container:
767
+ gr.Markdown("### 🎬 Generated Video")
768
+ job_video_player = gr.Video(
769
+ label="Video Output",
770
+ interactive=False,
771
+ show_download_button=True,
772
+ height=300
773
+ )
774
+ with gr.Row():
775
+ with gr.Column(scale=1):
776
+ job_thumbnail_display = gr.Image(
777
+ label="πŸ–ΌοΈ Thumbnail",
778
+ height=150,
779
+ interactive=False
780
+ )
781
+ with gr.Column(scale=2):
782
+ job_scene_gallery = gr.Gallery(
783
+ label="🎨 Scene Previews",
784
+ columns=3,
785
+ object_fit="contain",
786
+ height=150,
787
+ show_download_button=True
788
+ )
789
+ with gr.Column(visible=False) as job_error_container:
790
+ gr.Markdown("### ❌ Error Details")
791
+ job_error_display = gr.Textbox(
792
+ label="Error Message",
793
+ interactive=False,
794
+ lines=3
795
+ )
796
+ job_stack_trace_display = gr.Textbox(
797
+ label="Stack Trace",
798
+ interactive=False,
799
+ lines=5,
800
+ max_lines=10
801
+ )
802
+ no_job_selected = gr.Markdown(
803
+ """
804
+ <div style='padding:2em 0;text-align:center;color:#888;'>
805
+ <b>πŸ“„ No Job Selected</b><br>
806
+ Select a job from the list to view its details.
807
+ </div>
808
+ """,
809
+ visible=True
810
+ )
811
+
812
  with gr.Tab("ℹ️ Help & Documentation"):
813
  gr.Markdown("""
814
  ## 🎯 How to Use Theory2Manim
 
1091
  )
1092
 
1093
  # Job history tab functions
1094
+
1095
  def load_job_list():
1096
  jobs = get_job_list()
1097
  rows = []
 
1105
  formatted_time = start_time
1106
  else:
1107
  formatted_time = 'Unknown'
 
1108
  rows.append([
1109
+ job['id'][:8] + '...',
1110
+ job['topic'][:50] + ('...' if len(job['topic']) > 50 else ''),
1111
+ job['status'].title(),
1112
+ job['progress'],
1113
  formatted_time,
1114
  job['message'][:100] + ('...' if len(job['message']) > 100 else '')
1115
  ])
1116
  return rows
1117
 
1118
  def select_job(evt: gr.EventData):
1119
+ if not evt or not hasattr(evt, 'index') or not evt.index:
1120
+ # No job selected
1121
+ return "", "No job selected", gr.update(visible=False)
1122
+ selected_row = evt.index[0]
1123
  jobs = get_job_list()
1124
  if selected_row < len(jobs):
1125
+ # Job selected
1126
+ return jobs[selected_row]['id'], f"Selected job: {jobs[selected_row]['topic']}", gr.update(visible=True)
1127
+ return "", "No job selected", gr.update(visible=False)
1128
 
1129
+ def back_to_job_list():
1130
+ # Show job list, hide details
1131
+ return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
1132
+
1133
+ def view_job_details(job_id):
1134
+ """View details of a selected job."""
1135
+ if not job_id or job_id not in job_status:
1136
+ # Return 17 outputs, all hidden or empty
1137
+ return (
1138
+ gr.update(visible=False), # job_details_container
1139
+ gr.update(visible=True), # no_job_selected
1140
+ "", "", "", "", 0, "", "", "", # topic, desc, model, status, progress, start, proc_time, msg
1141
+ gr.update(visible=False), # job_video_container
1142
+ gr.update(visible=False, value=None), # job_video_player
1143
+ gr.update(visible=False, value=None), # job_thumbnail_display
1144
+ gr.update(visible=False, value=[]), # job_scene_gallery
1145
+ gr.update(visible=False), # job_error_container
1146
+ gr.update(visible=False, value=""), # job_error_display
1147
+ gr.update(visible=False, value="") # job_stack_trace_display
1148
+ )
1149
+ job = job_status[job_id]
1150
+ # Format start time
1151
+ start_time = job.get('start_time', '')
1152
+ if start_time:
1153
+ try:
1154
+ dt = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
1155
+ formatted_time = dt.strftime('%Y-%m-%d %H:%M:%S')
1156
+ except:
1157
+ formatted_time = start_time
1158
+ else:
1159
+ formatted_time = 'Unknown'
1160
+
1161
+ # Video and error visibility
1162
+ is_completed = job.get('status') == 'completed'
1163
+ is_failed = job.get('status') == 'failed'
1164
+ # Always return 17 outputs in order
1165
+ return (
1166
+ gr.update(visible=True), # job_details_container
1167
+ gr.update(visible=False), # no_job_selected
1168
+ job.get('topic', ''),
1169
+ job.get('description', ''),
1170
+ job.get('model', ''),
1171
+ gr.update(value=job.get('status', '').title()), # status_display
1172
+ job.get('progress', 0),
1173
+ formatted_time,
1174
+ job.get('processing_time', ''),
1175
+ job.get('message', ''),
1176
+ gr.update(visible=is_completed), # job_video_container
1177
+ gr.update(visible=is_completed, value=job.get('output_file') if is_completed else None), # job_video_player
1178
+ gr.update(visible=is_completed and job.get('thumbnail') is not None, value=job.get('thumbnail') if is_completed else None), # job_thumbnail_display
1179
+ gr.update(visible=is_completed, value=job.get('scene_snapshots', []) if is_completed else []), # job_scene_gallery
1180
+ gr.update(visible=is_failed), # job_error_container
1181
+ gr.update(visible=is_failed, value=job.get('error', '') if is_failed else ""), # job_error_display
1182
+ gr.update(visible=is_failed, value=job.get('stack_trace', '') if is_failed else "") # job_stack_trace_display
1183
+ )
1184
+
1185
  def delete_selected_job(job_id):
1186
+ """Delete the selected job and update the UI."""
1187
+ if not job_id or job_id not in job_status:
1188
+ return "Job not found", None, gr.update(visible=False)
1189
+
1190
+ # Delete the job
1191
+ result = delete_job(job_id)
1192
+
1193
+ # Update job list
1194
+ jobs = get_job_list()
1195
+
1196
+ # Refresh job table
1197
+ return result, gr.update(value=load_job_list()), gr.update(visible=False)
1198
+
1199
+ def download_job_results(job_id):
1200
+ """Download the results of a job."""
1201
+ if not job_id or job_id not in job_status:
1202
+ return "Job not found", None
1203
+
1204
+ job = job_status[job_id]
1205
+ output_file = job.get('output_file')
1206
+
1207
+ if not output_file or not os.path.exists(output_file):
1208
+ return "Output file not found", None
1209
+
1210
+ return "Download started", output_file
1211
 
1212
+ # Connect job history tab event handlers
1213
  refresh_jobs_btn.click(
1214
  fn=load_job_list,
1215
  outputs=[jobs_table]
 
1220
 
1221
  jobs_table.select(
1222
  fn=select_job,
1223
+ outputs=[selected_job_id, result_text, close_details_btn]
1224
  )
1225
 
1226
  select_job_btn.click(
1227
+ fn=view_job_details,
1228
  inputs=[selected_job_id],
1229
+ outputs=[
1230
+ job_details_container, no_job_selected,
1231
+ job_topic_display, job_description_display, job_model_display,
1232
+ job_status_display, job_progress_display, job_start_time_display,
1233
+ job_processing_time_display, job_message_display,
1234
+ job_video_container, job_video_player, job_thumbnail_display, job_scene_gallery,
1235
+ job_error_container, job_error_display, job_stack_trace_display
1236
+ ]
1237
+ )
1238
+
1239
+ close_details_btn.click(
1240
+ fn=back_to_job_list,
1241
+ outputs=[job_details_container, no_job_selected, close_details_btn]
1242
+ )
1243
+
1244
+ download_job_btn.click(
1245
+ fn=download_job_results,
1246
  inputs=[selected_job_id],
1247
+ outputs=[result_text]
1248
  )
1249
 
1250
  delete_job_btn.click(
 
1302
  )
1303
 
1304
 
1305
+
1306
+
1307
  if __name__ == "__main__":
1308
  import os
1309
  app.queue().launch(