Sean Carnahan commited on
Commit
5dd4f2e
·
1 Parent(s): cd361a4

Add robust error handling and logging for HF Spaces

Browse files
Files changed (2) hide show
  1. Dockerfile +6 -3
  2. app.py +47 -15
Dockerfile CHANGED
@@ -12,12 +12,15 @@ RUN apt-get update && apt-get install -y \
12
  COPY requirements.txt .
13
  RUN pip install --no-cache-dir -r requirements.txt
14
 
 
 
 
 
 
 
15
  # Copy the rest of the application
16
  COPY . .
17
 
18
- # Create necessary directories
19
- RUN mkdir -p static/uploads
20
-
21
  # Set environment variables
22
  ENV PYTHONUNBUFFERED=1
23
  ENV FLASK_APP=app.py
 
12
  COPY requirements.txt .
13
  RUN pip install --no-cache-dir -r requirements.txt
14
 
15
+ # Create necessary directories with proper permissions
16
+ RUN mkdir -p /code/static/uploads \
17
+ && mkdir -p /code/logs \
18
+ && chmod -R 777 /code/static/uploads \
19
+ && chmod -R 777 /code/logs
20
+
21
  # Copy the rest of the application
22
  COPY . .
23
 
 
 
 
24
  # Set environment variables
25
  ENV PYTHONUNBUFFERED=1
26
  ENV FLASK_APP=app.py
app.py CHANGED
@@ -36,19 +36,35 @@ from bodybuilding_pose_analyzer.src.movenet_analyzer import MoveNetAnalyzer
36
  from bodybuilding_pose_analyzer.src.pose_analyzer import PoseAnalyzer
37
 
38
  # Configure logging
39
- logging.basicConfig(level=logging.INFO)
 
 
 
40
  logger = logging.getLogger(__name__)
41
 
 
 
 
 
 
 
 
42
  def log_memory_usage():
43
  """Log current memory usage."""
44
- process = psutil.Process()
45
- memory_info = process.memory_info()
46
- logger.info(f"Memory usage: {memory_info.rss / 1024 / 1024:.2f} MB")
 
 
 
47
 
48
  def cleanup_memory():
49
  """Force garbage collection and log memory usage."""
50
- gc.collect()
51
- log_memory_usage()
 
 
 
52
 
53
  def wrap_text(text: str, font_face: int, font_scale: float, thickness: int, max_width: int) -> list[str]:
54
  """Wrap text to fit within max_width."""
@@ -94,8 +110,17 @@ app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB max file size
94
  os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
95
 
96
  # Load CNN model for bodybuilding pose classification
97
- cnn_model_path = 'external/BodybuildingPoseClassifier/bodybuilding_pose_classifier.h5'
98
- cnn_model = load_model(cnn_model_path)
 
 
 
 
 
 
 
 
 
99
  cnn_class_labels = ['side_chest', 'front_double_biceps', 'back_double_biceps', 'front_lat_spread', 'back_lat_spread']
100
 
101
  def predict_pose_cnn(img_path):
@@ -387,7 +412,7 @@ def index():
387
  @app.route('/upload', methods=['POST'])
388
  def upload_file():
389
  try:
390
- cleanup_memory() # Clean up before processing
391
  if 'video' not in request.files:
392
  logger.error("[UPLOAD] No video file in request")
393
  return jsonify({'error': 'No video file provided'}), 400
@@ -413,6 +438,9 @@ def upload_file():
413
  unique_filename = f"{base}_{int(time.time())}{ext}"
414
  filepath = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
415
 
 
 
 
416
  logger.info(f"[UPLOAD] Saving file to: {filepath}")
417
  file.save(filepath)
418
 
@@ -435,8 +463,9 @@ def upload_file():
435
 
436
  logger.info(f"[UPLOAD] Processing complete. Output URL: {output_path_url}")
437
 
438
- if not os.path.exists(os.path.join(app.config['UPLOAD_FOLDER'], os.path.basename(output_path_url))):
439
- logger.error(f"[UPLOAD] Output file not found: {output_path_url}")
 
440
  return jsonify({'error': 'Output video file not found'}), 500
441
 
