chandra7799 commited on
Commit
f849d19
·
verified ·
1 Parent(s): 6b75cdd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -122
app.py CHANGED
@@ -9,9 +9,10 @@ import shutil
9
  import base64
10
  import pytz
11
  import logging
 
12
 
13
- # Configure logging
14
- logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
15
  logger = logging.getLogger(__name__)
16
 
17
  # Load environment variables
@@ -21,7 +22,8 @@ SF_PASSWORD = os.getenv("SF_PASSWORD")
21
  SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
22
 
23
  if not all([SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN]):
24
- raise ValueError("Missing Salesforce credentials.")
 
25
 
26
  # Connect to Salesforce
27
  try:
@@ -29,135 +31,106 @@ try:
29
  username=SF_USERNAME,
30
  password=SF_PASSWORD,
31
  security_token=SF_SECURITY_TOKEN,
32
- domain="login"
33
  )
34
- logger.info("Connected to Salesforce successfully.")
35
  except Exception as e:
36
  logger.error(f"Salesforce connection failed: {str(e)}")
37
  raise
38
 
39
  VALID_MILESTONES = ["Planning", "Foundation", "Walls Erected", "Completed"]
40
- local_timezone = pytz.timezone("Asia/Kolkata")
41
-
42
- # Simulated content-based milestone detection (placeholder for ML model)
 
 
 
 
 
 
 
 
43
  def detect_milestone_from_image(image_path):
44
- """
45
- Placeholder for content-based milestone detection.
46
- In a real implementation, use a trained ML model to analyze image features.
47
- """
48
  try:
49
- img = Image.open(image_path).convert("RGB")
50
  img_array = np.array(img)
51
- # Example: Use color distribution as a proxy for milestone
52
- red_mean = np.mean(img_array[:, :, 0])
53
- green_mean = np.mean(img_array[:, :, 1])
54
- blue_mean = np.mean(img_array[:, :, 2])
55
 
56
- # Simulated logic: Adjust these thresholds based on real data or ML model
57
- if red_mean > 150 and green_mean < 100: # High red (e.g., soil)
58
  return "Planning"
59
- elif green_mean > 120: # High green (e.g., vegetation or scaffolding)
60
  return "Foundation"
61
- elif blue_mean > 130: # High blue (e.g., sky visible with walls)
62
  return "Walls Erected"
63
- else: # Balanced colors (e.g., finished building)
64
  return "Completed"
65
  except Exception as e:
66
- logger.error(f"Error in milestone detection: {str(e)}")
67
- return "Planning" # Default to earliest milestone on error
68
 
69
- def validate_project_name(project_name):
70
- """Check if project name is unique in Salesforce."""
71
- try:
72
- query = f"SELECT Id FROM Construction__c WHERE Name__c = '{project_name}'"
73
- result = sf.query(query)
74
- if result["totalSize"] > 0:
75
- return False, "Error: Project name already exists in Salesforce."
76
- return True, ""
77
- except Exception as e:
78
- logger.error(f"Error validating project name: {str(e)}")
79
- return False, f"Error: Failed to validate project name - {str(e)}"
80
-
81
- def calculate_completion_percentage(milestone, image_count):
82
- """Calculate completion percentage based on milestone and image count."""
83
- milestone_base = {
84
- "Planning": 10,
85
- "Foundation": 30,
86
- "Walls Erected": 50,
87
- "Completed": 90
88
- }
89
- base_percentage = milestone_base.get(milestone, 10)
90
- # Add incremental progress based on image count (max 10% boost)
91
- increment = min(image_count * 2, 10)
92
- total_percentage = min(base_percentage + increment, 100)
93
- return total_percentage
94
-
95
- def process_image(images, project_name, confirmed_milestone=None):
96
  try:
97
- if not images or len(images) == 0:
98
- return "Error: Please upload at least one image.", "Pending", "", "", 0, []
99
 
100
- if not project_name:
101
- return "Error: Project name is required.", "Failure", "", "", 0, []
102
-
103
- # Validate project name uniqueness
104
- is_valid, validation_error = validate_project_name(project_name)
105
- if not is_valid:
106
- return validation_error, "Failure", "", "", 0, []
107
 
108
  milestones = []
109
  file_urls = []
