neerajkalyank commited on
Commit
4b5d09a
·
verified ·
1 Parent(s): 2497c55

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -263
app.py CHANGED
@@ -8,8 +8,6 @@ import hashlib
8
  import shutil
9
  import base64
10
  import pytz
11
- import exifread
12
- import numpy as np
13
 
14
  # Load environment variables
15
  load_dotenv()
@@ -34,290 +32,164 @@ except Exception as e:
34
  raise
35
 
36
  # Valid milestones
37
- VALID_MILESTONES = ["Planning", "Foundation", "Walls Erected", "Completed"]
38
 
39
- # Adjust the timezone
40
  local_timezone = pytz.timezone("Asia/Kolkata")
41
 
42
- # Mock AI model for milestone detection
43
- def detect_milestone(image_path):
44
- """
45
- Detect construction milestone using filename, EXIF metadata, and image histogram.
46
- Returns (milestone, confidence) tuple.
47
- """
48
- try:
49
- # Initialize confidence score
50
- confidence = 0.5 # Base confidence
51
-
52
- # Extract EXIF metadata
53
- with open(image_path, 'rb') as img_file:
54
- tags = exifread.process_file(img_file, details=False)
55
- image_date = tags.get('EXIF DateTimeOriginal', None)
56
- image_size = os.path.getsize(image_path)
57
-
58
- # Analyze filename keywords
59
- filename = os.path.basename(image_path).lower()
60
- keywords = {
61
- "plan": ("Planning", 0.9),
62
- "blueprint": ("Planning", 0.9),
63
- "siteprep": ("Planning", 0.8),
64
- "foundation": ("Foundation", 0.9),
65
- "concrete": ("Foundation", 0.9),
66
- "slab": ("Foundation", 0.8),
67
- "wall": ("Walls Erected", 0.9),
68
- "structure": ("Walls Erected", 0.8),
69
- "beam": ("Walls Erected", 0.8),
70
- "interior": ("Completed", 0.9),
71
- "electrical": ("Completed", 0.9),
72
- "plumbing": ("Completed", 0.9),
73
- "finish": ("Completed", 0.9),
74
- "completed": ("Completed", 0.95)
75
- }
76
 
77
- detected_milestone = "Planning"
78
- for keyword, (milestone, keyword_conf) in keywords.items():
79
- if keyword in filename:
80
- detected_milestone = milestone
81
- confidence = max(confidence, keyword_conf)
82
- break
83
-
84
- # Adjust based on image histogram (simulate scene detection)
85
- img = Image.open(image_path).convert("RGB")
86
- histogram = np.array(img.histogram())
87
- # Normalize histogram
88
- histogram = histogram / histogram.sum()
89
- # Heuristic: High gray (concrete) suggests Foundation, high variety suggests Completed
90
- gray_intensity = histogram[128:192].sum() # Middle range for gray
91
- color_variety = np.std(histogram) # Standard deviation for color diversity
92
-
93
- if gray_intensity > 0.3 and detected_milestone in ["Planning", "Foundation"]:
94
- detected_milestone = "Foundation"
95
- confidence = max(confidence, 0.7)
96
- elif color_variety > 0.05 and detected_milestone in ["Planning", "Foundation", "Walls Erected"]:
97
- detected_milestone = "Completed"
98
- confidence = max(confidence, 0.8)
99
-
100
- # Adjust based on timestamp (newer images indicate later milestones)
101
- if image_date:
102
- try:
103
- img_datetime = datetime.strptime(str(image_date), "%Y:%m:%d %H:%M:%S")
104
- days_old = (datetime.now() - img_datetime).days
105
- if days_old < 30 and detected_milestone in ["Planning", "Foundation"]:
106
- detected_milestone = "Walls Erected"
107
- confidence = max(confidence, 0.75)
108
- elif days_old < 15 and detected_milestone != "Completed":
109
- detected_milestone = "Completed"
110
- confidence = max(confidence, 0.85)
111
- except ValueError:
112
- pass
113
-
114
- # Adjust confidence based on image size (larger images often from later stages)
115
- if image_size > 5 * 1024 * 1024:
116
- if detected_milestone != "Completed":
117
- detected_milestone = "Completed"
118
- confidence = max(confidence, 0.8)
119
- elif image_size > 2 * 1024 * 1024 and detected_milestone == "Planning":
120
- detected_milestone = "Foundation"
121
- confidence = max(confidence, 0.7)
122
-
123
- return detected_milestone, confidence
124
 
