Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -5,11 +5,6 @@ from transformers import pipeline
|
|
5 |
from simple_salesforce import Salesforce
|
6 |
from dotenv import load_dotenv
|
7 |
import base64
|
8 |
-
import logging
|
9 |
-
|
10 |
-
# Configure logging
|
11 |
-
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
12 |
-
logger = logging.getLogger(__name__)
|
13 |
|
14 |
# Load environment variables from .env
|
15 |
load_dotenv()
|
@@ -18,178 +13,88 @@ load_dotenv()
|
|
18 |
SF_USERNAME = os.getenv("SF_USERNAME")
|
19 |
SF_PASSWORD = os.getenv("SF_PASSWORD")
|
20 |
SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
|
21 |
-
|
22 |
SF_OBJECT_NAME = os.getenv("SF_OBJECT_NAME", "Agent_Prospect__c")
|
23 |
SF_SCORE_FIELD = os.getenv("SF_SCORE_FIELD", "Suitability_Score__c")
|
24 |
SF_LINK_FIELD = os.getenv("SF_RESUME_FIELD_LINK", "Resume_File_Link__c")
|
25 |
-
SF_OUTPUT_FIELD = os.getenv("SF_OUTPUT_FIELD", "Processing_Output__c")
|
26 |
SF_RECORD_ID = os.getenv("SF_RECORD_ID")
|
27 |
|
28 |
# Validate required credentials
|
29 |
required = ["SF_USERNAME", "SF_PASSWORD", "SF_SECURITY_TOKEN", "SF_RECORD_ID"]
|
30 |
missing = [var for var in required if not os.getenv(var)]
|
31 |
if missing:
|
32 |
-
|
33 |
-
|
34 |
-
|
|
|
35 |
|
36 |
# Connect to Salesforce
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
)
|
44 |
-
logger.info("Successfully connected to Salesforce")
|
45 |
-
except Exception as e:
|
46 |
-
error_msg = f"Failed to connect to Salesforce: {str(e)}"
|
47 |
-
logger.error(error_msg)
|
48 |
-
raise Exception(error_msg)
|
49 |
|
50 |
# Load Hugging Face model
|
51 |
-
|
52 |
-
classifier = pipeline("text-classification", model="nlptown/bert-base-multilingual-uncased-sentiment")
|
53 |
-
logger.info("Hugging Face model loaded successfully")
|
54 |
-
except Exception as e:
|
55 |
-
error_msg = f"Failed to load Hugging Face model: {str(e)}"
|
56 |
-
logger.error(error_msg)
|
57 |
-
raise Exception(error_msg)
|
58 |
|
59 |
def process_resume(file):
|
60 |
try:
|
61 |
record_id = SF_RECORD_ID
|
62 |
-
logger.info(f"Processing resume for record ID: {record_id}")
|
63 |
-
|
64 |
-
# Validate file
|
65 |
-
if not file:
|
66 |
-
output = "β No file uploaded."
|
67 |
-
logger.error(output)
|
68 |
-
update_salesforce_output(record_id, output)
|
69 |
-
return output
|
70 |
|
71 |
# Extract text from PDF
|
72 |
-
|
73 |
-
|
74 |
-
extracted_text = "\n".join([page.extract_text() or "" for page in pdf.pages])
|
75 |
-
logger.info("PDF text extracted successfully")
|
76 |
-
except Exception as e:
|
77 |
-
output = f"β Failed to extract text from PDF: {str(e)}"
|
78 |
-
logger.error(output)
|
79 |
-
update_salesforce_output(record_id, output)
|
80 |
-
return output
|
81 |
|
82 |
if not extracted_text.strip():
|
83 |
-
|
84 |
-
logger.warning(output)
|
85 |
-
update_salesforce_output(record_id, output)
|
86 |
-
return output
|
87 |
|
88 |
# Call Hugging Face model
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
summary = f"Predicted Label: {label}\nSuitability Score: {score:.2f}"
|
94 |
-
logger.info(f"Model prediction: {summary}")
|
95 |
-
except Exception as e:
|
96 |
-
output = f"β Failed to process text with model: {str(e)}"
|
97 |
-
logger.error(output)
|
98 |
-
update_salesforce_output(record_id, output)
|
99 |
-
return output
|
100 |
|
101 |
# Encode PDF in base64
|
102 |
-
|
103 |
-
|
104 |
-
encoded_pdf = base64.b64encode(f.read()).decode("utf-8")
|
105 |
-
logger.info("PDF encoded successfully")
|
106 |
-
except Exception as e:
|
107 |
-
output = f"β Failed to encode PDF: {str(e)}"
|
108 |
-
logger.error(output)
|
109 |
-
update_salesforce_output(record_id, output)
|
110 |
-
return output
|
111 |
|
112 |
# Upload file as ContentVersion
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
})
|
119 |
-
version_id = content_result.get("id")
|
120 |
-
logger.info(f"ContentVersion created with ID: {version_id}")
|
121 |
-
except Exception as e:
|
122 |
-
output = f"β Failed to upload file to Salesforce: {str(e)}"
|
123 |
-
logger.error(output)
|
124 |
-
update_salesforce_output(record_id, output)
|
125 |
-
return output
|
126 |
|
127 |
# Get ContentDocumentId from ContentVersion
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
logger.info(f"ContentDocumentId retrieved: {content_doc_id}")
|
134 |
-
except Exception as e:
|
135 |
-
output = f"β Failed to retrieve ContentDocumentId: {str(e)}"
|
136 |
-
logger.error(output)
|
137 |
-
update_salesforce_output(record_id, output)
|
138 |
-
return output
|
139 |
|
140 |
# Link file to Salesforce record
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
})
|
148 |
-
logger.info("ContentDocument linked to record")
|
149 |
-
except Exception as e:
|
150 |
-
output = f"β Failed to link document to record: {str(e)}"
|
151 |
-
logger.error(output)
|
152 |
-
update_salesforce_output(record_id, output)
|
153 |
-
return output
|
154 |
|
155 |
# Create download link
|
156 |
-
download_link = f"{
|
157 |
-
logger.info(f"Download link generated: {download_link}")
|
158 |
-
|
159 |
-
# Prepare output for Salesforce
|
160 |
-
output = f"{summary}\n\nβ
Score and resume uploaded to Salesforce.\nπ Download Resume: {download_link}"
|
161 |
-
|
162 |
-
# Update record with score, link, and output
|
163 |
-
try:
|
164 |
-
sf.__getattr__(SF_OBJECT_NAME).update(record_id, {
|
165 |
-
SF_SCORE_FIELD: score,
|
166 |
-
SF_LINK_FIELD: download_link,
|
167 |
-
SF_OUTPUT_FIELD: output
|
168 |
-
})
|
169 |
-
logger.info("Salesforce record updated successfully")
|
170 |
-
except Exception as e:
|
171 |
-
output = f"β Failed to update Salesforce record: {str(e)}"
|
172 |
-
logger.error(output)
|
173 |
-
update_salesforce_output(record_id, output)
|
174 |
-
return output
|
175 |
-
|
176 |
-
return output
|
177 |
|
178 |
-
|
179 |
-
output = f"β Unexpected error: {str(e)}"
|
180 |
-
logger.error(output)
|
181 |
-
update_salesforce_output(record_id, output)
|
182 |
-
return output
|
183 |
-
|
184 |
-
def update_salesforce_output(record_id, output):
|
185 |
-
"""Helper function to update Salesforce record with output in case of errors"""
|
186 |
-
try:
|
187 |
sf.__getattr__(SF_OBJECT_NAME).update(record_id, {
|
188 |
-
|
|
|
189 |
})
|
190 |
-
|
|
|
|
|
191 |
except Exception as e:
|
192 |
-
|
193 |
|
194 |
# Gradio Interface
|
195 |
gr.Interface(
|
@@ -197,5 +102,5 @@ gr.Interface(
|
|
197 |
inputs=gr.File(label="Upload Resume (PDF)", file_types=[".pdf"]),
|
198 |
outputs="text",
|
199 |
title="LIC Resume AI Scorer",
|
200 |
-
description="Upload a resume PDF. It will be scored
|
201 |
-
).launch(share=False)
|
|
|
5 |
from simple_salesforce import Salesforce
|
6 |
from dotenv import load_dotenv
|
7 |
import base64
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
# Load environment variables from .env
|
10 |
load_dotenv()
|
|
|
13 |
SF_USERNAME = os.getenv("SF_USERNAME")
|
14 |
SF_PASSWORD = os.getenv("SF_PASSWORD")
|
15 |
SF_SECURITY_TOKEN = os.getenv("SF_SECURITY_TOKEN")
|
16 |
+
SF_LOGIN_URL = os.getenv("SF_LOGIN_URL", "https://login.salesforce.com")
|
17 |
SF_OBJECT_NAME = os.getenv("SF_OBJECT_NAME", "Agent_Prospect__c")
|
18 |
SF_SCORE_FIELD = os.getenv("SF_SCORE_FIELD", "Suitability_Score__c")
|
19 |
SF_LINK_FIELD = os.getenv("SF_RESUME_FIELD_LINK", "Resume_File_Link__c")
|
|
|
20 |
SF_RECORD_ID = os.getenv("SF_RECORD_ID")
|
21 |
|
22 |
# Validate required credentials
|
23 |
required = ["SF_USERNAME", "SF_PASSWORD", "SF_SECURITY_TOKEN", "SF_RECORD_ID"]
|
24 |
missing = [var for var in required if not os.getenv(var)]
|
25 |
if missing:
|
26 |
+
raise ValueError(f"Missing required .env variables: {', '.join(missing)}")
|
27 |
+
|
28 |
+
# Determine domain
|
29 |
+
domain = "login" if "login" in SF_LOGIN_URL else "test"
|
30 |
|
31 |
# Connect to Salesforce
|
32 |
+
sf = Salesforce(
|
33 |
+
username=SF_USERNAME,
|
34 |
+
password=SF_PASSWORD,
|
35 |
+
security_token=SF_SECURITY_TOKEN,
|
36 |
+
domain=domain
|
37 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
# Load Hugging Face model
|
40 |
+
classifier = pipeline("text-classification", model="nlptown/bert-base-multilingual-uncased-sentiment")
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
def process_resume(file):
|
43 |
try:
|
44 |
record_id = SF_RECORD_ID
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
# Extract text from PDF
|
47 |
+
with pdfplumber.open(file.name) as pdf:
|
48 |
+
extracted_text = "\n".join([page.extract_text() or "" for page in pdf.pages])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
if not extracted_text.strip():
|
51 |
+
return "β No extractable text found in the PDF."
|
|
|
|
|
|
|
52 |
|
53 |
# Call Hugging Face model
|
54 |
+
result = classifier(extracted_text[:1000])
|
55 |
+
label = result[0]['label']
|
56 |
+
score = round(float(result[0]['score']) * 100, 2)
|
57 |
+
summary = f"Predicted Label: {label}\nSuitability Score: {score:.2f}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
# Encode PDF in base64
|
60 |
+
with open(file.name, "rb") as f:
|
61 |
+
encoded_pdf = base64.b64encode(f.read()).decode("utf-8")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
63 |
# Upload file as ContentVersion
|
64 |
+
content_result = sf.ContentVersion.create({
|
65 |
+
"Title": "Resume",
|
66 |
+
"PathOnClient": file.name,
|
67 |
+
"VersionData": encoded_pdf
|
68 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
|
70 |
# Get ContentDocumentId from ContentVersion
|
71 |
+
version_id = content_result.get("id")
|
72 |
+
query_result = sf.query(
|
73 |
+
f"SELECT ContentDocumentId FROM ContentVersion WHERE Id = '{version_id}'"
|
74 |
+
)
|
75 |
+
content_doc_id = query_result["records"][0]["ContentDocumentId"]
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
|
77 |
# Link file to Salesforce record
|
78 |
+
sf.ContentDocumentLink.create({
|
79 |
+
"ContentDocumentId": content_doc_id,
|
80 |
+
"LinkedEntityId": record_id,
|
81 |
+
"ShareType": "V",
|
82 |
+
"Visibility": "AllUsers"
|
83 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
# Create download link
|
86 |
+
download_link = f"https://{sf.sf_instance}/sfc/servlet.shepherd/document/download/{content_doc_id}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
|
88 |
+
# Update record with score and link
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
sf.__getattr__(SF_OBJECT_NAME).update(record_id, {
|
90 |
+
SF_SCORE_FIELD: score,
|
91 |
+
SF_LINK_FIELD: download_link
|
92 |
})
|
93 |
+
|
94 |
+
return f"{summary}\n\nβ
Score and resume uploaded to Salesforce.\nπ [Download Resume]({download_link})"
|
95 |
+
|
96 |
except Exception as e:
|
97 |
+
return f"β Error: {str(e)}"
|
98 |
|
99 |
# Gradio Interface
|
100 |
gr.Interface(
|
|
|
102 |
inputs=gr.File(label="Upload Resume (PDF)", file_types=[".pdf"]),
|
103 |
outputs="text",
|
104 |
title="LIC Resume AI Scorer",
|
105 |
+
description="Upload a resume PDF. It will be scored and stored in Salesforce automatically."
|
106 |
+
).launch(share=False)
|