442
  return jsonify({
@@ -446,7 +475,7 @@ def upload_file():
446
 
447
  except Exception as e:
448
  logger.error(f"[UPLOAD] Error processing video: {str(e)}")
449
- traceback.print_exc()
450
  return jsonify({'error': f'Error processing video: {str(e)}'}), 500
451
 
452
  finally:
@@ -459,22 +488,25 @@ def upload_file():
459
 
460
  except Exception as e:
461
  logger.error(f"[UPLOAD] Unexpected error: {str(e)}")
462
- traceback.print_exc()
463
  return jsonify({'error': 'Internal server error'}), 500
464
  finally:
465
- cleanup_memory() # Clean up after processing
466
 
467
- # Add error handlers
468
  @app.errorhandler(413)
469
  def request_entity_too_large(error):
 
470
  return jsonify({'error': 'File too large. Maximum size is 100MB'}), 413
471
 
472
  @app.errorhandler(500)
473
  def internal_server_error(error):
 
474
  return jsonify({'error': 'Internal server error. Please try again later.'}), 500
475
 
476
  @app.errorhandler(404)
477
  def not_found_error(error):
 
478
  return jsonify({'error': 'Resource not found'}), 404
479
 
480
  if __name__ == '__main__':
 
36
  from bodybuilding_pose_analyzer.src.pose_analyzer import PoseAnalyzer
37
 
38
  # Configure logging
39
+ logging.basicConfig(
40
+ level=logging.INFO,
41
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
42
+ )
43
  logger = logging.getLogger(__name__)
44
 
45
+ # Add file handler for persistent logging
46
+ log_dir = 'logs'
47
+ os.makedirs(log_dir, exist_ok=True)
48
+ file_handler = logging.FileHandler(os.path.join(log_dir, 'app.log'))
49
+ file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
50
+ logger.addHandler(file_handler)
51
+
52
  def log_memory_usage():
53
  """Log current memory usage."""
54
+ try:
55
+ process = psutil.Process()
56
+ memory_info = process.memory_info()
57
+ logger.info(f"Memory usage: {memory_info.rss / 1024 / 1024:.2f} MB")
58
+ except Exception as e:
59
+ logger.error(f"Error logging memory usage: {e}")
60
 
61
  def cleanup_memory():
62
  """Force garbage collection and log memory usage."""
63
+ try:
64
+ gc.collect()
65
+ log_memory_usage()
66
+ except Exception as e:
67
+ logger.error(f"Error in cleanup_memory: {e}")
68
 
69
  def wrap_text(text: str, font_face: int, font_scale: float, thickness: int, max_width: int) -> list[str]:
70
  """Wrap text to fit within max_width."""
 
110
  os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
111
 
112
  # Load CNN model for bodybuilding pose classification
113
+ try:
114
+ logger.info("Loading CNN model...")
115
+ cnn_model_path = 'external/BodybuildingPoseClassifier/bodybuilding_pose_classifier.h5'
116
+ if not os.path.exists(cnn_model_path):
117
+ raise FileNotFoundError(f"CNN model not found at {cnn_model_path}")
118
+ cnn_model = load_model(cnn_model_path)
119
+ logger.info("CNN model loaded successfully")
120
+ except Exception as e:
121
+ logger.error(f"Error loading CNN model: {e}")
122
+ raise
123
+
124
  cnn_class_labels = ['side_chest', 'front_double_biceps', 'back_double_biceps', 'front_lat_spread', 'back_lat_spread']
125
 
126
  def predict_pose_cnn(img_path):
 
412
  @app.route('/upload', methods=['POST'])
413
  def upload_file():
414
  try:
415
+ cleanup_memory()
416
  if 'video' not in request.files:
417
  logger.error("[UPLOAD] No video file in request")
418
  return jsonify({'error': 'No video file provided'}), 400
 
438
  unique_filename = f"{base}_{int(time.time())}{ext}"
439
  filepath = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
440
 
441
+ # Ensure upload directory exists
442
+ os.makedirs(os.path.dirname(filepath), exist_ok=True)
443
+
444
  logger.info(f"[UPLOAD] Saving file to: {filepath}")
445
  file.save(filepath)
446
 
 
463
 
464
  logger.info(f"[UPLOAD] Processing complete. Output URL: {output_path_url}")
465
 
466
+ output_path = os.path.join(app.config['UPLOAD_FOLDER'], os.path.basename(output_path_url))
467
+ if not os.path.exists(output_path):
468
+ logger.error(f"[UPLOAD] Output file not found: {output_path}")
469
  return jsonify({'error': 'Output video file not found'}), 500
470
 
471
  return jsonify({
 
475
 
476
  except Exception as e:
477
  logger.error(f"[UPLOAD] Error processing video: {str(e)}")
478
+ logger.error(traceback.format_exc())
479
  return jsonify({'error': f'Error processing video: {str(e)}'}), 500
480
 
481
  finally:
 
488
 
489
  except Exception as e:
490
  logger.error(f"[UPLOAD] Unexpected error: {str(e)}")
491
+ logger.error(traceback.format_exc())
492
  return jsonify({'error': 'Internal server error'}), 500
493
  finally:
494
+ cleanup_memory()
495
 
496
+ # Add more specific error handlers
497
  @app.errorhandler(413)
498
  def request_entity_too_large(error):
499
+ logger.error(f"File too large: {error}")
500
  return jsonify({'error': 'File too large. Maximum size is 100MB'}), 413
501
 
502
  @app.errorhandler(500)
503
  def internal_server_error(error):
504
+ logger.error(f"Internal server error: {error}")
505
  return jsonify({'error': 'Internal server error. Please try again later.'}), 500
506
 
507
  @app.errorhandler(404)
508
  def not_found_error(error):
509
+ logger.error(f"Resource not found: {error}")
510
  return jsonify({'error': 'Resource not found'}), 404
511
 
512
  if __name__ == '__main__':