125
- except Exception as e:
126
- print(f"Error in milestone detection: {str(e)}")
127
- return "Planning", 0.5
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
  # Image processing and Salesforce upload
130
- def process_image(images, project_name):
131
  try:
132
- if not images or len(images) == 0:
133
- return "Error: Please upload at least one image.", "Pending", "", "", 0
134
- if not project_name:
135
- return "Error: Project name is required.", "Failure", "", "", 0
136
-
137
- # Process each image
138
- milestone_scores = {m: 0.0 for m in VALID_MILESTONES}
139
- file_urls = []
140
- for image in images:
141
- # Validate image
142
- img = Image.open(image)
143
- image_size_mb = os.path.getsize(image) / (1024 * 1024)
144
- if image_size_mb > 20:
145
- return "Error: Image exceeds 20MB.", "Failure", "", "", 0
146
- if not str(image).lower().endswith(('.jpg', '.jpeg', '.png')):
147
- return "Error: Only JPG/PNG images supported.", "Failure", "", "", 0
148
-
149
- # Save image temporarily
150
- upload_dir = "public_uploads"
151
- os.makedirs(upload_dir, exist_ok=True)
152
- unique_id = datetime.now().strftime("%Y%m%d%H%M%S")
153
- image_filename = f"{unique_id}_{os.path.basename(image)}"
154
- saved_image_path = os.path.join(upload_dir, image_filename)
155
- shutil.copy(image, saved_image_path)
156
-
157
- # Convert to base64
158
- with open(saved_image_path, 'rb') as image_file:
159
- image_data = base64.b64encode(image_file.read()).decode('utf-8')
160
-
161
- # Upload to Salesforce
162
- try:
163
- content_version = {
164
- 'Title': image_filename,
165
- 'PathOnClient': saved_image_path,
166
- 'VersionData': image_data
167
- }
168
- content_version_result = sf.ContentVersion.create(content_version)
169
- content_version_id = content_version_result['id']
170
- file_url = f"https://sathkruthatechsolutionspri8-dev-ed.develop.lightning.force.com/{content_version_id}"
171
- file_urls.append(file_url)
172
- except Exception as e:
173
- return f"Error: Failed to upload image to Salesforce - {str(e)}", "Failure", "", "", 0
174
-
175
- # Detect milestone
176
- milestone, confidence = detect_milestone(image)
177
- milestone_scores[milestone] += confidence
178
-
179
- # Determine final milestone (highest weighted score)
180
- final_milestone = max(milestone_scores, key=milestone_scores.get)
181
- milestone_completion_map = {
182
- "Planning": 10,
183
- "Foundation": 30,
184
- "Walls Erected": 50,
185
- "Completed": 100
186
  }
187
- percent_complete = milestone_completion_map.get(final_milestone, 0)
188
 
