Spaces:
Sleeping
Sleeping
Update app_hug.py
Browse files- app_hug.py +133 -41
app_hug.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# from fastapi import FastAPI, HTTPException, BackgroundTasks, Depends, Header, status
|
| 2 |
# from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
# from fastapi.security import APIKeyHeader
|
| 4 |
# from pydantic import BaseModel, EmailStr, Field
|
|
@@ -455,10 +455,16 @@
|
|
| 455 |
# }
|
| 456 |
|
| 457 |
# @app.post("/send-message", response_model=MessageResponse, dependencies=[Depends(verify_api_key)])
|
| 458 |
-
# async def send_message(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 |
# try:
|
| 460 |
# # Authenticate sender
|
| 461 |
-
# service = authenticate_gmail(
|
| 462 |
# if not service:
|
| 463 |
# return MessageResponse(
|
| 464 |
# success=False,
|
|
@@ -468,15 +474,15 @@
|
|
| 468 |
|
| 469 |
|
| 470 |
# formatted_html = format_email_html(
|
| 471 |
-
#
|
| 472 |
# )
|
| 473 |
# # Create the email
|
| 474 |
# email_message = create_email_message(
|
| 475 |
-
# sender=
|
| 476 |
-
# to=
|
| 477 |
-
# subject=
|
| 478 |
# message_text=formatted_html,
|
| 479 |
-
# reply_to=
|
| 480 |
# is_html=True
|
| 481 |
# )
|
| 482 |
# # Send the email
|
|
@@ -486,14 +492,14 @@
|
|
| 486 |
# success=True,
|
| 487 |
# message="Email sent successfully.",
|
| 488 |
# email_sent=True,
|
| 489 |
-
# email_subject=
|
| 490 |
# )
|
| 491 |
# else:
|
| 492 |
# return MessageResponse(
|
| 493 |
# success=False,
|
| 494 |
# message="",
|
| 495 |
# email_sent=False,
|
| 496 |
-
# email_subject=
|
| 497 |
# error="Failed to send via Gmail API"
|
| 498 |
# )
|
| 499 |
# except Exception as e:
|
|
@@ -504,27 +510,43 @@
|
|
| 504 |
# )
|
| 505 |
|
| 506 |
# @app.post("/generate-message", response_model=MessageResponse, dependencies=[Depends(verify_api_key)])
|
| 507 |
-
# async def generate_message(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 508 |
# try:
|
| 509 |
-
# current_position = f"{
|
| 510 |
# email_subject, generated_message = generate_recruitment_message_with_subject(
|
| 511 |
-
# msg_type=
|
| 512 |
-
# company=
|
| 513 |
-
# role_title=
|
| 514 |
-
# recruiter=
|
| 515 |
-
# org=
|
| 516 |
-
# candidate=
|
| 517 |
# current_pos=current_position,
|
| 518 |
-
# evaluation=
|
| 519 |
-
# past_conversation=
|
| 520 |
# )
|
| 521 |
# email_sent = False
|
| 522 |
-
# if
|
| 523 |
# registered_users = []
|
| 524 |
# if os.path.exists("registered_users.csv"):
|
| 525 |
# df = pd.read_csv("registered_users.csv")
|
| 526 |
# registered_users = df['email'].tolist() if 'email' in df.columns else []
|
| 527 |
-
# if
|
| 528 |
# return MessageResponse(
|
| 529 |
# success=True,
|
| 530 |
# message=generated_message,
|
|
@@ -532,19 +554,19 @@
|
|
| 532 |
# email_subject=email_subject,
|
| 533 |
# error="Email not sent: Sender email is not registered"
|
| 534 |
# )
|
| 535 |
-
# service = authenticate_gmail(
|
| 536 |
# if service:
|
| 537 |
# formatted_html = format_email_html(
|
| 538 |
# generated_message,
|
| 539 |
-
# sender_name=
|
| 540 |
-
# sender_org=
|
| 541 |
# )
|
| 542 |
# email_message = create_email_message(
|
| 543 |
-
# sender=
|
| 544 |
-
# to=
|
| 545 |
# subject=email_subject,
|
| 546 |
# message_text=formatted_html,
|
| 547 |
-
# reply_to=
|
| 548 |
# is_html=True
|
| 549 |
# )
|
| 550 |
# email_sent = send_gmail_message(service, "me", email_message)
|
|
@@ -664,7 +686,6 @@
|
|
| 664 |
|
| 665 |
|
| 666 |
|
| 667 |
-
|
| 668 |
from fastapi import FastAPI, HTTPException, BackgroundTasks, Depends, Header, status, Query
|
| 669 |
from fastapi.middleware.cors import CORSMiddleware
|
| 670 |
from fastapi.security import APIKeyHeader
|
|
@@ -703,7 +724,7 @@ langfuse_handler = CallbackHandler()
|
|
| 703 |
# ------------------------------------------
|
| 704 |
# Security Configuration
|
| 705 |
# ------------------------------------------
|
| 706 |
-
API_PASSWORD = os.getenv("API_PASSWORD") # Can be overridden by env var
|
| 707 |
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
|
| 708 |
|
| 709 |
def verify_api_key(api_key: Optional[str] = Depends(api_key_header)) -> str:
|
|
@@ -1077,25 +1098,96 @@ def generate_recruitment_message_with_subject(
|
|
| 1077 |
|
| 1078 |
def format_email_html(body: str, sender_name: Optional[str]=None, sender_org: Optional[str]=None):
|
| 1079 |
"""Wrap plain text body in an HTML template for better appearance."""
|
| 1080 |
-
# Convert consecutive line breaks into HTML paragraphs
|
| 1081 |
import re
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1082 |
# Smart paragraphing
|
| 1083 |
-
body = re.sub(r"\n\s*\n", "</p><p>", body
|
| 1084 |
-
body = re.sub(r"\n", "<br>", body)
|
| 1085 |
-
|
|
|
|
| 1086 |
signature = ""
|
| 1087 |
if sender_name or sender_org:
|
| 1088 |
-
signature = "
|
|
|
|
|
|
|
|
|
|
| 1089 |
if sender_name:
|
| 1090 |
-
signature += f"{sender_name}
|
| 1091 |
if sender_org:
|
| 1092 |
-
signature += f"{sender_org}
|
|
|
|
|
|
|
| 1093 |
html = f"""
|
|
|
|
| 1094 |
<html>
|
| 1095 |
-
|
| 1096 |
-
<
|
| 1097 |
-
|
| 1098 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1099 |
</html>
|
| 1100 |
"""
|
| 1101 |
return html
|
|
|
|
| 1 |
+
# from fastapi import FastAPI, HTTPException, BackgroundTasks, Depends, Header, status, Query
|
| 2 |
# from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
# from fastapi.security import APIKeyHeader
|
| 4 |
# from pydantic import BaseModel, EmailStr, Field
|
|
|
|
| 455 |
# }
|
| 456 |
|
| 457 |
# @app.post("/send-message", response_model=MessageResponse, dependencies=[Depends(verify_api_key)])
|
| 458 |
+
# async def send_message(
|
| 459 |
+
# subject: str = Query(..., description="Email subject"),
|
| 460 |
+
# email_body: str = Query(..., description="Email body content"),
|
| 461 |
+
# sender_email: str = Query(..., description="Sender's email address"),
|
| 462 |
+
# recipient_email: str = Query(..., description="Recipient's email address"),
|
| 463 |
+
# reply_to_email: Optional[str] = Query(None, description="Reply-to email address")
|
| 464 |
+
# ):
|
| 465 |
# try:
|
| 466 |
# # Authenticate sender
|
| 467 |
+
# service = authenticate_gmail(sender_email)
|
| 468 |
# if not service:
|
| 469 |
# return MessageResponse(
|
| 470 |
# success=False,
|
|
|
|
| 474 |
|
| 475 |
|
| 476 |
# formatted_html = format_email_html(
|
| 477 |
+
# email_body
|
| 478 |
# )
|
| 479 |
# # Create the email
|
| 480 |
# email_message = create_email_message(
|
| 481 |
+
# sender=sender_email,
|
| 482 |
+
# to=recipient_email,
|
| 483 |
+
# subject=subject,
|
| 484 |
# message_text=formatted_html,
|
| 485 |
+
# reply_to=reply_to_email,
|
| 486 |
# is_html=True
|
| 487 |
# )
|
| 488 |
# # Send the email
|
|
|
|
| 492 |
# success=True,
|
| 493 |
# message="Email sent successfully.",
|
| 494 |
# email_sent=True,
|
| 495 |
+
# email_subject=subject
|
| 496 |
# )
|
| 497 |
# else:
|
| 498 |
# return MessageResponse(
|
| 499 |
# success=False,
|
| 500 |
# message="",
|
| 501 |
# email_sent=False,
|
| 502 |
+
# email_subject=subject,
|
| 503 |
# error="Failed to send via Gmail API"
|
| 504 |
# )
|
| 505 |
# except Exception as e:
|
|
|
|
| 510 |
# )
|
| 511 |
|
| 512 |
# @app.post("/generate-message", response_model=MessageResponse, dependencies=[Depends(verify_api_key)])
|
| 513 |
+
# async def generate_message(
|
| 514 |
+
# background_tasks: BackgroundTasks,
|
| 515 |
+
# sender_email: str = Query(..., description="Sender's email address"),
|
| 516 |
+
# recipient_email: str = Query(..., description="Recipient's email address"),
|
| 517 |
+
# candidate_name: str = Query(..., description="Candidate's name"),
|
| 518 |
+
# current_role: str = Query(..., description="Candidate's current role"),
|
| 519 |
+
# current_company: str = Query(..., description="Candidate's current company"),
|
| 520 |
+
# company_name: str = Query(..., description="Company name for the job"),
|
| 521 |
+
# role: str = Query(..., description="Job role title"),
|
| 522 |
+
# recruiter_name: str = Query(..., description="Recruiter's name"),
|
| 523 |
+
# organisation: str = Query(..., description="Recruiting organisation"),
|
| 524 |
+
# message_type: MessageType = Query(..., description="Type of message"),
|
| 525 |
+
# job_evaluation: Optional[str] = Query(None, description="Recruiter's evaluation of candidate for the job"),
|
| 526 |
+
# reply_to_email: Optional[str] = Query(None, description="Recruiter's email for reply-to header"),
|
| 527 |
+
# send_email: bool = Query(False, description="Whether to send the email"),
|
| 528 |
+
# past_conversation: Optional[str] = Query(None, description="Previous messages with candidate")
|
| 529 |
+
# ):
|
| 530 |
# try:
|
| 531 |
+
# current_position = f"{current_role} at {current_company}"
|
| 532 |
# email_subject, generated_message = generate_recruitment_message_with_subject(
|
| 533 |
+
# msg_type=message_type.value.replace('followup', 'follow-up'),
|
| 534 |
+
# company=company_name,
|
| 535 |
+
# role_title=role,
|
| 536 |
+
# recruiter=recruiter_name,
|
| 537 |
+
# org=organisation,
|
| 538 |
+
# candidate=candidate_name,
|
| 539 |
# current_pos=current_position,
|
| 540 |
+
# evaluation=job_evaluation,
|
| 541 |
+
# past_conversation=past_conversation
|
| 542 |
# )
|
| 543 |
# email_sent = False
|
| 544 |
+
# if send_email:
|
| 545 |
# registered_users = []
|
| 546 |
# if os.path.exists("registered_users.csv"):
|
| 547 |
# df = pd.read_csv("registered_users.csv")
|
| 548 |
# registered_users = df['email'].tolist() if 'email' in df.columns else []
|
| 549 |
+
# if sender_email.lower() not in [user.lower() for user in registered_users]:
|
| 550 |
# return MessageResponse(
|
| 551 |
# success=True,
|
| 552 |
# message=generated_message,
|
|
|
|
| 554 |
# email_subject=email_subject,
|
| 555 |
# error="Email not sent: Sender email is not registered"
|
| 556 |
# )
|
| 557 |
+
# service = authenticate_gmail(sender_email)
|
| 558 |
# if service:
|
| 559 |
# formatted_html = format_email_html(
|
| 560 |
# generated_message,
|
| 561 |
+
# sender_name=recruiter_name,
|
| 562 |
+
# sender_org=organisation
|
| 563 |
# )
|
| 564 |
# email_message = create_email_message(
|
| 565 |
+
# sender=sender_email,
|
| 566 |
+
# to=recipient_email,
|
| 567 |
# subject=email_subject,
|
| 568 |
# message_text=formatted_html,
|
| 569 |
+
# reply_to=reply_to_email,
|
| 570 |
# is_html=True
|
| 571 |
# )
|
| 572 |
# email_sent = send_gmail_message(service, "me", email_message)
|
|
|
|
| 686 |
|
| 687 |
|
| 688 |
|
|
|
|
| 689 |
from fastapi import FastAPI, HTTPException, BackgroundTasks, Depends, Header, status, Query
|
| 690 |
from fastapi.middleware.cors import CORSMiddleware
|
| 691 |
from fastapi.security import APIKeyHeader
|
|
|
|
| 724 |
# ------------------------------------------
|
| 725 |
# Security Configuration
|
| 726 |
# ------------------------------------------
|
| 727 |
+
API_PASSWORD = os.getenv("API_PASSWORD", "Synapse@2025") # Can be overridden by env var
|
| 728 |
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False)
|
| 729 |
|
| 730 |
def verify_api_key(api_key: Optional[str] = Depends(api_key_header)) -> str:
|
|
|
|
| 1098 |
|
| 1099 |
def format_email_html(body: str, sender_name: Optional[str]=None, sender_org: Optional[str]=None):
|
| 1100 |
"""Wrap plain text body in an HTML template for better appearance."""
|
|
|
|
| 1101 |
import re
|
| 1102 |
+
|
| 1103 |
+
# Smart paragraphing and formatting
|
| 1104 |
+
body = body.strip()
|
| 1105 |
+
|
| 1106 |
+
# Convert markdown-style formatting
|
| 1107 |
+
body = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', body) # Bold text
|
| 1108 |
+
body = re.sub(r'\*(.*?)\*', r'<em>\1</em>', body) # Italic text
|
| 1109 |
+
|
| 1110 |
# Smart paragraphing
|
| 1111 |
+
body = re.sub(r"\n\s*\n", "</p><p>", body) # Double newlines => new paragraph
|
| 1112 |
+
body = re.sub(r"\n", "<br>", body) # Single newlines => <br>
|
| 1113 |
+
|
| 1114 |
+
# Optional signature with better styling
|
| 1115 |
signature = ""
|
| 1116 |
if sender_name or sender_org:
|
| 1117 |
+
signature = f"""
|
| 1118 |
+
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #e0e0e0;">
|
| 1119 |
+
<p style="margin: 0; color: #666; font-size: 14px;">Best regards,</p>
|
| 1120 |
+
"""
|
| 1121 |
if sender_name:
|
| 1122 |
+
signature += f'<p style="margin: 5px 0 0 0; font-weight: 600; color: #333; font-size: 16px;">{sender_name}</p>'
|
| 1123 |
if sender_org:
|
| 1124 |
+
signature += f'<p style="margin: 3px 0 0 0; color: #666; font-size: 14px;">{sender_org}</p>'
|
| 1125 |
+
signature += "</div>"
|
| 1126 |
+
|
| 1127 |
html = f"""
|
| 1128 |
+
<!DOCTYPE html>
|
| 1129 |
<html>
|
| 1130 |
+
<head>
|
| 1131 |
+
<meta charset="UTF-8">
|
| 1132 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 1133 |
+
<title>Recruitment Message</title>
|
| 1134 |
+
</head>
|
| 1135 |
+
<body style="
|
| 1136 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 1137 |
+
color: #333333;
|
| 1138 |
+
line-height: 1.6;
|
| 1139 |
+
max-width: 600px;
|
| 1140 |
+
margin: 0 auto;
|
| 1141 |
+
padding: 20px;
|
| 1142 |
+
background-color: #f8f9fa;
|
| 1143 |
+
">
|
| 1144 |
+
<div style="
|
| 1145 |
+
background-color: #ffffff;
|
| 1146 |
+
border-radius: 8px;
|
| 1147 |
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
| 1148 |
+
padding: 30px;
|
| 1149 |
+
border: 1px solid #e9ecef;
|
| 1150 |
+
">
|
| 1151 |
+
<!-- Header -->
|
| 1152 |
+
<div style="
|
| 1153 |
+
border-bottom: 2px solid #007bff;
|
| 1154 |
+
padding-bottom: 15px;
|
| 1155 |
+
margin-bottom: 25px;
|
| 1156 |
+
">
|
| 1157 |
+
<h2 style="
|
| 1158 |
+
margin: 0;
|
| 1159 |
+
color: #007bff;
|
| 1160 |
+
font-size: 20px;
|
| 1161 |
+
font-weight: 600;
|
| 1162 |
+
">Professional Recruitment Message</h2>
|
| 1163 |
+
</div>
|
| 1164 |
+
|
| 1165 |
+
<!-- Main Content -->
|
| 1166 |
+
<div style="
|
| 1167 |
+
font-size: 15px;
|
| 1168 |
+
color: #444;
|
| 1169 |
+
line-height: 1.7;
|
| 1170 |
+
">
|
| 1171 |
+
<p style="margin: 0 0 15px 0;">{body}</p>
|
| 1172 |
+
</div>
|
| 1173 |
+
|
| 1174 |
+
<!-- Signature -->
|
| 1175 |
+
{signature}
|
| 1176 |
+
|
| 1177 |
+
<!-- Footer -->
|
| 1178 |
+
<div style="
|
| 1179 |
+
margin-top: 30px;
|
| 1180 |
+
padding-top: 15px;
|
| 1181 |
+
border-top: 1px solid #e0e0e0;
|
| 1182 |
+
text-align: center;
|
| 1183 |
+
font-size: 12px;
|
| 1184 |
+
color: #888;
|
| 1185 |
+
">
|
| 1186 |
+
<p style="margin: 0;">This message was generated by Synapse Recruitment Platform</p>
|
| 1187 |
+
<p style="margin: 5px 0 0 0;">Please reply to this email for any inquiries</p>
|
| 1188 |
+
</div>
|
| 1189 |
+
</div>
|
| 1190 |
+
</body>
|
| 1191 |
</html>
|
| 1192 |
"""
|
| 1193 |
return html
|