110
- for image in images:
111
- img = Image.open(image)
112
- image_size_mb = os.path.getsize(image) / (1024 * 1024)
113
- if image_size_mb > 20:
114
- return f"Error: Image {image} exceeds 20MB.", "Failure", "", "", 0, []
115
- if not str(image).lower().endswith(('.jpg', '.jpeg', '.png')):
116
- return "Error: Only JPG or PNG images are supported.", "Failure", "", "", 0, []
117
-
118
- # Save image to local directory
119
- upload_dir = "public_uploads"
120
- os.makedirs(upload_dir, exist_ok=True)
121
- unique_id = datetime.now().strftime("%Y%m%d%H%M%S")
122
- image_filename = f"{unique_id}_{os.path.basename(image)}"
123
- saved_path = os.path.join(upload_dir, image_filename)
124
- shutil.copy2(image, saved_path)
125
-
126
- # Upload image to Salesforce
127
- with open(saved_path, 'rb') as image_file:
128
- image_data = base64.b64encode(image_file.read()).decode('8')
129
-
130
- try:
 
131
  content_version = {
132
  'Title': image_filename,
133
  'PathOnClient': image_filename,
134
  'VersionData': image_data
135
  }
136
- content_version_result = sf.ContentVersion.create(content_version)
137
- content_version_id = content_version_result['id']
138
- file_url = f"https://sathkruthatechsolutionspri8-dev-ed.develop.lightning.force.com/{content_version_id}"
139
- file_urls.append(file_url)
140
- except Exception as e:
141
- logger.error(f"Failed to upload image to Salesforce: {str(e)}")
142
- return f"Error: Failed to upload image - {str(e)}", "Failure", "", "", 0
143
-
144
- milestone = detect_milestone_from_image(saved_path)
145
- milestones.append(milestone)
 
 
 
146
 
147
  # Determine final milestone
148
- final_milestone = confirmed_milestone or max(set(milestones), key=milestones.count)
149
- if final_milestone not in VALID_MILESTONES:
150
- return "Error: Invalid milestone detected.", "Failure", "", "", 0, []
151
-
152
- # Calculate completion percentage
153
- percent_complete = calculate_completion_percentage(final_milestone, len(images))
154
 
155
- # Prepare Salesforce record
156
- now = datetime.now(local_timezone)
157
- local_time = now.strftime("%Y-%m-%dT%H:%M:%S") + now.strftime("%z")[:-2] + ":" + now.strftime("%z")[-2:]
158
 
 
159
  record = {
160
- "Name__c": project_name,
161
  "Current_Milestone__c": final_milestone,
162
  "Completion_Percentage__c": percent_complete,
163
  "Last_Updated_On__c": local_time,
@@ -168,49 +141,57 @@ def process_image(images, project_name, confirmed_milestone=None):
168
 
169
  try:
170
  sf.Construction__c.create(record)
171
- logger.info(f"Created Salesforce record for project: {project_name}")
172
  except Exception as e:
173
  logger.error(f"Failed to create Salesforce record: {str(e)}")
174
- return f"Error: Failed to update Salesforce - {str(e)}", "Failure", "", "", 0, []
175
 
176
  return (
177
  f"<h3 style='color:green;'>Milestone: {final_milestone}</h3>",
178
  "Success",
179
  final_milestone,
180
  f"{percent_complete}%",
181
- percent_complete,
182
- file_urls
183
  )
184
 
185
  except Exception as e:
186
- logger.error(f"Unexpected error: {str(e)}")
187
- return f"Error: {str(e)}", "Failure", "", "", 0, []
188
 
189
  # Gradio UI
190
- with gr.Blocks(css=".title {text-align: center; font-size: 24px;}") as demo:
191
  gr.Markdown("<h1 class='title'>Construction Progress Analyzer</h1>")
192
  with gr.Row():
193
  with gr.Column(scale=2):
194
- image_input = gr.Files(type="filepath", label="Upload Construction Site Photos (JPG/PNG, ≤ 20MB)")
195
- project_name_input = gr.Textbox(label="Project Name (Required)", placeholder="e.g. Project_12345")
 
 
 
196
  with gr.Column(scale=1):
197
- milestone_dropdown = gr.Dropdown(
198
- choices=VALID_MILESTONES,
199
- label="Confirm Milestone (Optional)",
200
- info="Select to override detected milestone"
201
  )
202
 
203
- submit_button = gr.Button("Process Image")
204
  output_html = gr.HTML(label="Result")
205
- upload_status = gr.Textbox(label="Upload Status", interactive=False)
206
- milestone_output = gr.Textbox(label="Detected Milestone", interactive=False)
207
- progress = gr.Textbox(label="Completion Percentage", interactive=False)
 
 
208
 
209
  submit_button.click(
210
  fn=process_image,
211
- inputs=[image_input, project_name_input, milestone_dropdown],
212
- outputs=[output_html, upload_status, milestone_output, progress]
213
  )
214
 
215
- if __name__ == "__main__":
216
- demo.launch(share=True)
 
 
 
 
 
9
  import base64
10
  import pytz
11
  import logging
12
+ import tempfile
13
 
14
+ # Set up logging
15
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
16
  logger = logging.getLogger(__name__)
17
 
18
  # Load environment variables
 
22
  SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
23
 
24
  if not all([SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN]):
25
+ logger.error("Missing Salesforce credentials.")
26
+ raise ValueError("Missing Salesforce credentials in .env file.")
27
 
28
  # Connect to Salesforce
29
  try:
 
31
  username=SF_USERNAME,
32
  password=SF_PASSWORD,
33
  security_token=SF_SECURITY_TOKEN,
34
+ domain='login'
35
  )
