Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
|
@@ -798,12 +798,23 @@ def approve_project_plan(project_id):
|
|
| 798 |
logger.info(f"[PROJECT APPROVAL] Selected option: {selected_option}")
|
| 799 |
|
| 800 |
image_download_start = time.time()
|
| 801 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
image_download_time = time.time() - image_download_start
|
| 803 |
logger.info(f"[PROJECT APPROVAL] Image download completed in {image_download_time:.3f}s, size: {len(response.content)} bytes")
|
| 804 |
|
| 805 |
image_processing_start = time.time()
|
| 806 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 807 |
image_processing_time = time.time() - image_processing_start
|
| 808 |
logger.info(f"[PROJECT APPROVAL] Image processing completed in {image_processing_time:.3f}s")
|
| 809 |
|
|
@@ -860,19 +871,77 @@ def approve_project_plan(project_id):
|
|
| 860 |
parsing_time = time.time() - parsing_start
|
| 861 |
logger.info(f"[PROJECT APPROVAL] Response parsing completed in {parsing_time:.3f}s, found {len(inline_images)} images")
|
| 862 |
|
| 863 |
-
# Text extraction timing
|
| 864 |
extraction_start = time.time()
|
| 865 |
-
tools_section = re.search(r"TOOLS AND MATERIALS:\s*(.*?)\s*STEPS:", combined_text, re.DOTALL).group(1).strip()
|
| 866 |
-
steps_section = re.search(r"STEPS:\s*(.*)", combined_text, re.DOTALL).group(1).strip()
|
| 867 |
|
| 868 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
parsed_steps = parse_numbered_steps(steps_section)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 870 |
extraction_time = time.time() - extraction_start
|
| 871 |
logger.info(f"[PROJECT APPROVAL] Text extraction completed in {extraction_time:.3f}s, tools: {len(tools_list)}, steps: {len(parsed_steps)}")
|
| 872 |
|
| 873 |
if len(parsed_steps) != len(inline_images):
|
| 874 |
logger.error(f"[PROJECT APPROVAL] ERROR: AI response mismatch - Steps: {len(parsed_steps)}, Images: {len(inline_images)}")
|
| 875 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 876 |
|
| 877 |
# Step processing timing
|
| 878 |
step_processing_start = time.time()
|
|
@@ -883,30 +952,42 @@ def approve_project_plan(project_id):
|
|
| 883 |
for i, step_info in enumerate(parsed_steps):
|
| 884 |
logger.info(f"[PROJECT APPROVAL] Processing step {i+1}/{len(parsed_steps)}")
|
| 885 |
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
|
| 890 |
-
|
| 891 |
-
|
| 892 |
-
|
| 893 |
-
|
| 894 |
-
|
| 895 |
-
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
| 899 |
-
|
| 900 |
-
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
|
| 909 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 910 |
|
| 911 |
step_processing_time = time.time() - step_processing_start
|
| 912 |
logger.info(f"[PROJECT APPROVAL] All steps processing completed in {step_processing_time:.3f}s")
|
|
@@ -921,7 +1002,7 @@ def approve_project_plan(project_id):
|
|
| 921 |
"selectedOption": selected_option or ""
|
| 922 |
}
|
| 923 |
project_ref.update(update_data)
|
| 924 |
-
logger.info(f"[PROJECT APPROVAL]
|
| 925 |
db_update_time = time.time() - db_update_start
|
| 926 |
logger.info(f"[PROJECT APPROVAL] Database update completed in {db_update_time:.3f}s")
|
| 927 |
|
|
@@ -967,7 +1048,7 @@ def approve_project_plan(project_id):
|
|
| 967 |
logger.error(f"[PROJECT APPROVAL] Project ID: {project_id}, User ID: {uid}")
|
| 968 |
import traceback
|
| 969 |
logger.error(f"[PROJECT APPROVAL] Full traceback: {traceback.format_exc()}")
|
| 970 |
-
return jsonify({'error': str(e)}), 500
|
| 971 |
|
| 972 |
@app.route('/api/projects', methods=['GET'])
|
| 973 |
def list_projects():
|
|
|
|
| 798 |
logger.info(f"[PROJECT APPROVAL] Selected option: {selected_option}")
|
| 799 |
|
| 800 |
image_download_start = time.time()
|
| 801 |
+
try:
|
| 802 |
+
response = requests.get(project_data['userImageURL'], timeout=30)
|
| 803 |
+
response.raise_for_status()
|
| 804 |
+
except requests.RequestException as e:
|
| 805 |
+
logger.error(f"[PROJECT APPROVAL] ERROR: Image download failed: {e}")
|
| 806 |
+
return jsonify({'error': 'Failed to download project image'}), 500
|
| 807 |
+
|
| 808 |
image_download_time = time.time() - image_download_start
|
| 809 |
logger.info(f"[PROJECT APPROVAL] Image download completed in {image_download_time:.3f}s, size: {len(response.content)} bytes")
|
| 810 |
|
| 811 |
image_processing_start = time.time()
|
| 812 |
+
try:
|
| 813 |
+
pil_image = Image.open(io.BytesIO(response.content)).convert('RGB')
|
| 814 |
+
except Exception as e:
|
| 815 |
+
logger.error(f"[PROJECT APPROVAL] ERROR: Image processing failed: {e}")
|
| 816 |
+
return jsonify({'error': 'Failed to process project image'}), 500
|
| 817 |
+
|
| 818 |
image_processing_time = time.time() - image_processing_start
|
| 819 |
logger.info(f"[PROJECT APPROVAL] Image processing completed in {image_processing_time:.3f}s")
|
| 820 |
|
|
|
|
| 871 |
parsing_time = time.time() - parsing_start
|
| 872 |
logger.info(f"[PROJECT APPROVAL] Response parsing completed in {parsing_time:.3f}s, found {len(inline_images)} images")
|
| 873 |
|
| 874 |
+
# Text extraction timing with robust error handling
|
| 875 |
extraction_start = time.time()
|
|
|
|
|
|
|
| 876 |
|
| 877 |
+
# Add debug logging to see what the AI actually returned
|
| 878 |
+
logger.info(f"[PROJECT APPROVAL] AI Response structure check:")
|
| 879 |
+
logger.info(f"[PROJECT APPROVAL] Full response length: {len(combined_text)}")
|
| 880 |
+
logger.info(f"[PROJECT APPROVAL] Contains 'TOOLS AND MATERIALS': {'TOOLS AND MATERIALS' in combined_text.upper()}")
|
| 881 |
+
logger.info(f"[PROJECT APPROVAL] Contains 'STEPS': {'STEPS' in combined_text.upper()}")
|
| 882 |
+
logger.info(f"[PROJECT APPROVAL] Response preview: {combined_text[:300]}...")
|
| 883 |
+
|
| 884 |
+
# More robust regex patterns with error handling - updated for your production format
|
| 885 |
+
tools_match = re.search(r"TOOLS AND MATERIALS:\s*(.*?)\s*(?=STEPS\(|STEPS:|$)", combined_text, re.DOTALL | re.IGNORECASE)
|
| 886 |
+
steps_match = re.search(r"STEPS\([^)]*\):\s*(.*)|STEPS:\s*(.*)", combined_text, re.DOTALL | re.IGNORECASE)
|
| 887 |
+
|
| 888 |
+
if not tools_match:
|
| 889 |
+
logger.error(f"[PROJECT APPROVAL] ERROR: Could not find TOOLS AND MATERIALS section in AI response")
|
| 890 |
+
logger.error(f"[PROJECT APPROVAL] AI Response full text: {combined_text}")
|
| 891 |
+
return jsonify({'error': 'AI response format error: Could not parse tools section'}), 500
|
| 892 |
+
|
| 893 |
+
if not steps_match:
|
| 894 |
+
logger.error(f"[PROJECT APPROVAL] ERROR: Could not find STEPS section in AI response")
|
| 895 |
+
logger.error(f"[PROJECT APPROVAL] AI Response full text: {combined_text}")
|
| 896 |
+
return jsonify({'error': 'AI response format error: Could not parse steps section'}), 500
|
| 897 |
+
|
| 898 |
+
tools_section = tools_match.group(1).strip()
|
| 899 |
+
steps_section = (steps_match.group(1) or steps_match.group(2)).strip() if steps_match else ""
|
| 900 |
+
|
| 901 |
+
# Additional validation
|
| 902 |
+
if not tools_section:
|
| 903 |
+
logger.error(f"[PROJECT APPROVAL] ERROR: Empty tools section found")
|
| 904 |
+
return jsonify({'error': 'AI response format error: Empty tools section'}), 500
|
| 905 |
+
|
| 906 |
+
if not steps_section:
|
| 907 |
+
logger.error(f"[PROJECT APPROVAL] ERROR: Empty steps section found")
|
| 908 |
+
return jsonify({'error': 'AI response format error: Empty steps section'}), 500
|
| 909 |
+
|
| 910 |
+
tools_list = [line.strip("- ").strip() for line in tools_section.split('\n') if line.strip() and not line.strip().startswith('-')]
|
| 911 |
+
# Also include lines that start with dashes
|
| 912 |
+
dash_tools = [line.strip("- ").strip() for line in tools_section.split('\n') if line.strip().startswith('-')]
|
| 913 |
+
tools_list.extend(dash_tools)
|
| 914 |
+
|
| 915 |
+
# Remove duplicates while preserving order
|
| 916 |
+
seen = set()
|
| 917 |
+
tools_list = [x for x in tools_list if not (x in seen or seen.add(x))]
|
| 918 |
+
|
| 919 |
parsed_steps = parse_numbered_steps(steps_section)
|
| 920 |
+
|
| 921 |
+
# Validate parsed results
|
| 922 |
+
if not tools_list:
|
| 923 |
+
logger.error(f"[PROJECT APPROVAL] ERROR: No tools parsed from response")
|
| 924 |
+
logger.error(f"[PROJECT APPROVAL] Tools section was: {tools_section}")
|
| 925 |
+
return jsonify({'error': 'AI response format error: No tools found'}), 500
|
| 926 |
+
|
| 927 |
+
if not parsed_steps:
|
| 928 |
+
logger.error(f"[PROJECT APPROVAL] ERROR: No steps parsed from response")
|
| 929 |
+
logger.error(f"[PROJECT APPROVAL] Steps section was: {steps_section}")
|
| 930 |
+
return jsonify({'error': 'AI response format error: No steps found'}), 500
|
| 931 |
+
|
| 932 |
extraction_time = time.time() - extraction_start
|
| 933 |
logger.info(f"[PROJECT APPROVAL] Text extraction completed in {extraction_time:.3f}s, tools: {len(tools_list)}, steps: {len(parsed_steps)}")
|
| 934 |
|
| 935 |
if len(parsed_steps) != len(inline_images):
|
| 936 |
logger.error(f"[PROJECT APPROVAL] ERROR: AI response mismatch - Steps: {len(parsed_steps)}, Images: {len(inline_images)}")
|
| 937 |
+
# Try to handle the mismatch gracefully
|
| 938 |
+
min_length = min(len(parsed_steps), len(inline_images))
|
| 939 |
+
if min_length > 0:
|
| 940 |
+
logger.info(f"[PROJECT APPROVAL] Attempting to proceed with {min_length} steps/images")
|
| 941 |
+
parsed_steps = parsed_steps[:min_length]
|
| 942 |
+
inline_images = inline_images[:min_length]
|
| 943 |
+
else:
|
| 944 |
+
return jsonify({'error': 'AI response mismatch: No valid steps and images found.'}), 500
|
| 945 |
|
| 946 |
# Step processing timing
|
| 947 |
step_processing_start = time.time()
|
|
|
|
| 952 |
for i, step_info in enumerate(parsed_steps):
|
| 953 |
logger.info(f"[PROJECT APPROVAL] Processing step {i+1}/{len(parsed_steps)}")
|
| 954 |
|
| 955 |
+
try:
|
| 956 |
+
# Image upload timing
|
| 957 |
+
image_upload_start = time.time()
|
| 958 |
+
img_byte_arr = io.BytesIO()
|
| 959 |
+
inline_images[i].save(img_byte_arr, format='JPEG', optimize=True, quality=70)
|
| 960 |
+
img_path = f"users/{uid}/projects/{project_id}/steps/step_{i+1}_image.jpg"
|
| 961 |
+
img_url = upload_to_storage(img_byte_arr.getvalue(), img_path, 'image/jpeg')
|
| 962 |
+
image_upload_time = time.time() - image_upload_start
|
| 963 |
+
total_upload_time += image_upload_time
|
| 964 |
+
logger.info(f"[PROJECT APPROVAL] Step {i+1} image upload completed in {image_upload_time:.3f}s")
|
| 965 |
+
|
| 966 |
+
# TTS generation timing
|
| 967 |
+
tts_start = time.time()
|
| 968 |
+
narration_url = generate_tts_audio_and_upload(step_info['text'], uid, project_id, i + 1)
|
| 969 |
+
tts_time = time.time() - tts_start
|
| 970 |
+
total_tts_time += tts_time
|
| 971 |
+
logger.info(f"[PROJECT APPROVAL] Step {i+1} TTS generation completed in {tts_time:.3f}s")
|
| 972 |
+
|
| 973 |
+
step_info.update({
|
| 974 |
+
"imageUrl": img_url,
|
| 975 |
+
"narrationUrl": narration_url,
|
| 976 |
+
"isDone": False,
|
| 977 |
+
"notes": ""
|
| 978 |
+
})
|
| 979 |
+
final_steps.append(step_info)
|
| 980 |
+
|
| 981 |
+
except Exception as e:
|
| 982 |
+
logger.error(f"[PROJECT APPROVAL] ERROR processing step {i+1}: {e}")
|
| 983 |
+
# Continue with other steps rather than failing entirely
|
| 984 |
+
step_info.update({
|
| 985 |
+
"imageUrl": "",
|
| 986 |
+
"narrationUrl": "",
|
| 987 |
+
"isDone": False,
|
| 988 |
+
"notes": ""
|
| 989 |
+
})
|
| 990 |
+
final_steps.append(step_info)
|
| 991 |
|
| 992 |
step_processing_time = time.time() - step_processing_start
|
| 993 |
logger.info(f"[PROJECT APPROVAL] All steps processing completed in {step_processing_time:.3f}s")
|
|
|
|
| 1002 |
"selectedOption": selected_option or ""
|
| 1003 |
}
|
| 1004 |
project_ref.update(update_data)
|
| 1005 |
+
logger.info(f"[PROJECT APPROVAL] Updating data in db: {len(update_data)} fields")
|
| 1006 |
db_update_time = time.time() - db_update_start
|
| 1007 |
logger.info(f"[PROJECT APPROVAL] Database update completed in {db_update_time:.3f}s")
|
| 1008 |
|
|
|
|
| 1048 |
logger.error(f"[PROJECT APPROVAL] Project ID: {project_id}, User ID: {uid}")
|
| 1049 |
import traceback
|
| 1050 |
logger.error(f"[PROJECT APPROVAL] Full traceback: {traceback.format_exc()}")
|
| 1051 |
+
return jsonify({'error': f'Internal server error: {str(e)}'}), 500
|
| 1052 |
|
| 1053 |
@app.route('/api/projects', methods=['GET'])
|
| 1054 |
def list_projects():
|