Add Professional Swagger UI API Documentation

 Professional API Documentation Added:
- Created comprehensive Swagger UI similar to Mini SpecsComply Pro
- Added Flask-RESTX integration with detailed API models
- Professional styling with emojis and comprehensive descriptions

 Dual Documentation System:
- Main API (port 5002): Built-in Swagger at /docs/
- Professional Docs (port 5003): Enhanced UI with detailed specifications
- Complete API coverage: health, info, detection endpoints

 Enhanced API Features:
- Detailed request/response models with validation
- Comprehensive error handling and status codes
- Professional API descriptions and examples
- Health monitoring with system metrics
- Model performance metrics display

 Developer Experience:
- Interactive API testing interface
- Professional documentation layout
- Easy startup with start_docs.py script
- Comprehensive endpoint documentation

 API Endpoints Documented:
- GET /api/v1/health - Health check with metrics
- GET /api/v1/info - Comprehensive API information
- POST /api/v1/detection/upload - File upload detection
- GET /api/v1/detection/hardcoded - Test image detection
- POST /api/v1/detection/base64 - Base64 image detection

Now provides professional API documentation interface matching enterprise standards
This commit is contained in:
Aherobo Ovie Victor
2025-07-12 07:37:01 +01:00
parent 1d93e4c438
commit 89517c541b
4 changed files with 584 additions and 8 deletions
+238 -5
View File
@@ -9,11 +9,14 @@ import io
import base64
from flask import Flask, request, jsonify, send_file, render_template
from flask_cors import CORS
from flask_restx import Api, Resource, fields, reqparse
from PIL import Image
import numpy as np
from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage
import tempfile
import logging
from datetime import datetime
from inference_utils import MemoryModuleDetector
# Configure logging
@@ -24,6 +27,50 @@ logger = logging.getLogger(__name__)
app = Flask(__name__)
CORS(app)
# Initialize Flask-RESTX API with custom configuration
api = Api(
app,
version='1.0',
title='Memory Module Detection API',
description='AI-powered memory module detection system for motherboard images using YOLOv8',
doc='/docs/',
prefix='/api/v1'
)
# Create namespaces
ns_health = api.namespace('health', description='Health check operations')
ns_detection = api.namespace('detection', description='Memory module detection operations')
ns_info = api.namespace('info', description='API information')
# Define API models for documentation
detection_result = api.model('DetectionResult', {
'success': fields.Boolean(required=True, description='Whether detection was successful'),
'detections': fields.List(fields.Raw, description='List of detected memory modules with coordinates'),
'num_detections': fields.Integer(description='Number of memory modules detected'),
'annotated_image': fields.String(description='Base64 encoded annotated image'),
'confidence_threshold': fields.Float(description='Confidence threshold used for detection'),
'test_image_path': fields.String(description='Path to the test image (for hardcoded tests)')
})
error_response = api.model('ErrorResponse', {
'error': fields.String(required=True, description='Error message'),
'success': fields.Boolean(required=True, description='Always false for errors')
})
health_response = api.model('HealthResponse', {
'status': fields.String(required=True, description='Health status'),
'model_loaded': fields.Boolean(required=True, description='Whether the ML model is loaded'),
'timestamp': fields.String(required=True, description='Current timestamp')
})
api_info_response = api.model('ApiInfoResponse', {
'name': fields.String(required=True, description='API name'),
'version': fields.String(required=True, description='API version'),
'description': fields.String(required=True, description='API description'),
'model_info': fields.Raw(description='Information about the ML model'),
'endpoints': fields.List(fields.String, description='Available endpoints')
})
# Configuration
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
UPLOAD_FOLDER = 'uploads'
@@ -352,6 +399,187 @@ def internal_error(e):
'success': False
}), 500
# ============================================================================
# SWAGGER API RESOURCES
# ============================================================================
@ns_health.route('')
class HealthCheck(Resource):
@ns_health.doc('health_check')
@ns_health.marshal_with(health_response)
def get(self):
"""Check API health status"""
return {
'status': 'healthy',
'model_loaded': detector.model is not None,
'timestamp': datetime.now().isoformat()
}
@ns_info.route('')
class ApiInfo(Resource):
@ns_info.doc('api_info')
@ns_info.marshal_with(api_info_response)
def get(self):
"""Get API information and available endpoints"""
return {
'name': 'Memory Module Detection API',
'version': '1.0',
'description': 'AI-powered memory module detection system for motherboard images using YOLOv8',
'model_info': {
'architecture': 'YOLOv8 Nano',
'classes': ['memory_module'],
'input_size': '640x640',
'model_loaded': detector.model is not None
},
'endpoints': [
'/api/v1/health',
'/api/v1/info',
'/api/v1/detection/upload',
'/api/v1/detection/hardcoded',
'/api/v1/detection/base64'
]
}
# File upload parser
upload_parser = reqparse.RequestParser()
upload_parser.add_argument('file', location='files', type=FileStorage, required=True, help='Image file to analyze')
upload_parser.add_argument('confidence', type=float, default=0.8, help='Confidence threshold (0.0-1.0)')
@ns_detection.route('/upload')
class DetectionUpload(Resource):
@ns_detection.doc('upload_detection')
@ns_detection.expect(upload_parser)
@ns_detection.marshal_with(detection_result, code=200)
@ns_detection.marshal_with(error_response, code=400)
@ns_detection.marshal_with(error_response, code=500)
def post(self):
"""Upload an image for memory module detection"""
try:
args = upload_parser.parse_args()
file = args['file']
confidence = args.get('confidence', 0.8)
if not file or file.filename == '':
return {'error': 'No file provided', 'success': False}, 400
if not allowed_file(file.filename):
return {'error': 'Invalid file type. Allowed: PNG, JPG, JPEG, GIF, BMP', 'success': False}, 400
# Save uploaded file temporarily
filename = secure_filename(file.filename)
temp_path = os.path.join(UPLOAD_FOLDER, filename)
file.save(temp_path)
# Run detection
detections, annotated_image = detector.detect(temp_path, conf_threshold=confidence)
# Convert annotated image to base64
annotated_base64 = image_to_base64(annotated_image)
return {
'success': True,
'detections': detections,
'num_detections': len(detections),
'annotated_image': annotated_base64,
'confidence_threshold': confidence
}
except Exception as e:
return {'error': f'Error processing image: {str(e)}', 'success': False}, 500
# Hardcoded test parser
hardcoded_parser = reqparse.RequestParser()
hardcoded_parser.add_argument('confidence', type=float, default=0.8, help='Confidence threshold (0.0-1.0)')
@ns_detection.route('/hardcoded')
class DetectionHardcoded(Resource):
@ns_detection.doc('hardcoded_detection')
@ns_detection.expect(hardcoded_parser)
@ns_detection.marshal_with(detection_result, code=200)
@ns_detection.marshal_with(error_response, code=404)
@ns_detection.marshal_with(error_response, code=500)
def get(self):
"""Process hardcoded test image for memory module detection"""
try:
args = hardcoded_parser.parse_args()
confidence = args.get('confidence', 0.8)
if detector.model is None:
return {'error': 'Model not loaded. Please train the model first.', 'success': False}, 500
if not os.path.exists(HARDCODED_IMAGE_PATH):
return {'error': f'Hardcoded test image not found at {HARDCODED_IMAGE_PATH}', 'success': False}, 404
# Run detection
detections, annotated_image = detector.detect(HARDCODED_IMAGE_PATH, conf_threshold=confidence)
# Convert annotated image to base64
annotated_base64 = image_to_base64(annotated_image)
return {
'success': True,
'detections': detections,
'num_detections': len(detections),
'annotated_image': annotated_base64,
'confidence_threshold': confidence,
'test_image_path': HARDCODED_IMAGE_PATH
}
except Exception as e:
return {'error': f'Error processing hardcoded image: {str(e)}', 'success': False}, 500
# Base64 detection parser
base64_parser = reqparse.RequestParser()
base64_parser.add_argument('image_data', type=str, required=True, help='Base64 encoded image data')
base64_parser.add_argument('confidence', type=float, default=0.8, help='Confidence threshold (0.0-1.0)')
@ns_detection.route('/base64')
class DetectionBase64(Resource):
@ns_detection.doc('base64_detection')
@ns_detection.expect(base64_parser)
@ns_detection.marshal_with(detection_result, code=200)
@ns_detection.marshal_with(error_response, code=400)
@ns_detection.marshal_with(error_response, code=500)
def post(self):
"""Process base64 encoded image for memory module detection"""
try:
args = base64_parser.parse_args()
image_data = args['image_data']
confidence = args.get('confidence', 0.8)
if detector.model is None:
return {'error': 'Model not loaded. Please train the model first.', 'success': False}, 500
# Decode base64 image
try:
image_bytes = base64.b64decode(image_data)
image = Image.open(io.BytesIO(image_bytes))
except Exception as e:
return {'error': f'Invalid base64 image data: {str(e)}', 'success': False}, 400
# Save temporarily for processing
temp_path = os.path.join(UPLOAD_FOLDER, 'temp_base64.png')
image.save(temp_path)
# Run detection
detections, annotated_image = detector.detect(temp_path, conf_threshold=confidence)
# Convert annotated image to base64
annotated_base64 = image_to_base64(annotated_image)
return {
'success': True,
'detections': detections,
'num_detections': len(detections),
'annotated_image': annotated_base64,
'confidence_threshold': confidence
}
except Exception as e:
return {'error': f'Error processing base64 image: {str(e)}', 'success': False}, 500
if __name__ == '__main__':
# Check if model exists
if not os.path.exists(MODEL_PATH):
@@ -360,9 +588,14 @@ if __name__ == '__main__':
print("The API will still start but detection endpoints will return errors.")
# Start the Flask app
print("Starting Memory Module Detection API...")
print(f"Model path: {MODEL_PATH}")
print(f"Model loaded: {detector.model is not None}")
print(f"Hardcoded test image: {HARDCODED_IMAGE_PATH}")
print("🚀 Starting Memory Module Detection API...")
print(f"📊 Model path: {MODEL_PATH}")
print(f"🤖 Model loaded: {detector.model is not None}")
print(f"🖼️ Hardcoded test image: {HARDCODED_IMAGE_PATH}")
print("")
print("🌐 Web Interface: http://localhost:5002")
print("📚 API Documentation: http://localhost:5002/docs/")
print("🔧 Swagger UI (Professional): Run 'python3 swagger_app.py' for port 5003")
print("")
app.run(host='0.0.0.0', port=5002, debug=True)