36
+ logger.info("Successfully connected to Salesforce.")
37
  except Exception as e:
38
  logger.error(f"Salesforce connection failed: {str(e)}")
39
  raise
40
 
41
  VALID_MILESTONES = ["Planning", "Foundation", "Walls Erected", "Completed"]
42
+ MILESTONE_COMPLETION_MAP = {
43
+ "Planning": 25,
44
+ "Foundation": 50,
45
+ "Walls Erected": 75,
46
+ "Completed": 100
47
+ }
48
+ LOCAL_TIMEZONE = pytz.timezone("Asia/Kolkata")
49
+ ALLOWED_EXTENSIONS = {'.jpg', '.jpeg', '.png'}
50
+ MAX_FILE_SIZE_MB = 20
51
+
52
+ # Milestone detection based on image brightness
53
  def detect_milestone_from_image(image_path):
 
 
 
 
54
  try:
55
+ img = Image.open(image_path).convert("L")
56
  img_array = np.array(img)
57
+ brightness = np.mean(img_array)
58
+ logger.debug(f"Image {image_path} brightness: {brightness}")
 
 
59
 
60
+ if brightness < 80:
 
61
  return "Planning"
62
+ elif 80 <= brightness < 130:
63
  return "Foundation"
64
+ elif 130 <= brightness < 180:
65
  return "Walls Erected"
66
+ else:
67
  return "Completed"
68
  except Exception as e:
69
+ logger.error(f"Failed to detect milestone for {image_path}: {str(e)}")
70
+ return "Planning" # Default milestone on error
71
 
72
+ def process_image(images, project_name):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  try:
74
+ if not project_name or not project_name.strip():
75
+ return "Error: Project name is required.", "Failure", "", "", "0%"
76
 
77
+ if not images or len(images) == 0:
78
+ return "Error: Please upload at least one image.", "Failure", "", "", "0%"
 
 
 
 
 
79
 
80
  milestones = []
81
  file_urls = []
82
+ with tempfile.TemporaryDirectory() as temp_dir:
83
+ for image in images:
84
+ # Validate file extension
85
+ ext = os.path.splitext(image.name)[1].lower()
86
+ if ext not in ALLOWED_EXTENSIONS:
87
+ return f"Error: {os.path.basename(image.name)} is not a supported format (JPG/PNG only).", "Failure", "", "", "0%"
88
+
89
+ # Validate file size
90
+ image_size_mb = os.path.getsize(image.name) / (1024 * 1024)
91
+ if image_size_mb > MAX_FILE_SIZE_MB:
92
+ return f"Error: {os.path.basename(image.name)} exceeds {MAX_FILE_SIZE_MB}MB.", "Failure", "", "", "0%"
93
+
94
+ # Save image to temporary directory
95
+ unique_id = datetime.now().strftime("%Y%m%d%H%M%S")
96
+ image_filename = f"{unique_id}_{os.path.basename(image.name)}"
97
+ temp_image_path = os.path.join(temp_dir, image_filename)
98
+ shutil.copy(image.name, temp_image_path)
99
+
100
+ # Upload to Salesforce
101
+ with open(temp_image_path, 'rb') as image_file:
102
+ image_data = base64.b64encode(image_file.read()).decode('utf-8')
103
+
104
  content_version = {
105
  'Title': image_filename,
106
  'PathOnClient': image_filename,
107
  'VersionData': image_data
108
  }
