File size: 6,889 Bytes
8a21dba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import HTMLResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from simple_salesforce import Salesforce
from apscheduler.schedulers.asyncio import AsyncIOScheduler
import sqlite3
import os
import logging
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_exception_type
from dotenv import load_dotenv

# Load environment variables from .env file (for local development)
load_dotenv()

# Set up logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

app = FastAPI()

# Mount static files
app.mount("/static", StaticFiles(directory="static"), name="static")

# Set up templates
templates = Jinja2Templates(directory="templates")

# Initialize SQLite database
def init_db():
    try:
        conn = sqlite3.connect("cases.db")
        cursor = conn.cursor()
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS cases (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                mobile_number TEXT,
                email TEXT NOT NULL,
                issue_description TEXT NOT NULL,
                synced INTEGER DEFAULT 0
            )
        """)
        conn.commit()
        conn.close()
        logger.debug("SQLite database initialized successfully.")
    except Exception as e:
        logger.error(f"Error initializing SQLite database: {e}")
        raise

# Run database initialization on startup
init_db()

# Salesforce connection (using environment variables)
def get_salesforce_connection():
    try:
        username = os.getenv("SFDC_USERNAME")
        password = os.getenv("SFDC_PASSWORD")
        security_token = os.getenv("SFDC_SECURITY_TOKEN")
        domain = os.getenv("SFDC_DOMAIN", "login")

        if not username or not password or not security_token:
            logger.error("Missing Salesforce credentials.")
            return None

        sf = Salesforce(
            username=username,
            password=password,
            security_token=security_token,
            domain=domain
        )
        logger.debug("Salesforce connection successful.")
        return sf
    except Exception as e:
        logger.error(f"Error connecting to Salesforce: {e}")
        return None

# Sync function with retry logic
@retry(
    stop=stop_after_attempt(3),
    wait=wait_fixed(2),
    retry=retry_if_exception_type(Exception),
    before_sleep=lambda retry_state: logger.debug(f"Retrying Salesforce sync, attempt {retry_state.attempt_number}")
)
async def sync_to_salesforce():
    try:
        sf = get_salesforce_connection()
        if not sf:
            logger.error("Cannot sync: Salesforce connection failed.")
            return

        conn = sqlite3.connect("cases.db")
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM cases WHERE synced = 0")
        cases = cursor.fetchall()

        for case in cases:
            case_id, name, mobile_number, email, issue_description, _ = case
            case_data = {
                "Name": name,
                "Mobile_Number__c": mobile_number,
                "Email__c": email,
                "Issue_Description__c": issue_description
            }
            sf.Case__c.create(case_data)
            cursor.execute("UPDATE cases SET synced = 1 WHERE id = ?", (case_id,))
            conn.commit()
            logger.debug(f"Synced case ID {case_id} to Salesforce.")
        conn.close()
    except Exception as e:
        logger.error(f"Error syncing to Salesforce: {str(e)}")
        raise

# Set up scheduler
scheduler = AsyncIOScheduler()
scheduler.add_job(sync_to_salesforce, "interval", hours=4)

@app.on_event("startup")
async def startup_event():
    scheduler.start()
    logger.debug("Scheduler started successfully.")

@app.on_event("shutdown")
async def shutdown_event():
    scheduler.shutdown()
    logger.debug("Scheduler shut down successfully.")

@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

@app.get("/static/{filename}")
async def serve_static(filename: str):
    file_path = os.path.join("static", filename)
    if not os.path.exists(file_path):
        raise HTTPException(status_code=404, detail="File not found")
    return FileResponse(file_path)

@app.post("/submit-case")
async def submit_case(request: Request):
    try:
        data = await request.json()
        logger.debug(f"Received form data: {data}")

        # Extract form data with default values and strip whitespace
        name = data.get("name", "").strip()
        mobile_number = data.get("mobileNumber", "").strip()
        email = data.get("email", "").strip()
        issue_description = data.get("issueDescription", "").strip()

        # Validate required fields with specific error messages
        if not name:
            logger.error("Missing 'name' field in form data.")
            raise HTTPException(status_code=400, detail="Name is required")
        if not email:
            logger.error("Missing 'email' field in form data.")
            raise HTTPException(status_code=400, detail="Email is required")
        if not issue_description:
            logger.error("Missing 'issueDescription' field in form data.")
            raise HTTPException(status_code=400, detail="Issue description is required")

        # Basic email validation
        if "@" not in email or "." not in email:
            logger.error("Invalid email format.")
            raise HTTPException(status_code=400, detail="Invalid email format")

        # Store in SQLite
        conn = sqlite3.connect("cases.db")
        cursor = conn.cursor()
        cursor.execute(
            """
            INSERT INTO cases (name, mobile_number, email, issue_description)
            VALUES (?, ?, ?, ?)
            """,
            (name, mobile_number, email, issue_description)
        )
        conn.commit()
        conn.close()
        logger.debug("Data successfully stored in SQLite.")

        # Immediately trigger sync
        await sync_to_salesforce()

        return {"success": True}
    except Exception as e:
        logger.error(f"Error processing request: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/debug-cases")
async def debug_cases():
    try:
        conn = sqlite3.connect("cases.db")
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM cases")
        cases = cursor.fetchall()
        conn.close()
        return {"cases": cases}
    except Exception as e:
        logger.error(f"Error retrieving cases: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=7860)