Update app.py
Browse files
app.py
CHANGED
@@ -7,9 +7,8 @@ import datetime
|
|
7 |
import logging
|
8 |
import sys
|
9 |
from simple_salesforce import Salesforce
|
10 |
-
from decouple import config
|
11 |
|
12 |
-
# Configure logging
|
13 |
logging.basicConfig(
|
14 |
level=logging.INFO,
|
15 |
format='%(asctime)s - %(levelname)s - %(message)s',
|
@@ -20,14 +19,10 @@ logging.basicConfig(
|
|
20 |
)
|
21 |
logger = logging.getLogger(__name__)
|
22 |
|
23 |
-
#
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
SALESFORCE_SECURITY_TOKEN = config('SALESFORCE_SECURITY_TOKEN')
|
28 |
-
except Exception as e:
|
29 |
-
logger.error(f"Failed to load environment variables: {str(e)}")
|
30 |
-
raise
|
31 |
|
32 |
# Initialize Salesforce connection
|
33 |
try:
|
@@ -42,7 +37,7 @@ except Exception as e:
|
|
42 |
logger.error(f"Failed to connect to Salesforce: {str(e)}")
|
43 |
raise
|
44 |
|
45 |
-
# Initialize PaddleOCR
|
46 |
try:
|
47 |
logger.info("Initializing PaddleOCR...")
|
48 |
ocr = PaddleOCR(use_angle_cls=True, lang='en')
|
@@ -51,126 +46,146 @@ except Exception as e:
|
|
51 |
logger.error(f"Failed to initialize PaddleOCR: {str(e)}")
|
52 |
raise
|
53 |
|
|
|
54 |
def calculate_materials_from_dimensions(wall_area, foundation_area, structure_type):
|
55 |
logger.info(f"Calculating materials for wall_area={wall_area:.2f} m², foundation_area={foundation_area:.2f} m²")
|
56 |
-
|
57 |
"cement": 0,
|
58 |
"bricks": 0,
|
59 |
"steel": 0,
|
60 |
"sand": 0,
|
61 |
-
"gravel": 0
|
62 |
-
|
63 |
-
foundation_materials = {
|
64 |
-
"cement": 0,
|
65 |
-
"bricks": 0,
|
66 |
-
"steel": 0,
|
67 |
-
"sand": 0,
|
68 |
-
"gravel": 0
|
69 |
-
}
|
70 |
-
|
71 |
-
# Material coefficients based on structure type
|
72 |
-
coefficients = {
|
73 |
-
"Residential": {
|
74 |
-
"cement_wall": 10, "cement_foundation": 20,
|
75 |
-
"brick_wall": 500, "brick_foundation": 750,
|
76 |
-
"steel_wall": 2, "steel_foundation": 5,
|
77 |
-
"sand_wall": 0.5, "sand_foundation": 0.8,
|
78 |
-
"gravel_wall": 0.8, "gravel_foundation": 1.2
|
79 |
-
},
|
80 |
-
"Commercial": {
|
81 |
-
"cement_wall": 12, "cement_foundation": 25,
|
82 |
-
"brick_wall": 600, "brick_foundation": 900,
|
83 |
-
"steel_wall": 3, "steel_foundation": 7,
|
84 |
-
"sand_wall": 0.7, "sand_foundation": 1.0,
|
85 |
-
"gravel_wall": 1.0, "gravel_foundation": 1.5
|
86 |
-
},
|
87 |
-
"Industrial": {
|
88 |
-
"cement_wall": 15, "cement_foundation": 30,
|
89 |
-
"brick_wall": 700, "brick_foundation": 1000,
|
90 |
-
"steel_wall": 4, "steel_foundation": 8,
|
91 |
-
"sand_wall": 0.9, "sand_foundation": 1.2,
|
92 |
-
"gravel_wall": 1.2, "gravel_foundation": 1.8
|
93 |
-
}
|
94 |
}
|
95 |
|
96 |
-
|
97 |
-
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
if wall_area > 0:
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
|
106 |
-
# Foundation calculations
|
107 |
if foundation_area > 0:
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
|
114 |
-
logger.info(f"
|
115 |
-
|
116 |
-
return wall_materials, foundation_materials
|
117 |
|
|
|
118 |
def calculate_construction_materials(site_area, slab_thickness, wall_height):
|
119 |
logger.info(f"Calculating construction materials for site_area={site_area:.2f} sq ft, "
|
120 |
f"slab_thickness={slab_thickness:.2f} ft, wall_height={wall_height:.2f} ft")
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
"steel": 0,
|
126 |
-
"bricks": 0
|
127 |
-
}
|
128 |
-
wall_materials = {
|
129 |
-
"cement": 0,
|
130 |
-
"sand": 0,
|
131 |
-
"gravel": 0,
|
132 |
-
"steel": 0,
|
133 |
-
"bricks": 0
|
134 |
-
}
|
135 |
-
|
136 |
-
# Concrete mix ratio (Cement:Sand:Gravel = 1:2:4)
|
137 |
-
cement_ratio, sand_ratio, gravel_ratio = 1, 2, 4
|
138 |
total_ratio = cement_ratio + sand_ratio + gravel_ratio
|
|
|
|
|
139 |
steel_per_cubic_ft = 1.5
|
140 |
-
brick_size = 0.10 # 7.5in × 3.5in ≈ 0.10 sq ft
|
141 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
try:
|
143 |
slab_volume = site_area * slab_thickness
|
|
|
|
|
144 |
except Exception as e:
|
145 |
-
logger.error(f"Error calculating
|
146 |
raise
|
147 |
|
148 |
-
#
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
|
|
|
|
153 |
|
154 |
-
#
|
155 |
-
|
156 |
|
157 |
-
|
158 |
-
|
159 |
-
return slab_materials, wall_materials
|
160 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
def process_blueprint(image, blueprint_title, uploaded_by, structure_type, site_area, slab_thickness, wall_height):
|
162 |
logger.info("Processing blueprint image...")
|
163 |
try:
|
|
|
164 |
open_cv_image = np.array(image)
|
165 |
open_cv_image = cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2BGR)
|
166 |
except Exception as e:
|
167 |
logger.error(f"Error converting image to OpenCV format: {str(e)}")
|
168 |
raise
|
169 |
|
|
|
170 |
gray = cv2.cvtColor(open_cv_image, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
171 |
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
|
|
|
|
|
172 |
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=50, maxLineGap=10)
|
173 |
|
|
|
174 |
total_wall_length_pixels = 0
|
175 |
if lines is not None:
|
176 |
for line in lines:
|
@@ -179,165 +194,143 @@ def process_blueprint(image, blueprint_title, uploaded_by, structure_type, site_
|
|
179 |
total_wall_length_pixels += length
|
180 |
logger.info(f"Total wall length (pixels): {total_wall_length_pixels:.2f}")
|
181 |
|
|
|
182 |
image_height, image_width = open_cv_image.shape[:2]
|
183 |
-
blueprint_width_m = 27 #
|
184 |
-
blueprint_height_m = 9.78
|
185 |
-
|
|
|
|
|
|
|
|
|
186 |
logger.info(f"Pixel-to-meter ratio: {pixel_to_meter:.6f}")
|
187 |
|
|
|
188 |
total_wall_length_m = total_wall_length_pixels * pixel_to_meter
|
189 |
-
|
190 |
-
total_area = blueprint_width_m * blueprint_height_m
|
191 |
-
foundation_area = total_area * 0.1
|
192 |
-
logger.info(f"Wall area: {wall_area:.2f} m², Foundation area: {foundation_area:.2f} m²")
|
193 |
|
194 |
-
#
|
195 |
-
|
196 |
-
|
|
|
197 |
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
}
|
206 |
-
combined_slab_materials = {
|
207 |
-
'cement': slab_materials['cement'] * cement_conversion_factor,
|
208 |
-
'sand': slab_materials['sand'],
|
209 |
-
'gravel': slab_materials['gravel'],
|
210 |
-
'steel': slab_materials['steel'],
|
211 |
-
'bricks': slab_materials['bricks']
|
212 |
-
}
|
213 |
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
#
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
"steel_kg": combined_wall_materials['steel'],
|
228 |
-
"sand_cft": combined_wall_materials['sand'],
|
229 |
-
"gravel_cft": combined_wall_materials['gravel']
|
230 |
-
}
|
231 |
-
},
|
232 |
-
{
|
233 |
-
"element_type": "slab",
|
234 |
-
"dimensions": {"area_sqft": site_area, "thickness_ft": slab_thickness},
|
235 |
-
"material_quantities": {
|
236 |
-
"cement_kg": combined_slab_materials['cement'],
|
237 |
-
"bricks_units": combined_slab_materials['bricks'],
|
238 |
-
"steel_kg": combined_slab_materials['steel'],
|
239 |
-
"sand_cft": combined_slab_materials['sand'],
|
240 |
-
"gravel_cft": combined_slab_materials['gravel']
|
241 |
-
}
|
242 |
-
},
|
243 |
-
{
|
244 |
-
"element_type": "foundation",
|
245 |
-
"dimensions": {"area_m2": foundation_area},
|
246 |
-
"material_quantities": {
|
247 |
-
"cement_kg": foundation_materials['cement'],
|
248 |
-
"bricks_units": foundation_materials['bricks'],
|
249 |
-
"steel_kg": foundation_materials['steel'],
|
250 |
-
"sand_cft": foundation_materials['sand'],
|
251 |
-
"gravel_cft": foundation_materials['gravel']
|
252 |
-
}
|
253 |
-
}
|
254 |
-
],
|
255 |
-
"total_quantities": {
|
256 |
-
"cement_kg": combined_wall_materials['cement'] + combined_slab_materials['cement'] + foundation_materials['cement'],
|
257 |
-
"bricks_units": combined_wall_materials['bricks'] + combined_slab_materials['bricks'] + foundation_materials['bricks'],
|
258 |
-
"steel_kg": combined_wall_materials['steel'] + combined_slab_materials['steel'] + foundation_materials['steel'],
|
259 |
-
"sand_cft": combined_wall_materials['sand'] + combined_slab_materials['sand'] + foundation_materials['sand'],
|
260 |
-
"gravel_cft": combined_wall_materials['gravel'] + combined_slab_materials['gravel'] + foundation_materials['gravel']
|
261 |
-
},
|
262 |
-
"estimation_accuracy": {"score": 0.95, "warnings": []}
|
263 |
}
|
|
|
264 |
|
|
|
265 |
material_summary = f"""
|
266 |
-
<h3>
|
267 |
-
<p><b>Cement:</b> {
|
268 |
-
<p><b>Sand:</b> {
|
269 |
-
<p><b>Gravel:</b> {
|
270 |
-
<p><b>Steel:</b> {
|
271 |
-
<p><b>Bricks:</b> {
|
272 |
-
<
|
273 |
-
<p><b>Cement:</b> {combined_slab_materials['cement']:.2f} kg</p>
|
274 |
-
<p><b>Sand:</b> {combined_slab_materials['sand']:.2f} CFT</p>
|
275 |
-
<p><b>Gravel:</b> {combined_slab_materials['gravel']:.2f} CFT</p>
|
276 |
-
<p><b>Steel:</b> {combined_slab_materials['steel']:.2f} kg</p>
|
277 |
-
<p><b>Bricks:</b> {combined_slab_materials['bricks']:.0f} units</p>
|
278 |
"""
|
279 |
|
|
|
280 |
estimation_pdf_url = "https://example.com/estimation_pdfs/blueprint_123.pdf"
|
|
|
|
|
281 |
upload_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
|
|
|
|
282 |
extracted_text = extract_text_with_paddleocr(image)
|
283 |
|
|
|
284 |
salesforce_record = {
|
285 |
"Blueprint_Title__c": blueprint_title,
|
286 |
-
"Uploaded_By__c": uploaded_by,
|
287 |
"Upload_Date__c": upload_date,
|
288 |
"Structure_Type__c": structure_type,
|
289 |
-
"Cement_Required_KG__c":
|
290 |
-
"Steel_Required_KG__c":
|
291 |
-
"Brick_Count__c":
|
292 |
-
"
|
293 |
-
"
|
294 |
-
"
|
295 |
-
"
|
|
|
|
|
|
|
296 |
}
|
|
|
297 |
|
|
|
298 |
try:
|
299 |
-
response = sf.
|
300 |
-
logger.info(f"Salesforce record created: {response}")
|
301 |
-
salesforce_record['Salesforce_Record_Id'] = response['id']
|
302 |
except Exception as e:
|
303 |
logger.error(f"Failed to create Salesforce record: {str(e)}")
|
304 |
salesforce_record['Salesforce_Error'] = str(e)
|
305 |
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
"wall_materials": combined_wall_materials,
|
310 |
-
"slab_materials": combined_slab_materials
|
311 |
-
}
|
312 |
return result, extracted_text
|
313 |
|
|
|
314 |
def extract_text_with_paddleocr(image):
|
315 |
logger.info("Extracting text with PaddleOCR...")
|
316 |
try:
|
|
|
317 |
open_cv_image = np.array(image)
|
318 |
open_cv_image = cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2BGR)
|
|
|
|
|
319 |
ocr_results = ocr.ocr(open_cv_image, cls=True)
|
320 |
-
|
321 |
-
|
|
|
|
|
|
|
|
|
322 |
return extracted_text
|
323 |
except Exception as e:
|
324 |
logger.error(f"Error during OCR processing: {str(e)}")
|
325 |
return f"OCR failed: {str(e)}"
|
326 |
|
|
|
327 |
def gradio_process_file(image, blueprint_title, uploaded_by, structure_type, site_area, slab_thickness, wall_height):
|
328 |
logger.info("Starting Gradio process...")
|
329 |
-
if
|
330 |
logger.error("No image uploaded.")
|
331 |
return {"error": "No image uploaded"}, None
|
332 |
|
|
|
333 |
try:
|
334 |
-
|
335 |
-
|
336 |
-
|
|
|
|
|
|
|
|
|
|
|
337 |
if site_area <= 0 or slab_thickness <= 0 or wall_height <= 0:
|
338 |
-
raise ValueError("Site area, slab thickness, and wall height must be positive.")
|
339 |
-
if not blueprint_title or not uploaded_by:
|
340 |
-
raise ValueError("Blueprint title and uploaded by fields are required.")
|
341 |
except (ValueError, TypeError) as e:
|
342 |
logger.error(f"Invalid input: {str(e)}")
|
343 |
return {"error": f"Invalid input: {str(e)}"}, None
|
@@ -350,13 +343,18 @@ def gradio_process_file(image, blueprint_title, uploaded_by, structure_type, sit
|
|
350 |
logger.error(f"Gradio processing failed: {str(e)}")
|
351 |
return {"error": str(e)}, None
|
352 |
|
|
|
353 |
interface = gr.Interface(
|
354 |
fn=gradio_process_file,
|
355 |
inputs=[
|
356 |
gr.Image(type="pil", label="Upload Blueprint Image"),
|
357 |
-
gr.Textbox(label="Blueprint Title", placeholder="Enter blueprint title"),
|
358 |
-
gr.Textbox(label="Uploaded By (Salesforce User ID)", placeholder="Enter Salesforce User ID"),
|
359 |
-
gr.Dropdown(
|
|
|
|
|
|
|
|
|
360 |
gr.Number(label="Site Area (sq ft)", value=1000.0, minimum=1.0),
|
361 |
gr.Number(label="Slab Thickness (ft)", value=0.5, minimum=0.1),
|
362 |
gr.Number(label="Wall Height (ft)", value=10.0, minimum=1.0)
|
@@ -366,9 +364,10 @@ interface = gr.Interface(
|
|
366 |
gr.Textbox(label="Extracted Text")
|
367 |
],
|
368 |
title="Blueprint Material Estimator",
|
369 |
-
description="Upload a blueprint to estimate materials and save to Salesforce."
|
370 |
)
|
371 |
|
|
|
372 |
if __name__ == "__main__":
|
373 |
logger.info("Launching Gradio interface...")
|
374 |
try:
|
@@ -376,4 +375,4 @@ if __name__ == "__main__":
|
|
376 |
logger.info("Gradio interface launched successfully.")
|
377 |
except Exception as e:
|
378 |
logger.error(f"Failed to launch Gradio interface: {str(e)}")
|
379 |
-
sys.exit(1)
|
|
|
7 |
import logging
|
8 |
import sys
|
9 |
from simple_salesforce import Salesforce
|
|
|
10 |
|
11 |
+
# Configure logging to capture errors and debug information
|
12 |
logging.basicConfig(
|
13 |
level=logging.INFO,
|
14 |
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
|
19 |
)
|
20 |
logger = logging.getLogger(__name__)
|
21 |
|
22 |
+
# Salesforce credentials
|
23 |
+
SALESFORCE_USERNAME = "[email protected]"
|
24 |
+
SALESFORCE_PASSWORD = "Vijaya@1888"
|
25 |
+
SALESFORCE_SECURITY_TOKEN = "LfxlLZssa4Bp9vTSd0B78Cyu"
|
|
|
|
|
|
|
|
|
26 |
|
27 |
# Initialize Salesforce connection
|
28 |
try:
|
|
|
37 |
logger.error(f"Failed to connect to Salesforce: {str(e)}")
|
38 |
raise
|
39 |
|
40 |
+
# Initialize PaddleOCR with error handling
|
41 |
try:
|
42 |
logger.info("Initializing PaddleOCR...")
|
43 |
ocr = PaddleOCR(use_angle_cls=True, lang='en')
|
|
|
46 |
logger.error(f"Failed to initialize PaddleOCR: {str(e)}")
|
47 |
raise
|
48 |
|
49 |
+
# Function to calculate materials based on blueprint dimensions (wall and foundation)
|
50 |
def calculate_materials_from_dimensions(wall_area, foundation_area, structure_type):
|
51 |
logger.info(f"Calculating materials for wall_area={wall_area:.2f} m², foundation_area={foundation_area:.2f} m²")
|
52 |
+
materials = {
|
53 |
"cement": 0,
|
54 |
"bricks": 0,
|
55 |
"steel": 0,
|
56 |
"sand": 0,
|
57 |
+
"gravel": 0,
|
58 |
+
"tiles": 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
}
|
60 |
|
61 |
+
# Adjust material coefficients based on structure type
|
62 |
+
if structure_type == "Residential":
|
63 |
+
cement_factor_wall = 10 # kg/m²
|
64 |
+
cement_factor_foundation = 20
|
65 |
+
brick_factor_wall = 500
|
66 |
+
brick_factor_foundation = 750
|
67 |
+
steel_factor_wall = 2
|
68 |
+
steel_factor_foundation = 5
|
69 |
+
sand_factor_wall = 0.5
|
70 |
+
sand_factor_foundation = 0.8
|
71 |
+
gravel_factor_wall = 0.8
|
72 |
+
gravel_factor_foundation = 1.2
|
73 |
+
elif structure_type == "Commercial":
|
74 |
+
cement_factor_wall = 12
|
75 |
+
cement_factor_foundation = 25
|
76 |
+
brick_factor_wall = 600
|
77 |
+
brick_factor_foundation = 900
|
78 |
+
steel_factor_wall = 3
|
79 |
+
steel_factor_foundation = 7
|
80 |
+
sand_factor_wall = 0.7
|
81 |
+
sand_factor_foundation = 1.0
|
82 |
+
gravel_factor_wall = 1.0
|
83 |
+
gravel_factor_foundation = 1.5
|
84 |
+
else: # Industrial
|
85 |
+
cement_factor_wall = 15
|
86 |
+
cement_factor_foundation = 30
|
87 |
+
brick_factor_wall = 700
|
88 |
+
brick_factor_foundation = 1000
|
89 |
+
steel_factor_wall = 4
|
90 |
+
steel_factor_foundation = 8
|
91 |
+
sand_factor_wall = 0.9
|
92 |
+
sand_factor_foundation = 1.2
|
93 |
+
gravel_factor_wall = 1.2
|
94 |
+
gravel_factor_foundation = 1.8
|
95 |
+
|
96 |
+
# Wall calculations (in m²)
|
97 |
if wall_area > 0:
|
98 |
+
materials['cement'] += wall_area * cement_factor_wall
|
99 |
+
materials['bricks'] += wall_area * brick_factor_wall
|
100 |
+
materials['steel'] += wall_area * steel_factor_wall
|
101 |
+
materials['sand'] += wall_area * sand_factor_wall
|
102 |
+
materials['gravel'] += wall_area * gravel_factor_wall
|
103 |
|
104 |
+
# Foundation calculations (in m²)
|
105 |
if foundation_area > 0:
|
106 |
+
materials['cement'] += foundation_area * cement_factor_foundation
|
107 |
+
materials['bricks'] += foundation_area * brick_factor_foundation
|
108 |
+
materials['steel'] += foundation_area * steel_factor_foundation
|
109 |
+
materials['sand'] += foundation_area * sand_factor_foundation
|
110 |
+
materials['gravel'] += foundation_area * gravel_factor_foundation
|
111 |
|
112 |
+
logger.info(f"Blueprint materials calculated: {materials}")
|
113 |
+
return materials
|
|
|
114 |
|
115 |
+
# Function to calculate materials based on site area, slab thickness, and wall height
|
116 |
def calculate_construction_materials(site_area, slab_thickness, wall_height):
|
117 |
logger.info(f"Calculating construction materials for site_area={site_area:.2f} sq ft, "
|
118 |
f"slab_thickness={slab_thickness:.2f} ft, wall_height={wall_height:.2f} ft")
|
119 |
+
# Concrete Mix Ratio (Cement: Sand: Gravel)
|
120 |
+
cement_ratio = 1
|
121 |
+
sand_ratio = 2
|
122 |
+
gravel_ratio = 4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
total_ratio = cement_ratio + sand_ratio + gravel_ratio
|
124 |
+
|
125 |
+
# Steel estimation (kg per cubic foot of concrete)
|
126 |
steel_per_cubic_ft = 1.5
|
|
|
127 |
|
128 |
+
# Brick size (in square feet per brick)
|
129 |
+
brick_size = 0.10 # 7.5 inches × 3.5 inches (approx 0.10 sq ft per brick)
|
130 |
+
|
131 |
+
# Tile size (in square feet per tile)
|
132 |
+
tile_size = 1 # 1x1 foot tiles
|
133 |
+
|
134 |
+
# Concrete Calculation
|
135 |
try:
|
136 |
slab_volume = site_area * slab_thickness
|
137 |
+
foundation_volume = site_area * slab_thickness * 3
|
138 |
+
total_concrete_volume = slab_volume + foundation_volume
|
139 |
except Exception as e:
|
140 |
+
logger.error(f"Error calculating concrete volume: {str(e)}")
|
141 |
raise
|
142 |
|
143 |
+
# Materials for Concrete Mix
|
144 |
+
cement_needed = (cement_ratio / total_ratio) * total_concrete_volume # in cubic feet
|
145 |
+
sand_needed = (sand_ratio / total_ratio) * total_concrete_volume
|
146 |
+
gravel_needed = (gravel_ratio / total_ratio) * total_concrete_volume
|
147 |
+
|
148 |
+
# Steel Bars Calculation
|
149 |
+
steel_needed = steel_per_cubic_ft * total_concrete_volume
|
150 |
|
151 |
+
# Bricks Calculation
|
152 |
+
bricks_needed = (wall_height * site_area) / brick_size
|
153 |
|
154 |
+
# Tiles Calculation
|
155 |
+
tiles_needed = site_area / tile_size
|
|
|
156 |
|
157 |
+
materials = {
|
158 |
+
'cement': cement_needed, # in cubic feet (will convert to kg for Salesforce)
|
159 |
+
'sand': sand_needed,
|
160 |
+
'gravel': gravel_needed,
|
161 |
+
'steel': steel_needed,
|
162 |
+
'bricks': bricks_needed,
|
163 |
+
'tiles': tiles_needed
|
164 |
+
}
|
165 |
+
logger.info(f"Construction materials calculated: {materials}")
|
166 |
+
return materials
|
167 |
+
|
168 |
+
# Function to process the blueprint and extract dimensions
|
169 |
def process_blueprint(image, blueprint_title, uploaded_by, structure_type, site_area, slab_thickness, wall_height):
|
170 |
logger.info("Processing blueprint image...")
|
171 |
try:
|
172 |
+
# Convert PIL image to OpenCV format
|
173 |
open_cv_image = np.array(image)
|
174 |
open_cv_image = cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2BGR)
|
175 |
except Exception as e:
|
176 |
logger.error(f"Error converting image to OpenCV format: {str(e)}")
|
177 |
raise
|
178 |
|
179 |
+
# Convert to grayscale for easier processing
|
180 |
gray = cv2.cvtColor(open_cv_image, cv2.COLOR_BGR2GRAY)
|
181 |
+
|
182 |
+
# Apply edge detection to find lines (walls)
|
183 |
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
|
184 |
+
|
185 |
+
# Use Hough Transform to detect lines (walls)
|
186 |
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=50, maxLineGap=10)
|
187 |
|
188 |
+
# Calculate total wall length (in pixels)
|
189 |
total_wall_length_pixels = 0
|
190 |
if lines is not None:
|
191 |
for line in lines:
|
|
|
194 |
total_wall_length_pixels += length
|
195 |
logger.info(f"Total wall length (pixels): {total_wall_length_pixels:.2f}")
|
196 |
|
197 |
+
# From the blueprint, we know the dimensions (27 m × 9.78 m)
|
198 |
image_height, image_width = open_cv_image.shape[:2]
|
199 |
+
blueprint_width_m = 27 # From the blueprint (27 m)
|
200 |
+
blueprint_height_m = 9.78 # From the blueprint (9.78 m)
|
201 |
+
|
202 |
+
# Calculate pixel-to-meter ratio
|
203 |
+
pixel_to_meter_width = blueprint_width_m / image_width
|
204 |
+
pixel_to_meter_height = blueprint_height_m / image_height
|
205 |
+
pixel_to_meter = (pixel_to_meter_width + pixel_to_meter_height) / 2
|
206 |
logger.info(f"Pixel-to-meter ratio: {pixel_to_meter:.6f}")
|
207 |
|
208 |
+
# Convert wall length to meters
|
209 |
total_wall_length_m = total_wall_length_pixels * pixel_to_meter
|
210 |
+
logger.info(f"Total wall length (meters): {total_wall_length_m:.2f}")
|
|
|
|
|
|
|
211 |
|
212 |
+
# Estimate wall area (assume wall height of 3 m for simplicity)
|
213 |
+
wall_height_m = 3 # Standard room height
|
214 |
+
wall_area = total_wall_length_m * wall_height_m
|
215 |
+
logger.info(f"Wall area: {wall_area:.2f} m²")
|
216 |
|
217 |
+
# Estimate foundation area (based on the blueprint's total area)
|
218 |
+
total_area = blueprint_width_m * blueprint_height_m # 27 m × 9.78 m
|
219 |
+
foundation_area = total_area * 0.1 # Assume 10% of the total area is foundation
|
220 |
+
logger.info(f"Foundation area: {foundation_area:.2f} m²")
|
221 |
+
|
222 |
+
# Calculate materials based on blueprint dimensions
|
223 |
+
blueprint_materials = calculate_materials_from_dimensions(wall_area, foundation_area, structure_type)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
|
225 |
+
# Calculate construction materials based on site area, slab thickness, and wall height
|
226 |
+
construction_materials = calculate_construction_materials(site_area, slab_thickness, wall_height)
|
227 |
+
|
228 |
+
# Combine materials from both calculations
|
229 |
+
# Convert cement from cubic feet to kg for Salesforce (1 cubic foot ≈ 144 kg for cement)
|
230 |
+
cement_conversion_factor = 144 # kg per cubic foot
|
231 |
+
combined_materials = {
|
232 |
+
'cement': (blueprint_materials['cement'] + (construction_materials['cement'] * cement_conversion_factor)), # in kg
|
233 |
+
'sand': blueprint_materials['sand'] + construction_materials['sand'], # in CFT
|
234 |
+
'gravel': blueprint_materials['gravel'] + construction_materials['gravel'], # in CFT
|
235 |
+
'steel': blueprint_materials['steel'] + construction_materials['steel'], # in kg
|
236 |
+
'bricks': blueprint_materials['bricks'] + construction_materials['bricks'], # units
|
237 |
+
'tiles': construction_materials['tiles'] # units
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
238 |
}
|
239 |
+
logger.info(f"Combined materials: {combined_materials}")
|
240 |
|
241 |
+
# Generate Material_Summary__c in HTML format (for display)
|
242 |
material_summary = f"""
|
243 |
+
<h3>Construction Material Requirements:</h3>
|
244 |
+
<p><b>Cement:</b> {combined_materials['cement']:.2f} kg</p>
|
245 |
+
<p><b>Sand:</b> {combined_materials['sand']:.2f} CFT</p>
|
246 |
+
<p><b>Gravel:</b> {combined_materials['gravel']:.2f} CFT</p>
|
247 |
+
<p><b>Steel:</b> {combined_materials['steel']:.2f} kg</p>
|
248 |
+
<p><b>Bricks:</b> {combined_materials['bricks']:.0f} units</p>
|
249 |
+
<p><b>Tiles:</b> {combined_materials['tiles']:.0f} units</p>
|
|
|
|
|
|
|
|
|
|
|
250 |
"""
|
251 |
|
252 |
+
# Simulate Estimation_PDF__c as a placeholder URL
|
253 |
estimation_pdf_url = "https://example.com/estimation_pdfs/blueprint_123.pdf"
|
254 |
+
|
255 |
+
# Current date for Upload_Date__c
|
256 |
upload_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
257 |
+
|
258 |
+
# Extract text from the image using PaddleOCR
|
259 |
extracted_text = extract_text_with_paddleocr(image)
|
260 |
|
261 |
+
# Map fields to Salesforce record for Blue_print_Estimation__c
|
262 |
salesforce_record = {
|
263 |
"Blueprint_Title__c": blueprint_title,
|
264 |
+
"Uploaded_By__c": uploaded_by, # Assumes a valid User ID
|
265 |
"Upload_Date__c": upload_date,
|
266 |
"Structure_Type__c": structure_type,
|
267 |
+
"Cement_Required_KG__c": combined_materials['cement'], # Already in kg
|
268 |
+
"Steel_Required_KG__c": combined_materials['steel'], # Already in kg
|
269 |
+
"Brick_Count__c": combined_materials['bricks'], # Units
|
270 |
+
"Sand__c": combined_materials['sand'], # CFT
|
271 |
+
"Sand_Required_CFT__c": combined_materials['sand'], # CFT (same as Sand__c)
|
272 |
+
"Material_Summary__c": material_summary, # HTML formatted
|
273 |
+
"Estimation_PDF__c": estimation_pdf_url, # URL
|
274 |
+
"Gravel__c": combined_materials['gravel'], # CFT
|
275 |
+
"Tiles__c": combined_materials['tiles'] # Units
|
276 |
+
# Note: User__c is not mapped as it's assumed to be the same as Uploaded_By__c
|
277 |
}
|
278 |
+
logger.info(f"Salesforce record prepared: {salesforce_record}")
|
279 |
|
280 |
+
# Create the record in Salesforce (using Blue_print_Estimation__c)
|
281 |
try:
|
282 |
+
response = sf.Blue_print_Estimation__c.create(salesforce_record)
|
283 |
+
logger.info(f"Salesforce record created successfully: {response}")
|
284 |
+
salesforce_record['Salesforce_Record_Id'] = response['id'] # Add the record ID to the result
|
285 |
except Exception as e:
|
286 |
logger.error(f"Failed to create Salesforce record: {str(e)}")
|
287 |
salesforce_record['Salesforce_Error'] = str(e)
|
288 |
|
289 |
+
# Populate the fields in a structured dictionary for return
|
290 |
+
result = salesforce_record
|
291 |
+
logger.info("Blueprint processing completed successfully.")
|
|
|
|
|
|
|
292 |
return result, extracted_text
|
293 |
|
294 |
+
# Function to extract text using PaddleOCR
|
295 |
def extract_text_with_paddleocr(image):
|
296 |
logger.info("Extracting text with PaddleOCR...")
|
297 |
try:
|
298 |
+
# Convert PIL image to OpenCV format
|
299 |
open_cv_image = np.array(image)
|
300 |
open_cv_image = cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2BGR)
|
301 |
+
|
302 |
+
# Perform OCR using PaddleOCR
|
303 |
ocr_results = ocr.ocr(open_cv_image, cls=True)
|
304 |
+
if ocr_results and ocr_results[0]:
|
305 |
+
extracted_text = '\n'.join([line[1][0] for line in ocr_results[0]])
|
306 |
+
logger.info("Text extracted successfully.")
|
307 |
+
else:
|
308 |
+
extracted_text = "No text detected."
|
309 |
+
logger.info("No text detected in the image.")
|
310 |
return extracted_text
|
311 |
except Exception as e:
|
312 |
logger.error(f"Error during OCR processing: {str(e)}")
|
313 |
return f"OCR failed: {str(e)}"
|
314 |
|
315 |
+
# Wrapper function for Gradio to handle image input and additional fields
|
316 |
def gradio_process_file(image, blueprint_title, uploaded_by, structure_type, site_area, slab_thickness, wall_height):
|
317 |
logger.info("Starting Gradio process...")
|
318 |
+
if image is None:
|
319 |
logger.error("No image uploaded.")
|
320 |
return {"error": "No image uploaded"}, None
|
321 |
|
322 |
+
# Provide default values and validate numerical inputs
|
323 |
try:
|
324 |
+
# Check for None and provide defaults
|
325 |
+
site_area = float(site_area) if site_area is not None else 1000.0
|
326 |
+
slab_thickness = float(slab_thickness) if slab_thickness is not None else 0.5
|
327 |
+
wall_height = float(wall_height) if wall_height is not None else 10.0
|
328 |
+
|
329 |
+
logger.info(f"Input values - site_area: {site_area}, slab_thickness: {slab_thickness}, wall_height: {wall_height}")
|
330 |
+
|
331 |
+
# Validate that values are positive
|
332 |
if site_area <= 0 or slab_thickness <= 0 or wall_height <= 0:
|
333 |
+
raise ValueError("Site area, slab thickness, and wall height must be positive numbers.")
|
|
|
|
|
334 |
except (ValueError, TypeError) as e:
|
335 |
logger.error(f"Invalid input: {str(e)}")
|
336 |
return {"error": f"Invalid input: {str(e)}"}, None
|
|
|
343 |
logger.error(f"Gradio processing failed: {str(e)}")
|
344 |
return {"error": str(e)}, None
|
345 |
|
346 |
+
# Set up a single Gradio interface for image input with additional fields
|
347 |
interface = gr.Interface(
|
348 |
fn=gradio_process_file,
|
349 |
inputs=[
|
350 |
gr.Image(type="pil", label="Upload Blueprint Image"),
|
351 |
+
gr.Textbox(label="Blueprint Title", placeholder="Enter the blueprint title"),
|
352 |
+
gr.Textbox(label="Uploaded By (Salesforce User ID)", placeholder="Enter the Salesforce User ID (e.g., 005xxxxxxxxxxxx)"),
|
353 |
+
gr.Dropdown(
|
354 |
+
choices=["Residential", "Commercial", "Industrial"],
|
355 |
+
label="Structure Type",
|
356 |
+
value="Residential"
|
357 |
+
),
|
358 |
gr.Number(label="Site Area (sq ft)", value=1000.0, minimum=1.0),
|
359 |
gr.Number(label="Slab Thickness (ft)", value=0.5, minimum=0.1),
|
360 |
gr.Number(label="Wall Height (ft)", value=10.0, minimum=1.0)
|
|
|
364 |
gr.Textbox(label="Extracted Text")
|
365 |
],
|
366 |
title="Blueprint Material Estimator",
|
367 |
+
description="Upload a blueprint image to estimate construction materials and save to Salesforce (Blue_print_Estimation__c)."
|
368 |
)
|
369 |
|
370 |
+
# Launch the interface
|
371 |
if __name__ == "__main__":
|
372 |
logger.info("Launching Gradio interface...")
|
373 |
try:
|
|
|
375 |
logger.info("Gradio interface launched successfully.")
|
376 |
except Exception as e:
|
377 |
logger.error(f"Failed to launch Gradio interface: {str(e)}")
|
378 |
+
sys.exit(1)
|