109
+ try:
110
+ content_version_result = sf.ContentVersion.create(content_version)
111
+ content_version_id = content_version_result['id']
112
+ file_url = f"https://sathkruthatechsolutionspri8-dev-ed.develop.lightning.force.com/{content_version_id}"
113
+ file_urls.append(file_url)
114
+ logger.info(f"Uploaded {image_filename} to Salesforce: {file_url}")
115
+ except Exception as e:
116
+ logger.error(f"Failed to upload {image_filename} to Salesforce: {str(e)}")
117
+ return f"Error: Failed to upload {image_filename} to Salesforce.", "Failure", "", "", "0%"
118
+
119
+ # Detect milestone
120
+ milestone = detect_milestone_from_image(temp_image_path)
121
+ milestones.append(milestone)
122
 
123
  # Determine final milestone
124
+ final_milestone = max(set(milestones), key=milestones.count) if milestones else "Planning"
125
+ percent_complete = MILESTONE_COMPLETION_MAP.get(final_milestone, 0)
 
 
 
 
126
 
127
+ # Get current time in local timezone
128
+ now = datetime.now(LOCAL_TIMEZONE)
129
+ local_time = now.strftime("%Y-%m-%dT%H:%M:%S%z")[:-2] + ":" + now.strftime("%z")[-2:]
130
 
131
+ # Create Salesforce record
132
  record = {
133
+ "Name__c": project_name.strip(),
134
  "Current_Milestone__c": final_milestone,
135
  "Completion_Percentage__c": percent_complete,
136
  "Last_Updated_On__c": local_time,
 
141
 
142
  try:
143
  sf.Construction__c.create(record)
144
+ logger.info(f"Created Salesforce record for project {project_name}: {final_milestone}")
145
  except Exception as e:
146
  logger.error(f"Failed to create Salesforce record: {str(e)}")
147
+ return f"Error: Failed to update Salesforce record.", "Failure", "", "", "0%"
148
 
149
  return (
150
  f"<h3 style='color:green;'>Milestone: {final_milestone}</h3>",
151
  "Success",
152
  final_milestone,
153
  f"{percent_complete}%",
154
+ file_urls[-1] if file_urls else ""
 
155
  )
156
 
157
  except Exception as e:
158
+ logger.error(f"Unexpected error in process_image: {str(e)}")
159
+ return f"Error: An unexpected error occurred - {str(e)}", "Failure", "", "", "0%"
160
 
161
  # Gradio UI
162
+ with gr.Blocks(theme=gr.themes.Soft(), css=".title {text-align: center; font-size: 2em; margin-bottom: 20px;}") as demo:
163
  gr.Markdown("<h1 class='title'>Construction Progress Analyzer</h1>")
164
  with gr.Row():
165
  with gr.Column(scale=2):
166
+ image_input = gr.Files(
167
+ file_types=[".jpg", ".jpeg", ".png"],
168
+ label=f"Upload Construction Site Photos (JPG/PNG, ≤ {MAX_FILE_SIZE_MB}MB)",
169
+ file_count="multiple"
170
+ )
171
  with gr.Column(scale=1):
172
+ project_name_input = gr.Textbox(
173
+ label="Project Name (Required)",
174
+ placeholder="e.g., Highrise_001",
175
+ lines=1
176
  )
177
 
178
+ submit_button = gr.Button("Process Images", variant="primary")
179
  output_html = gr.HTML(label="Result")
180
+ with gr.Row():
181
+ upload_status = gr.Textbox(label="Upload Status", interactive=False)
182
+ milestone = gr.Textbox(label="Detected Milestone", interactive=False)
183
+ progress = gr.Textbox(label="Completion Percentage", interactive=False)
184
+ file_url = gr.Textbox(label="Last Image URL", interactive=False, visible=False)
185
 
186
  submit_button.click(
187
  fn=process_image,
188
+ inputs=[image_input, project_name_input],
189
+ outputs=[output_html, upload_status, milestone, progress, file_url]
190
  )
191
 
192
+ # Launch Gradio app
193
+ try:
194
+ demo.launch(share=False, server_name="0.0.0.0", server_port=7860)
195
+ except Exception as e:
196
+ logger.error(f"Failed to launch Gradio app: {str(e)}")
197
+ raise