189
- # Completion details
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  completion_details = {
191
  "Planning": {
192
  "completed": [
193
- "Initial project outline and objectives established.",
194
- "Preliminary designs and architectural plans drafted.",
195
- "Stakeholder meetings and initial approvals completed."
196
  ],
197
  "not_completed": [
198
- "Detailed construction plans pending finalization.",
199
- "Permits and regulatory approvals not yet obtained.",
200
- "Contractor selection and procurement not complete."
201
  ]
202
  },
203
  "Foundation": {
204
  "completed": [
205
- "Site preparation, including clearing and leveling, finished.",
206
- "Excavation for foundation completed.",
207
- "Concrete pouring for foundation done.",
208
- "Initial structural inspections passed."
209
  ],
210
  "not_completed": [
211
- "Plumbing and electrical groundwork pending.",
212
- "Backfilling and site grading not done.",
213
- "Above-ground structural work not started."
214
  ]
215
  },
216
  "Walls Erected": {
217
  "completed": [
218
- "Concrete framework, including columns and beams, in place.",
219
- "All structural walls erected and stabilized.",
220
- "Temporary scaffolding and safety measures installed.",
221
- "Initial inspections for structural integrity completed."
222
  ],
223
  "not_completed": [
224
- "Roofing installation and weatherproofing pending.",
225
- "Windows, doors, and exterior cladding not installed.",
226
- "Interior walls, electrical, and plumbing not implemented."
227
  ]
228
  },
229
  "Completed": {
230
  "completed": [
231
- "Concrete framework fully constructed.",
232
- "Exterior walls, windows, and cladding installed.",
233
- "Interior work, including electrical and plumbing, completed.",
234
- "Finishing touches, such as flooring and painting, done.",
235
- "All phases finished, including final inspections."
236
  ],
237
  "not_completed": [
238
- "No pending work as project is fully completed."
239
  ]
240
  }
241
  }
242
 
243
- completed_tasks = completion_details[final_milestone]["completed"]
244
- not_completed_tasks = completion_details[final_milestone]["not_completed"]
245
-
246
- completed_html = "".join([f'<li style="color: green;">✔ {task}</li>' for task in completed_tasks])
247
- not_completed_html = "".join([f'<li style="color: red;">✘ {task}</li>' for task in not_completed_tasks])
248
-
249
- result_html = f"""
250
- <div style="font-family: Arial, sans-serif; padding: 20px; background-color: #f9f9f9; border-radius: 10px;">
251
- <h2 style="color: #2c3e50; text-align: center;">Project Summary</h2>
252
- <div style="display: flex; justify-content: space-around; margin-bottom: 20px;">
253
- <div style="text-align: center;">
254
- <h3 style="color: #34495e;">Detected Milestone</h3>
255
- <p style="font-size: 18px; font-weight: bold;">{final_milestone}</p>
256
- </div>
257
- <div style="text-align: center;">
258
- <h3 style="color: #34495e;">Completion</h3>
259
- <progress value="{percent_complete}" max="100" style="width: 200px; height: 20px;"></progress>
260
- <p>{percent_complete}%</p>
261
- </div>
262
- </div>
263
-
264
- <h3 style="color: #2c3e50;">Milestone Timeline</h3>
265
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
266
- <span style="color: {'#2ecc71' if final_milestone == 'Planning' else '#bdc3c7'};">Planning</span>
267
- <span style="color: {'#2ecc71' if final_milestone == 'Foundation' else '#bdc3c7'};">Foundation</span>
268
- <span style="color: {'#2ecc71' if final_milestone == 'Walls Erected' else '#bdc3c7'};">Walls Erected</span>
269
- <span style="color: {'#2ecc71' if final_milestone == 'Completed' else '#bdc3c7'};">Completed</span>
270
- </div>
271
-
272
- <details style="margin-bottom: 20px;">
273
- <summary style="color: #2c3e50; font-weight: bold;">Completed Tasks</summary>
274
- <ul style="padding-left: 20px;">
275
- {completed_html}
276
- </ul>
277
- </details>
278
-
279
- <details style="margin-bottom: 20px;">
280
- <summary style="color: #2c3e50; font-weight: bold;">Not Completed Tasks</summary>
281
- <ul style="padding-left: 20px;">
282
- {not_completed_html}
283
- </ul>
284
- </details>
285
-
286
- <h3 style="color: #2c3e50;">Uploaded Images</h3>
287
- <ul style="padding-left: 20px;">
288
- {"".join([f'<li><a href="{url}" target="_blank">{url}</a></li>' for url in file_urls])}
289
- </ul>
290
- </div>
291
- """
292
 
