nniehaus commited on
Commit
8d264bd
·
verified ·
1 Parent(s): 9540a98

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -65
app.py CHANGED
@@ -39,20 +39,39 @@ if "token_usage" not in st.session_state:
39
 
40
  # Function to encode image to base64
41
  def encode_image(image):
42
- # Convert RGBA to RGB if needed
43
- if image.mode == 'RGBA':
44
- background = Image.new('RGB', image.size, (255, 255, 255))
45
- background.paste(image, (0, 0), image)
46
- image = background
47
-
48
- # Resize large images to reduce token usage
49
- max_size = (1024, 1024)
50
- if image.width > max_size[0] or image.height > max_size[1]:
51
- image.thumbnail(max_size, Image.LANCZOS)
52
-
53
- buffered = BytesIO()
54
- image.save(buffered, format="JPEG", quality=85)
55
- return base64.b64encode(buffered.getvalue()).decode('utf-8')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
  # Function to fix formatting issues in text
58
  def fix_formatting(text):
@@ -72,6 +91,9 @@ def analyze_home_photos(images, timeframe, additional_details, api_key):
72
  if not api_key:
73
  return "Error: API Key is required for analysis."
74
 
 
 
 
75
  headers = {
76
  "Content-Type": "application/json",
77
  "Authorization": f"Bearer {api_key}"
@@ -90,15 +112,22 @@ def analyze_home_photos(images, timeframe, additional_details, api_key):
90
  ]
91
 
92
  # Add images to the message with proper format for vision API
93
- for img in images:
94
- base64_img = encode_image(img)
95
- content.append({
96
- "type": "image_url",
97
- "image_url": {
98
- "url": f"data:image/jpeg;base64,{base64_img}",
99
- "detail": "low" # Use low detail to reduce token usage
100
- }
101
- })
 
 
 
 
 
 
 
102
 
103
  # Enhanced system prompt for more specific and actionable recommendations
