File size: 7,296 Bytes
c2fa27a
 
58f1841
 
 
 
a5fe846
 
 
 
ee60524
58f1841
 
 
 
 
 
 
 
ee60524
58f1841
ee60524
58f1841
a5fe846
58f1841
 
 
 
 
 
 
 
 
 
 
 
 
 
f3b82b8
a5fe846
 
 
 
 
 
 
 
 
 
ee60524
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a5fe846
 
 
 
 
 
 
 
 
 
 
 
ee60524
a5fe846
ee60524
a5fe846
 
 
 
 
 
 
 
ee60524
 
a5fe846
 
 
ee60524
 
a5fe846
 
 
 
 
ee60524
58f1841
c2fa27a
58f1841
c2fa27a
ee60524
58f1841
ee60524
 
 
 
 
 
58f1841
 
 
 
ee60524
58f1841
ee60524
 
 
 
58f1841
 
 
 
 
ee60524
58f1841
 
ee60524
58f1841
 
 
ee60524
58f1841
 
 
 
 
 
 
ee60524
c2fa27a
 
ee60524
58f1841
c2fa27a
ee60524
 
c2fa27a
 
 
ee60524
c2fa27a
ee60524
58f1841
 
ee60524
c2fa27a
 
 
58f1841
c2fa27a
ee60524
 
c2fa27a
 
 
58f1841
ee60524
c2fa27a
 
ee60524
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import gradio as gr
from PIL import Image
import os
from dotenv import load_dotenv
from simple_salesforce import Salesforce
from datetime import datetime
from fastapi import FastAPI, HTTPException, Security, Depends
from fastapi.security import APIKeyHeader
import base64
import io
import random  # For mock predictions

# Load environment variables
load_dotenv()
SF_USERNAME = os.getenv("SF_USERNAME")
SF_PASSWORD = os.getenv("SF_PASSWORD")
SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
SF_CONSUMER_KEY = os.getenv("SF_CONSUMER_KEY")
SF_CONSUMER_SECRET = os.getenv("SF_CONSUMER_SECRET")
API_KEY = os.getenv("API_KEY", "your-api-key-here")

# Validate Salesforce credentials
if not all([SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN, SF_CONSUMER_KEY, SF_CONSUMER_SECRET]):
    raise ValueError("Missing Salesforce credentials. Set SF_USERNAME, SF_PASSWORD, SF_SECURITY_TOKEN, SF_CONSUMER_KEY, and SF_CONSUMER_SECRET in environment variables.")

# Initialize Salesforce connection
try:
    sf = Salesforce(
        username=SF_USERNAME,
        password=SF_PASSWORD,
        security_token=SF_SECURITY_TOKEN,
        consumer_key=SF_CONSUMER_KEY,
        consumer_secret=SF_CONSUMER_SECRET,
        domain='login'  # Use 'test' for sandbox
    )
except Exception as e:
    print(f"Salesforce connection failed: {str(e)}")
    raise

# FastAPI app for API endpoint
app = FastAPI()

# API Key authentication
api_key_header = APIKeyHeader(name="X-API-Key")
async def verify_api_key(api_key: str = Security(api_key_header)):
    if api_key != API_KEY:
        raise HTTPException(status_code=401, detail="Invalid API Key")
    return api_key

# Mock AI model for milestone detection (since we can't train a real model here)
def mock_ai_model(image):
    # Preprocessing: Resize, normalize (simulated)
    img = image.convert("RGB")
    max_size = 1024
    img.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)

    # Feature Extraction and Milestone Detection (simulated)
    # In a real scenario, this would use a CNN model trained on construction images
    milestones = [
        "Foundation Completed",
        "Structural Framework Started",
        "Walls In Progress",
        "Roofing Started",
        "Interior Work Started",
        "Project Completed"
    ]
    
    # For this image, based on the concrete pillars and rebar, we assume "Structural Framework Started"
    milestone = "Structural Framework Started"
    completion_percent = 30  # Estimated based on the image
    confidence_score = round(random.uniform(0.85, 0.95), 2)  # Random confidence between 85-95%

    return milestone, completion_percent, confidence_score

