Spaces:
Sleeping
Sleeping
File size: 14,685 Bytes
a4a766c | 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 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | # api/decision_api.py
from fastapi import APIRouter, HTTPException, Form, Query
from fastapi.responses import HTMLResponse, RedirectResponse
from typing import Dict, Any, Optional
import logging
from services.analytics_service import analytics_service
logger = logging.getLogger(__name__)
decision_router = APIRouter()
@decision_router.get("/approve/{decision_id}")
async def approve_decision(decision_id: str):
"""
β
Approve sponsor decision - sends contract to influencer
"""
try:
logger.info(f"π Processing approval for decision: {decision_id}")
result = await analytics_service.process_sponsor_decision(
decision_id=decision_id,
decision="approve"
)
if result["status"] == "success":
return HTMLResponse(content=f"""
<!DOCTYPE html>
<html>
<head>
<title>Decision Approved</title>
<style>
body {{ font-family: Arial, sans-serif; text-align: center; padding: 50px; }}
.success {{ color: #28a745; font-size: 24px; margin: 20px 0; }}
.details {{ background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px auto; max-width: 500px; }}
</style>
</head>
<body>
<h1>β
Decision Approved!</h1>
<div class="success">Contract has been sent to the influencer</div>
<div class="details">
<p><strong>Decision ID:</strong> {decision_id}</p>
<p><strong>Action:</strong> {result.get('action', 'N/A')}</p>
<p><strong>Influencer Email:</strong> {result.get('influencer_email', 'N/A')}</p>
</div>
<p>The influencer will receive a contract email with all the negotiated terms.</p>
</body>
</html>
""")
else:
# Handle error cases with user-friendly HTML instead of exceptions
return HTMLResponse(content=f"""
<!DOCTYPE html>
<html>
<head>
<title>Decision Processing Issue</title>
<style>
body {{ font-family: Arial, sans-serif; text-align: center; padding: 50px; }}
.error {{ color: #dc3545; font-size: 24px; margin: 20px 0; }}
.details {{ background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px auto; max-width: 600px; }}
.info {{ background: #d1ecf1; padding: 15px; border-radius: 4px; margin: 20px 0; }}
</style>
</head>
<body>
<h1>β οΈ Decision Cannot Be Processed</h1>
<div class="error">{result.get('message', 'Unknown error occurred')}</div>
<div class="details">
<p><strong>Decision ID:</strong> {decision_id}</p>
<div class="info">
<p><strong>Possible reasons:</strong></p>
<ul style="text-align: left;">
<li>This decision link has already been used</li>
<li>The decision has expired (48-hour limit)</li>
<li>The server was restarted, clearing temporary data</li>
</ul>
</div>
</div>
<p>If you need to make a decision on this collaboration, please contact the campaign manager directly.</p>
</body>
</html>
""", status_code=200) # Use 200 instead of 400 for better user experience
except Exception as e:
logger.error(f"β Error processing approval: {str(e)}")
# Return user-friendly error page instead of throwing exception
return HTMLResponse(content=f"""
<!DOCTYPE html>
<html>
<head>
<title>System Error</title>
<style>
body {{ font-family: Arial, sans-serif; text-align: center; padding: 50px; }}
.error {{ color: #dc3545; font-size: 24px; margin: 20px 0; }}
.details {{ background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px auto; max-width: 600px; }}
</style>
</head>
<body>
<h1>β System Error</h1>
<div class="error">Unable to process your decision at this time</div>
<div class="details">
<p><strong>Decision ID:</strong> {decision_id}</p>
<p>Please try again later or contact support if the issue persists.</p>
<p><small>Error: {str(e)}</small></p>
</div>
</body>
</html>
""", status_code=500)
@decision_router.get("/reject/{decision_id}")
async def reject_decision_form(decision_id: str):
"""
β Show rejection form for sponsor to provide message
"""
return HTMLResponse(content=f"""
<!DOCTYPE html>
<html>
<head>
<title>Reject Decision</title>
<style>
body {{ font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }}
.form-group {{ margin: 20px 0; }}
label {{ display: block; margin-bottom: 5px; font-weight: bold; }}
textarea {{ width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }}
button {{ background: #dc3545; color: white; padding: 12px 24px; border: none; border-radius: 4px; cursor: pointer; }}
button:hover {{ background: #c82333; }}
.warning {{ background: #fff3cd; padding: 15px; border-radius: 4px; margin: 20px 0; }}
</style>
</head>
<body>
<h1>β Reject Collaboration</h1>
<div class="warning">
<strong>Note:</strong> This will send a polite rejection email to the influencer.
</div>
<form method="post" action="/api/decision/reject/{decision_id}">
<div class="form-group">
<label for="message">Optional Message to Influencer:</label>
<textarea id="message" name="message" rows="4" placeholder="We appreciate your interest, but we've decided to go with a different approach for this campaign..."></textarea>
</div>
<div class="form-group">
<button type="submit">Send Rejection Email</button>
</div>
</form>
<p><small><strong>Decision ID:</strong> {decision_id}</small></p>
</body>
</html>
""")
@decision_router.post("/reject/{decision_id}")
async def reject_decision(decision_id: str, message: str = Form("")):
"""
β Process rejection decision - sends regret email to influencer
"""
try:
logger.info(f"π Processing rejection for decision: {decision_id}")
result = await analytics_service.process_sponsor_decision(
decision_id=decision_id,
decision="reject",
sponsor_message=message if message.strip() else None
)
if result["status"] == "success":
return HTMLResponse(content=f"""
<!DOCTYPE html>
<html>
<head>
<title>Decision Rejected</title>
<style>
body {{ font-family: Arial, sans-serif; text-align: center; padding: 50px; }}
.rejected {{ color: #dc3545; font-size: 24px; margin: 20px 0; }}
.details {{ background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px auto; max-width: 500px; }}
</style>
</head>
<body>
<h1>β Decision Rejected</h1>
<div class="rejected">Regret email has been sent to the influencer</div>
<div class="details">
<p><strong>Decision ID:</strong> {decision_id}</p>
<p><strong>Action:</strong> {result.get('action', 'N/A')}</p>
<p><strong>Influencer Email:</strong> {result.get('influencer_email', 'N/A')}</p>
</div>
<p>The influencer has been politely notified that we won't be proceeding with this collaboration.</p>
</body>
</html>
""")
else:
# Handle error cases with user-friendly HTML instead of exceptions
return HTMLResponse(content=f"""
<!DOCTYPE html>
<html>
<head>
<title>Decision Processing Issue</title>
<style>
body {{ font-family: Arial, sans-serif; text-align: center; padding: 50px; }}
.error {{ color: #dc3545; font-size: 24px; margin: 20px 0; }}
.details {{ background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px auto; max-width: 600px; }}
.info {{ background: #d1ecf1; padding: 15px; border-radius: 4px; margin: 20px 0; }}
</style>
</head>
<body>
<h1>β οΈ Decision Cannot Be Processed</h1>
<div class="error">{result.get('message', 'Unknown error occurred')}</div>
<div class="details">
<p><strong>Decision ID:</strong> {decision_id}</p>
<div class="info">
<p><strong>Possible reasons:</strong></p>
<ul style="text-align: left;">
<li>This decision link has already been used</li>
<li>The decision has expired (48-hour limit)</li>
<li>The server was restarted, clearing temporary data</li>
</ul>
</div>
</div>
<p>If you need to make a decision on this collaboration, please contact the campaign manager directly.</p>
</body>
</html>
""", status_code=200)
except Exception as e:
logger.error(f"β Error processing rejection: {str(e)}")
# Return user-friendly error page instead of throwing exception
return HTMLResponse(content=f"""
<!DOCTYPE html>
<html>
<head>
<title>System Error</title>
<style>
body {{ font-family: Arial, sans-serif; text-align: center; padding: 50px; }}
.error {{ color: #dc3545; font-size: 24px; margin: 20px 0; }}
.details {{ background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px auto; max-width: 600px; }}
</style>
</head>
<body>
<h1>β System Error</h1>
<div class="error">Unable to process your decision at this time</div>
<div class="details">
<p><strong>Decision ID:</strong> {decision_id}</p>
<p>Please try again later or contact support if the issue persists.</p>
<p><small>Error: {str(e)}</small></p>
</div>
</body>
</html>
""", status_code=500)
@decision_router.get("/pending")
async def get_pending_decisions():
"""
π Get all pending sponsor decisions
"""
try:
pending = analytics_service.get_pending_decisions()
return {
"status": "success",
"pending_decisions": pending
}
except Exception as e:
logger.error(f"β Error getting pending decisions: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to get pending decisions: {str(e)}")
@decision_router.post("/test-analytics")
async def test_analytics_workflow(test_data: Dict[str, Any]):
"""
π§ͺ Test the analytics workflow with sample data
"""
try:
logger.info("π§ͺ Testing analytics workflow...")
# Sample call data
call_data = {
"conversation_id": "test_conv_12345",
"call_duration_seconds": 180,
"status": "completed",
"negotiation_results": {
"final_rate": test_data.get("final_rate", 2500),
"deliverables": ["1 Instagram post", "3 Instagram stories"],
"timeline": "2 weeks",
"creator_enthusiasm": 8,
"special_terms": ["Usage rights for 6 months"]
},
"influencer_data": {
"name": test_data.get("influencer_name", "Test Influencer"),
"email": test_data.get("influencer_email", "test@example.com"),
"platform": "Instagram"
}
}
# Sample campaign data
campaign_data = {
"campaign_name": test_data.get("campaign_name", "Test Product Launch"),
"brand_name": test_data.get("brand_name", "Test Brand"),
"product_name": "Test Product Pro",
"total_budget": 10000,
"offered_rate": test_data.get("original_rate", 2000)
}
# Test sponsor email
sponsor_email = test_data.get("sponsor_email", "sponsor@testbrand.com")
# Process the completed call
result = await analytics_service.process_completed_call(
call_data=call_data,
campaign_data=campaign_data,
sponsor_email=sponsor_email
)
if result["status"] == "success":
return {
"status": "success",
"message": "Analytics workflow test completed successfully",
"decision_id": result["decision_id"],
"analytics_sent": result["email_sent"],
"test_urls": {
"approve": f"/api/decision/approve/{result['decision_id']}",
"reject": f"/api/decision/reject/{result['decision_id']}"
},
"analytics_report": result["analytics_report"]
}
else:
return {
"status": "error",
"message": result["message"]
}
except Exception as e:
logger.error(f"β Error testing analytics workflow: {str(e)}")
raise HTTPException(status_code=500, detail=f"Test failed: {str(e)}") |