104
  system_prompt = """You are an expert real estate advisor with deep knowledge of home selling strategies. You analyze home photos to provide EXTREMELY SPECIFIC, actionable recommendations that will maximize the property's selling price.
@@ -224,7 +253,7 @@ For example, if selling within 1-3 months:
224
  error_text += f" - {response.text[:200]}"
225
  return f"Error: {error_text}. Please check your API key and try again."
226
  except Exception as e:
227
- return f"Error: {str(e)}"
228
 
229
  # Calculate estimated cost
230
  def calculate_cost(token_usage):
@@ -476,26 +505,52 @@ with col1:
476
  # Process uploaded images
477
  if uploaded_files:
478
  images = []
 
 
 
479
  for file in uploaded_files:
480
  try:
481
- image = Image.open(file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
482
  # Resize large images to reduce token usage
483
  max_size = (1024, 1024)
484
  if image.width > max_size[0] or image.height > max_size[1]:
485
  image.thumbnail(max_size, Image.LANCZOS)
 
486
  images.append(image)
 
487
  except Exception as e:
488
- st.error(f"Error opening image {file.name}: {e}")
489
 
 
490
  st.session_state["uploaded_images"] = images
491
 
492
- # Display uploaded images
493
  if images:
494
- st.write(f"**{len(images)} images uploaded**")
495
  image_cols = st.columns(min(3, len(images)))
496
  for i, img in enumerate(images):
497
  with image_cols[i % min(3, len(images))]:
498
- st.image(img, width=150, caption=f"Image {i+1}")
 
 
 
 
 
 
499
 
500
  # Custom subheader with background
501
  st.markdown('<div class="custom-subheader">🏠 Selling Timeline & Details</div>', unsafe_allow_html=True)
@@ -548,42 +603,50 @@ with col2:
548
  if analyze_button or "analysis_result" in st.session_state:
549
  # Process analysis if button pressed
550
  if analyze_button:
551
- # Simple loading indicator using pure Streamlit - no HTML, JS or CSS
552
- with st.spinner("🏡 Analyzing your home photos..."):
553
- # Show a progress bar
554
- progress_bar = st.progress(0)
555
-
556
- # Show the analysis steps with native Streamlit components
557
- steps_placeholder = st.empty()
558
- steps_placeholder.info("Step 1: Identifying property features and conditions...")
559
- progress_bar.progress(25)
560
- time.sleep(1) # Small delay for visual effect
561
-
562
- steps_placeholder.info("Step 2: Evaluating improvement opportunities and researching specific recommendations...")
563
- progress_bar.progress(50)
564
- time.sleep(1) # Small delay for visual effect
565
-
566
- steps_placeholder.info("Step 3: Calculating ROI potential and precise cost estimates...")
567
- progress_bar.progress(75)
568
- time.sleep(1) # Small delay for visual effect
569
-
570
- steps_placeholder.info("Step 4: Creating detailed timeline and finalizing recommendations...")
571
-
572
- # Make the actual API call
573
- analysis_text = analyze_home_photos(
574
- st.session_state["uploaded_images"],
575
- timeframe,
576
- f"Budget: ${max_budget}. Focus areas: {', '.join(improvement_focus)}. DIY preference: {diy_preference}. {additional_details}",
577
- st.session_state["api_key"]
578
- )
579
-
580
- # Complete the progress bar
581
- progress_bar.progress(100)
582
-
583
- # Store the result and clear the loading indicators
584
- st.session_state["analysis_result"] = analysis_text
585
- steps_placeholder.empty()
586
- progress_bar.empty()
 
 
 
 
 
 
 
 
587
 
588
  # Display results
589
  if "analysis_result" in st.session_state:
 
39
 
40
  # Function to encode image to base64
41
  def encode_image(image):
42
+ try:
43
+ # Convert RGBA to RGB if needed
44
+ if image.mode == 'RGBA':
45
+ background = Image.new('RGB', image.size, (255, 255, 255))
46
+ background.paste(image, (0, 0), image)
47
+ image = background
48
+ elif image.mode not in ['RGB', 'L']:
49
+ # Convert any other mode to RGB
50
+ image = image.convert('RGB')
51
+
52
+ # Verify the image has valid dimensions
53
+ if image.width <= 0 or image.height <= 0:
54
+ raise ValueError("Image has invalid dimensions: width or height ≤ 0")
55
+
56
+ # Resize large images to reduce token usage
57
+ max_size = (1024, 1024)
58
+ if image.width > max_size[0] or image.height > max_size[1]:
59
+ image.thumbnail(max_size, Image.LANCZOS)
60
+
61
+ # Save to buffer with error handling
62
+ buffered = BytesIO()
63
+ image.save(buffered, format="JPEG", quality=85)
64
+ if buffered.getvalue() == b'':
65
+ raise ValueError("Generated empty image data")
66
+
67
+ # Encode to base64
68
+ encoded = base64.b64encode(buffered.getvalue()).decode('utf-8')
69
+ if not encoded:
70
+ raise ValueError("Base64 encoding produced empty result")
71
+
72
+ return encoded
73
+ except Exception as e:
74
+ raise Exception(f"Image encoding failed: {str(e)}")
75
 
76
  # Function to fix formatting issues in text
77
  def fix_formatting(text):
 
91
  if not api_key:
92
  return "Error: API Key is required for analysis."
93
 
94
+ if not images or len(images) == 0:
95
+ return "Error: No valid images provided for analysis. Please upload at least one image of your home."
96
+
97
  headers = {
98
  "Content-Type": "application/json",
99
  "Authorization": f"Bearer {api_key}"
 
112
  ]
113
 
114
  # Add images to the message with proper format for vision API
115
+ try:
116
+ for i, img in enumerate(images):
117
+ try:
118
+ base64_img = encode_image(img)
119
+ content.append({
120
+ "type": "image_url",
121
+ "image_url": {
122
+ "url": f"data:image/jpeg;base64,{base64_img}",
123
+ "detail": "low" # Use low detail to reduce token usage
124
+ }
125
+ })
126
+ except Exception as img_error:
127
+ # Log error but continue with other images
128
+ print(f"Error processing image {i}: {str(img_error)}")
129
+ except Exception as e:
130
+ return f"Error preparing images for analysis: {str(e)}"
131
 
132
  # Enhanced system prompt for more specific and actionable recommendations
133
  system_prompt = """You are an expert real estate advisor with deep knowledge of home selling strategies. You analyze home photos to provide EXTREMELY SPECIFIC, actionable recommendations that will maximize the property's selling price.
 
253
  error_text += f" - {response.text[:200]}"
254
  return f"Error: {error_text}. Please check your API key and try again."
255
  except Exception as e:
256
+ return f"Error communicating with OpenAI API: {str(e)}. Please check your internet connection and try again."
257
 
258
  # Calculate estimated cost
259
  def calculate_cost(token_usage):
 
505
  # Process uploaded images