@app.post("/predict-milestone")
async def predict_milestone(payload: dict, api_key: str = Depends(verify_api_key)):
    try:
        # Validate payload
        if "image" not in payload:
            raise HTTPException(status_code=400, detail="Image field is required")
        
        # Decode base64 image
        image_data = payload["image"]
        if image_data.startswith("data:image"):
            image_data = image_data.split(",")[1]  # Remove data URI prefix
        img_bytes = base64.b64decode(image_data)
        img = Image.open(io.BytesIO(img_bytes))

        # Validate image size (max 20MB)
        img_bytes_size = len(img_bytes) / (1024 * 1024)
        if img_bytes_size > 20:
            raise HTTPException(status_code=400, detail="Image size exceeds 20MB")

        # Validate image type
        if not img.format.lower() in ["jpeg", "png"]:
            raise HTTPException(status_code=400, detail="Only JPG/PNG images are supported")

        # Run mock AI model
        milestone, percent_complete, confidence_score = mock_ai_model(img)

        return {
            "milestone": milestone,
            "percent_complete": percent_complete,
            "confidence_score": confidence_score
        }

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error processing image: {str(e)}")

# Function for Gradio UI to process the image
def process_image(image, project_name):
    try:
        # Validate inputs
        if image is None:
            return "Error: Please upload an image to proceed.", "Pending", "", "", 0
        if not project_name:
            return "Error: Please enter a project name to proceed.", "Pending", "", "", 0
        if not project_name.isalnum():
            return "Error: Project name must be alphanumeric (letters and numbers only).", "Pending", "", "", 0

        # Open and validate image
        img = Image.open(image)

        # Validate image size and type
        image_size_mb = os.path.getsize(image) / (1024 * 1024)
        if image_size_mb > 20:
            return "Error: Image size exceeds 20MB.", "Failure", "", "", 0
        if not image.lower().endswith(('.jpg', '.jpeg', '.png')):
            return "Error: Only JPG/PNG images are supported.", "Failure", "", "", 0

        # Run mock AI model
        milestone, percent_complete, confidence_score = mock_ai_model(img)

        # Update Salesforce record
        record = {
            "Name": project_name,
            "Current_Milestone__c": milestone,
            "Completion_Percentage__c": percent_complete,
            "Last_Updated_On__c": datetime.now().isoformat(),
            "Upload_Status__c": "Success",
            "Comments__c": f"AI Prediction: {milestone} with {confidence_score*100}% confidence"
        }

        try:
            query = f"SELECT Id FROM Construction_Project__c WHERE Name = '{project_name}'"
            result = sf.query(query)
            if result["totalSize"] > 0:
                project_id = result["records"][0]["Id"]
                sf.Construction_Project__c.update(project_id, record)
            else:
                sf.Construction_Project__c.create(record)
        except Exception as e:
            return f"Error: Failed to update Salesforce - {str(e)}", "Failure", "", "", 0

        return (
            f"Success: Milestone: {milestone}, Completion: {percent_complete}%",
            "Success",
            milestone,
            f"Confidence Score: {confidence_score}",
            percent_complete
        )

    except Exception as e:
        return f"Error: {str(e)}", "Failure", "", "", 0

# Gradio interface for testing
with gr.Blocks(css=".gradio-container {background-color: #f0f4f8; font-family: Arial;} .title {color: #2c3e50; font-size: 24px; text-align: center;}") as demo:
    gr.Markdown("<h1 class='title'>Construction Milestone Detector</h1>")
    project_name = gr.Textbox(label="Project Name", placeholder="Enter project name (e.g., MyHouse)")
    image_input = gr.Image(type="filepath", label="Upload Construction Site Photo (JPG/PNG, ≤ 20MB)")
    submit_button = gr.Button("Process Image")
    output_text = gr.Textbox(label="Result")
    upload_status = gr.Textbox(label="Upload Status")
    milestone = gr.Textbox(label="Detected Milestone")
    confidence = gr.Textbox(label="Confidence Score")
    progress = gr.Slider(0, 100, label="Completion Percentage", interactive=False, value=0)

    submit_button.click(
        fn=process_image,
        inputs=[image_input, project_name],
        outputs=[output_text, upload_status, milestone, confidence, progress]
    )

# Launch the Gradio app
demo.launch()