293
- now = datetime.now(local_timezone)
294
- local_time = now.strftime("%Y-%m-%dT%H:%M:%S") + now.strftime("%z")[:-2] + ":" + now.strftime("%z")[-2:]
295
 
 
296
  record = {
297
  "Name__c": project_name,
298
- "Current_Milestone__c": final_milestone,
299
  "Completion_Percentage__c": percent_complete,
300
- "Last_Updated_On__c": local_time,
301
  "Upload_Status__c": "Success",
302
- "Comments__c": f"{final_milestone} (based on {len(images)} images)",
303
- "Last_Updated_Image__c": file_urls[-1] if file_urls else ""
304
  }
305
 
 
306
  try:
307
  sf.Construction__c.create(record)
308
  except Exception as e:
309
  return f"Error: Failed to update Salesforce - {str(e)}", "Failure", "", "", 0
310
 
311
- return result_html, "Success", final_milestone, f"{percent_complete}%"
 
 
 
 
 
 
 
 
 
 
 
 
 
312
 
313
  except Exception as e:
314
- return f"Error: {str(e)}", "Failure", "", "", "0%"
315
 
316
- # Gradio UI
317
  with gr.Blocks(css="""
318
  .gradio-container {
319
  background-color: #f0f4f8;
320
- font-family: Arial, sans-serif;
321
  }
322
  .title {
323
  color: #2c3e50;
@@ -337,50 +209,24 @@ with gr.Blocks(css="""
337
  .gradio-container .button {
338
  display: block;
339
  margin: 0 auto;
340
- background-color: #3498db;
341
- color: white;
342
- border: none;
343
- padding: 10px 20px;
344
- border-radius: 5px;
345
- cursor: pointer;
346
- }
347
- .gradio-container .button:hover {
348
- background-color: #2980b9;
349
- }
350
- progress::-webkit-progress-value {
351
- background-color: #2ecc71;
352
- border-radius: 5px;
353
- }
354
- progress::-webkit-progress-bar {
355
- background-color: #ecf0f1;
356
- border-radius: 5px;
357
- }
358
- details summary {
359
- cursor: pointer;
360
- padding: 10px;
361
- background-color: #ecf0f1;
362
- border-radius: 5px;
363
- }
364
- details ul {
365
- margin-top: 10px;
366
  }
367
  """) as demo:
368
- gr.Markdown("<h1 class='title'>Construction Progress Analyzer</h1>")
369
  with gr.Row():
370
- image_input = gr.Files(type="filepath", label="Upload Construction Site Photos (JPG/PNG, ≤ 20MB)")
371
  project_name_input = gr.Textbox(label="Project Name (Required)", placeholder="e.g. Project_12345")
372
 
373
  submit_button = gr.Button("Process Image")
374
- output_html = gr.HTML(label="Result")
375
  upload_status = gr.Textbox(label="Upload Status")
376
  milestone = gr.Textbox(label="Detected Milestone")
377
- progress = gr.Textbox(label="Completion Percentage", interactive=False)
 
378
 
379
  submit_button.click(
380
  fn=process_image,
381
  inputs=[image_input, project_name_input],
382
- outputs=[output_html, upload_status, milestone, progress]
383
  )
384
 
385
- if __name__ == "__main__":
386
- demo.launch()
 
8
  import shutil
9
  import base64
10
  import pytz
 
 
11
 
12
  # Load environment variables
13
  load_dotenv()
 
32
  raise
33
 
34
  # Valid milestones
35
+ VALID_MILESTONES = ["Foundation", "Walls Erected", "Planning", "Completed"]
36
 
37
+ # Adjust the timezone to your local timezone (replace 'Asia/Kolkata' with your timezone if needed)
38
  local_timezone = pytz.timezone("Asia/Kolkata")
39
 
40
+ # Deterministic AI prediction with fixed confidence and percent
41
+ def mock_ai_model(image):
42
+ img = image.convert("RGB")
43
+ max_size = 1024
44
+ img.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ img_bytes = img.tobytes()
47
+ img_hash = int(hashlib.sha256(img_bytes).hexdigest(), 16)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ milestone_index = img_hash % len(VALID_MILESTONES)
50
+ milestone = VALID_MILESTONES[milestone_index]
51
+
52
+ milestone_completion_map = {
53
+ "Planning": 10,
54
+ "Foundation": 30,
55
+ "Walls Erected": 50,
56
+ "Completed": 100,
57
+ }
58
+ completion_percent = milestone_completion_map.get(milestone, 0)
59
+
60
+ confidence_raw = 0.85 + ((img_hash % 1000) / 1000) * (0.95 - 0.85)
61
+ confidence_score = round(confidence_raw, 2)
62
+
63
+ return milestone, completion_percent, confidence_score
64
 
65
  # Image processing and Salesforce upload
66
+ def process_image(image, project_name):
67
  try:
68
+ if image is None:
69
+ return "Error: Please upload an image to proceed.", "Pending", "", "", 0
70
+
71
+ img = Image.open(image)
72
+ image_size_mb = os.path.getsize(image) / (1024 * 1024)
73
+ if image_size_mb > 20:
74
+ return "Error: Image size exceeds 20MB.", "Failure", "", "", 0
75
+ if not str(image).lower().endswith(('.jpg', '.jpeg', '.png')):
76
+ return "Error: Only JPG/PNG images are supported.", "Failure", "", "", 0
77
+
78
+ # Save image to public folder temporarily before uploading to Salesforce
79
+ upload_dir = "public_uploads"
80
+ os.makedirs(upload_dir, exist_ok=True)
81
+ unique_id = datetime.now().strftime("%Y%m%d%H%M%S")
82
+ image_filename = f"{unique_id}_{os.path.basename(image)}"
83
+ saved_image_path = os.path.join(upload_dir, image_filename)
84
+ shutil.copy(image, saved_image_path)
85
+
86
+ # Convert image to base64 before uploading to Salesforce
87
+ with open(saved_image_path, 'rb') as image_file:
88
+ image_data = base64.b64encode(image_file.read()).decode('utf-8')
89
+
90
+ # Create the ContentVersion record in Salesforce
91
+ content_version = {
92
+ 'Title': image_filename,
93
+ 'PathOnClient': saved_image_path,
94
+ 'VersionData': image_data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  }
 
96
 
97
+ # Upload the file to Salesforce
98
+ try:
99
+ content_version_result = sf.ContentVersion.create(content_version)
100
+ content_version_id = content_version_result['id']
101
+
102
+ # Generate the public URL for the uploaded file in Salesforce
103
+ file_url = f"https://sathkruthatechsolutionspri8-dev-ed.develop.lightning.force.com/{content_version_id}"
104
+ except Exception as e:
105
+ return f"Error: Failed to upload image to Salesforce - {str(e)}", "Failure", "", "", 0
106
+
107
+ # AI-based milestone and completion prediction
108
+ milestone, percent_complete, confidence_score = mock_ai_model(img)
109
+
110
+ # Detailed Completion Breakdown (based on detected milestone)
111
  completion_details = {
112
  "Planning": {
113
  "completed": [
114
+ "Project outline and goals set, initial designs done."
 
 
115
  ],
116
  "not_completed": [
117
+ "Detailed plans, permits, and contractor hiring pending."
 
 
118
  ]
119
  },
120
  "Foundation": {
121
  "completed": [
122
+ "Foundation work is complete, concrete is poured."
 
 
 
123
  ],
124
  "not_completed": [
125
+ "Plumbing, electrical groundwork not yet done."
 
 
126
  ]
127
  },
128
  "Walls Erected": {
129
  "completed": [
130
+ "All structural walls are up."
 
 
 
131
  ],
132
  "not_completed": [
133
+ "Roofing, windows, and internal walls are not yet finished."
 
 
134
  ]
135
  },
136
  "Completed": {
137
  "completed": [
138
+ "All phases of the project are finished, including final touches."
 
 
 
 
139
  ],
140
  "not_completed": [
141
+ "There should be no more pending work."
142
  ]
143
  }
144
  }
145
 
146
+ # Get the detailed completion information based on the detected milestone
147
+ completed_work = "\n".join([f"{idx+1}. {task}" for idx, task in enumerate(completion_details[milestone]["completed"])])
148
+ not_completed_work = "\n".join([f"{idx+1}. {task}" for idx, task in enumerate(completion_details[milestone]["not_completed"])])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
+ # Adjust the current time to local timezone
151
+ local_time = datetime.now(local_timezone).strftime("%Y-%m-%dT%H:%M:%S%z") # Corrected ISO 8601 format with timezone
152
 
153
+ # Create the Salesforce record with the image URL and AI prediction
154
  record = {
155
  "Name__c": project_name,
156
+ "Current_Milestone__c": milestone,
157
  "Completion_Percentage__c": percent_complete,
158
+ "Last_Updated_On__c": local_time, # Corrected format for Salesforce
159
  "Upload_Status__c": "Success",
160
+ "Comments__c": f"{milestone} with {confidence_score*100}% confidence", # Removed "AI Prediction:"
161
+ "Last_Updated_Image__c": file_url
162
  }
163
 
164
+ # Upload the record to Salesforce
165
  try:
166
  sf.Construction__c.create(record)
167
  except Exception as e:
168
  return f"Error: Failed to update Salesforce - {str(e)}", "Failure", "", "", 0
169
 
170
+ # Return the detailed result in the desired format
171
+ result = f"""
172
+ Completed:
173
+
174
+ {completed_work}
175
+
176
+ Not Completed:
177
+
178
+ {not_completed_work}
179
+
180
+ Confidence Score: {confidence_score * 100}%
181
+ """
182
+
183
+ return result, "Success", milestone, f"Confidence Score: {confidence_score}", percent_complete
184
 
185
  except Exception as e:
186
+ return f"Error: {str(e)}", "Failure", "", "", 0
187
 
188
+ # Gradio UI with added styling
189
  with gr.Blocks(css="""
190
  .gradio-container {
191
  background-color: #f0f4f8;
192
+ font-family: Arial;
193
  }
194
  .title {
195
  color: #2c3e50;
 
209
  .gradio-container .button {
210
  display: block;
211
  margin: 0 auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
  """) as demo:
214
+ gr.Markdown("<h1 class='title'></h1>")
215
  with gr.Row():
216
+ image_input = gr.Image(type="filepath", label="Upload Construction Site Photo (JPG/PNG, ≤ 20MB)")
217
  project_name_input = gr.Textbox(label="Project Name (Required)", placeholder="e.g. Project_12345")
218
 
219
  submit_button = gr.Button("Process Image")
220
+ output_text = gr.Textbox(label="Result")
221
  upload_status = gr.Textbox(label="Upload Status")
222
  milestone = gr.Textbox(label="Detected Milestone")
223
+ confidence = gr.Textbox(label="Confidence Score")
224
+ progress = gr.Slider(0, 100, label="Completion Percentage", interactive=False, value=0)
225
 
226
  submit_button.click(
227
  fn=process_image,
228
  inputs=[image_input, project_name_input],
229
+ outputs=[output_text, upload_status, milestone, confidence, progress]
230
  )
231
 
232
+ demo.launch(share=True)