506
  if uploaded_files:
507
  images = []
508
+ valid_images = []
509
+ error_messages = []
510
+
511
  for file in uploaded_files:
512
  try:
513
+ # Read file content first to check if it's valid
514
+ file_bytes = file.getvalue()
515
+ if len(file_bytes) == 0:
516
+ error_messages.append(f"Error: {file.name} appears to be empty.")
517
+ continue
518
+
519
+ # Try to open the image with PIL
520
+ image = Image.open(BytesIO(file_bytes))
521
+
522
+ # Validate image by trying to get its format
523
+ image_format = image.format
524
+ if not image_format:
525
+ error_messages.append(f"Error: {file.name} doesn't appear to be a valid image format.")
526
+ continue
527
+
528
  # Resize large images to reduce token usage
529
  max_size = (1024, 1024)
530
  if image.width > max_size[0] or image.height > max_size[1]:
531
  image.thumbnail(max_size, Image.LANCZOS)
532
+
533
  images.append(image)
534
+ valid_images.append(file.name)
535
  except Exception as e:
536
+ error_messages.append(f"Error processing {file.name}: {str(e)}")
537
 
538
+ # Update session state with valid images
539
  st.session_state["uploaded_images"] = images
540
 
541
+ # Display uploaded images and errors
542
  if images:
543
+ st.write(f"**{len(images)} valid images uploaded**")
544
  image_cols = st.columns(min(3, len(images)))
545
  for i, img in enumerate(images):
546
  with image_cols[i % min(3, len(images))]:
547
+ st.image(img, width=150, caption=f"Image {i+1}: {valid_images[i]}")
548
+
549
+ # Display error messages if any
550
+ if error_messages:
551
+ with st.expander(f"⚠️ {len(error_messages)} image upload issues detected. Click to view details."):
552
+ for error in error_messages:
553
+ st.error(error)
554
 
555
  # Custom subheader with background
556
  st.markdown('<div class="custom-subheader">🏠 Selling Timeline & Details</div>', unsafe_allow_html=True)
 
603
  if analyze_button or "analysis_result" in st.session_state:
604
  # Process analysis if button pressed
605
  if analyze_button:
606
+ # Check if there are valid images
607
+ if len(st.session_state["uploaded_images"]) == 0:
608
+ st.error("No valid images to analyze. Please upload at least one home photo.")
609
+ else:
610
+ # Simple loading indicator using pure Streamlit - no HTML, JS or CSS
611
+ with st.spinner("🏡 Analyzing your home photos..."):
612
+ # Show a progress bar
613
+ progress_bar = st.progress(0)
614
+
615
+ # Show the analysis steps with native Streamlit components
616
+ steps_placeholder = st.empty()
617
+ steps_placeholder.info("Step 1: Identifying property features and conditions...")
618
+ progress_bar.progress(25)
619
+ time.sleep(0.5) # Reduced delay for faster response
620
+
621
+ steps_placeholder.info("Step 2: Evaluating improvement opportunities and researching specific recommendations...")
622
+ progress_bar.progress(50)
623
+ time.sleep(0.5) # Reduced delay for faster response
624
+
625
+ steps_placeholder.info("Step 3: Calculating ROI potential and precise cost estimates...")
626
+ progress_bar.progress(75)
627
+ time.sleep(0.5) # Reduced delay for faster response
628
+
629
+ steps_placeholder.info("Step 4: Creating detailed timeline and finalizing recommendations...")
630
+
631
+ # Make the actual API call with enhanced error handling
632
+ try:
633
+ analysis_text = analyze_home_photos(
634
+ st.session_state["uploaded_images"],
635
+ timeframe,
636
+ f"Budget: ${max_budget}. Focus areas: {', '.join(improvement_focus)}. DIY preference: {diy_preference}. {additional_details}",
637
+ st.session_state["api_key"]
638
+ )
639
+
640
+ # Store the result
641
+ st.session_state["analysis_result"] = analysis_text
642
+ except Exception as e:
643
+ st.error(f"Error during analysis: {str(e)}")
644
+ st.session_state["analysis_result"] = f"An error occurred during analysis: {str(e)}. Please try again with different images or check your API key."
645
+
646
+ # Complete the progress bar and clear the loading indicators
647
+ progress_bar.progress(100)
648
+ steps_placeholder.empty()
649
+ progress_bar.empty()
650
 
651
  # Display results
652
  if "analysis_result" in st.session_state: