Spaces:
Runtime error
Runtime error
| from flask import Flask, request, jsonify | |
| from STOUT import translate_forward, translate_reverse | |
| import logging | |
| from concurrent.futures import ThreadPoolExecutor, as_completed | |
| import time | |
| app = Flask(__name__) | |
| logging.basicConfig(level=logging.INFO) | |
| # Thread pool for parallel processing | |
| executor = ThreadPoolExecutor(max_workers=4) | |
| def home(): | |
| return jsonify({ | |
| "message": "STOUT V2 API - SMILES to IUPAC Translator", | |
| "endpoints": { | |
| "/smiles_to_iupac": "GET/POST - Convert SMILES to IUPAC name", | |
| "/iupac_to_smiles": "GET/POST - Convert IUPAC name to SMILES", | |
| "/batch/smiles_to_iupac": "POST - Convert multiple SMILES to IUPAC names", | |
| "/batch/iupac_to_smiles": "POST - Convert multiple IUPAC names to SMILES", | |
| "/health": "GET - Health check" | |
| } | |
| }) | |
| def health(): | |
| return jsonify({"status": "healthy"}) | |
| def process_single_smiles(smiles): | |
| """Process a single SMILES string""" | |
| try: | |
| iupac_name = translate_forward(smiles) | |
| return {"smiles": smiles, "iupac": iupac_name, "success": True} | |
| except Exception as e: | |
| return {"smiles": smiles, "error": str(e), "success": False} | |
| def process_single_iupac(iupac): | |
| """Process a single IUPAC name""" | |
| try: | |
| smiles = translate_reverse(iupac) | |
| return {"iupac": iupac, "smiles": smiles, "success": True} | |
| except Exception as e: | |
| return {"iupac": iupac, "error": str(e), "success": False} | |
| def smiles_to_iupac(): | |
| try: | |
| if request.method == 'GET': | |
| smiles = request.args.get('smiles') | |
| else: # POST | |
| data = request.get_json() | |
| smiles = data.get('smiles') if data else None | |
| if not smiles: | |
| return jsonify({"error": "Missing 'smiles' parameter"}), 400 | |
| result = process_single_smiles(smiles) | |
| if result['success']: | |
| return jsonify(result) | |
| else: | |
| return jsonify(result), 500 | |
| except Exception as e: | |
| app.logger.error(f"Error translating SMILES: {str(e)}") | |
| return jsonify({"error": str(e)}), 500 | |
| def batch_smiles_to_iupac(): | |
| """Process multiple SMILES strings in parallel""" | |
| try: | |
| start_time = time.time() | |
| data = request.get_json() | |
| if not data or 'smiles_list' not in data: | |
| return jsonify({"error": "Missing 'smiles_list' in request body"}), 400 | |
| smiles_list = data['smiles_list'] | |
| if not isinstance(smiles_list, list): | |
| return jsonify({"error": "'smiles_list' must be an array"}), 400 | |
| # Limit batch size to prevent abuse | |
| if len(smiles_list) > 100: | |
| return jsonify({"error": "Maximum batch size is 100"}), 400 | |
| # Process in parallel using thread pool | |
| futures = [executor.submit(process_single_smiles, smiles) for smiles in smiles_list] | |
| results = [] | |
| for future in as_completed(futures): | |
| results.append(future.result()) | |
| # Sort results to maintain input order | |
| results_dict = {r['smiles']: r for r in results} | |
| ordered_results = [results_dict.get(smiles, {"smiles": smiles, "error": "Not processed", "success": False}) | |
| for smiles in smiles_list] | |
| processing_time = time.time() - start_time | |
| return jsonify({ | |
| "results": ordered_results, | |
| "total": len(smiles_list), | |
| "successful": sum(1 for r in ordered_results if r.get('success', False)), | |
| "failed": sum(1 for r in ordered_results if not r.get('success', False)), | |
| "processing_time_seconds": round(processing_time, 3) | |
| }) | |
| except Exception as e: | |
| app.logger.error(f"Error in batch processing: {str(e)}") | |
| return jsonify({"error": str(e)}), 500 | |
| def batch_iupac_to_smiles(): | |
| """Process multiple IUPAC names in parallel""" | |
| try: | |
| start_time = time.time() | |
| data = request.get_json() | |
| if not data or 'iupac_list' not in data: | |
| return jsonify({"error": "Missing 'iupac_list' in request body"}), 400 | |
| iupac_list = data['iupac_list'] | |
| if not isinstance(iupac_list, list): | |
| return jsonify({"error": "'iupac_list' must be an array"}), 400 | |
| # Limit batch size to prevent abuse | |
| if len(iupac_list) > 100: | |
| return jsonify({"error": "Maximum batch size is 100"}), 400 | |
| # Process in parallel using thread pool | |
| futures = [executor.submit(process_single_iupac, iupac) for iupac in iupac_list] | |
| results = [] | |
| for future in as_completed(futures): | |
| results.append(future.result()) | |
| # Sort results to maintain input order | |
| results_dict = {r['iupac']: r for r in results} | |
| ordered_results = [results_dict.get(iupac, {"iupac": iupac, "error": "Not processed", "success": False}) | |
| for iupac in iupac_list] | |
| processing_time = time.time() - start_time | |
| return jsonify({ | |
| "results": ordered_results, | |
| "total": len(iupac_list), | |
| "successful": sum(1 for r in ordered_results if r.get('success', False)), | |
| "failed": sum(1 for r in ordered_results if not r.get('success', False)), | |
| "processing_time_seconds": round(processing_time, 3) | |
| }) | |
| except Exception as e: | |
| app.logger.error(f"Error in batch processing: {str(e)}") | |
| return jsonify({"error": str(e)}), 500 | |
| def iupac_to_smiles(): | |
| try: | |
| if request.method == 'GET': | |
| iupac = request.args.get('iupac') | |
| else: # POST | |
| data = request.get_json() | |
| iupac = data.get('iupac') if data else None | |
| if not iupac: | |
| return jsonify({"error": "Missing 'iupac' parameter"}), 400 | |
| result = process_single_iupac(iupac) | |
| if result['success']: | |
| return jsonify(result) | |
| else: | |
| return jsonify(result), 500 | |
| except Exception as e: | |
| app.logger.error(f"Error translating IUPAC: {str(e)}") | |
| return jsonify({"error": str(e)}), 500 | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860, debug=False) |