|
|
import gradio as gr |
|
|
import requests |
|
|
import json |
|
|
from typing import List, Dict, Any |
|
|
import pandas as pd |
|
|
|
|
|
|
|
|
API_ENDPOINT = "https://huggingface.co/spaces/JayBene1/testapicontacts" |
|
|
|
|
|
|
|
|
theme = gr.themes.Soft( |
|
|
primary_hue="blue", |
|
|
secondary_hue="slate", |
|
|
neutral_hue="slate", |
|
|
text_size="md", |
|
|
spacing_size="md", |
|
|
radius_size="md" |
|
|
).set( |
|
|
body_background_fill="#f8fafc", |
|
|
body_background_fill_dark="#1e293b", |
|
|
block_background_fill="#ffffff", |
|
|
block_background_fill_dark="#334155", |
|
|
block_border_color="#e2e8f0", |
|
|
block_border_color_dark="#475569", |
|
|
input_background_fill="#ffffff", |
|
|
input_background_fill_dark="#334155", |
|
|
input_border_color="#cbd5e1", |
|
|
input_border_color_dark="#64748b", |
|
|
button_primary_background_fill="#1e40af", |
|
|
button_primary_background_fill_hover="#1d4ed8", |
|
|
button_primary_text_color="#ffffff", |
|
|
button_secondary_background_fill="#f1f5f9", |
|
|
button_secondary_background_fill_hover="#e2e8f0", |
|
|
button_secondary_text_color="#334155" |
|
|
) |
|
|
|
|
|
def search_contacts(url: str) -> tuple[str, str]: |
|
|
""" |
|
|
Search for contacts associated with a given URL using the Hugging Face API. |
|
|
|
|
|
Args: |
|
|
url: The website URL to search for contacts |
|
|
|
|
|
Returns: |
|
|
Tuple of (formatted_results, raw_json) |
|
|
""" |
|
|
|
|
|
if not url.strip(): |
|
|
return "β Please enter a website URL", "" |
|
|
|
|
|
|
|
|
url = url.strip() |
|
|
if not url.startswith(('http://', 'https://')): |
|
|
url = 'https://' + url |
|
|
|
|
|
try: |
|
|
|
|
|
headers = { |
|
|
'Content-Type': 'application/json' |
|
|
} |
|
|
|
|
|
payload = { |
|
|
"inputs": url, |
|
|
"parameters": { |
|
|
"task": "contact_search", |
|
|
"url": url |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
response = requests.post( |
|
|
API_ENDPOINT, |
|
|
headers=headers, |
|
|
json=payload, |
|
|
timeout=30 |
|
|
) |
|
|
|
|
|
if response.status_code == 200: |
|
|
try: |
|
|
result = response.json() |
|
|
|
|
|
|
|
|
formatted_output = format_contact_results(result, url) |
|
|
raw_json = json.dumps(result, indent=2) |
|
|
|
|
|
return formatted_output, raw_json |
|
|
|
|
|
except json.JSONDecodeError: |
|
|
return f"β Error: Invalid JSON response from API", response.text |
|
|
|
|
|
else: |
|
|
return f"β API Error ({response.status_code}): {response.text}", "" |
|
|
|
|
|
except requests.exceptions.Timeout: |
|
|
return "β Request timeout. Please try again.", "" |
|
|
except requests.exceptions.ConnectionError: |
|
|
return "β Connection error. Please check your API endpoint.", "" |
|
|
except Exception as e: |
|
|
return f"β Error: {str(e)}", "" |
|
|
|
|
|
def format_contact_results(results: Dict[Any, Any], url: str) -> str: |
|
|
""" |
|
|
Format the API results into a readable format. |
|
|
|
|
|
Args: |
|
|
results: The JSON response from the API |
|
|
url: The searched URL |
|
|
|
|
|
Returns: |
|
|
Formatted string with contact information |
|
|
""" |
|
|
|
|
|
output = f"# π Contact Search Results for: {url}\n\n" |
|
|
|
|
|
|
|
|
if isinstance(results, dict): |
|
|
if 'contacts' in results: |
|
|
contacts = results['contacts'] |
|
|
elif 'results' in results: |
|
|
contacts = results['results'] |
|
|
elif 'data' in results: |
|
|
contacts = results['data'] |
|
|
else: |
|
|
contacts = results |
|
|
|
|
|
if isinstance(contacts, list) and len(contacts) > 0: |
|
|
output += f"**Found {len(contacts)} contact(s):**\n\n" |
|
|
|
|
|
for i, contact in enumerate(contacts, 1): |
|
|
output += f"## π€ Contact {i}\n" |
|
|
|
|
|
|
|
|
name = contact.get('name') or contact.get('full_name') or contact.get('contact_name') or "N/A" |
|
|
email = contact.get('email') or contact.get('email_address') or "N/A" |
|
|
title = contact.get('title') or contact.get('job_title') or contact.get('position') or "N/A" |
|
|
company = contact.get('company') or contact.get('organization') or "N/A" |
|
|
phone = contact.get('phone') or contact.get('phone_number') or "N/A" |
|
|
linkedin = contact.get('linkedin') or contact.get('linkedin_url') or "N/A" |
|
|
|
|
|
output += f"- **Name:** {name}\n" |
|
|
output += f"- **Email:** {email}\n" |
|
|
output += f"- **Title:** {title}\n" |
|
|
output += f"- **Company:** {company}\n" |
|
|
output += f"- **Phone:** {phone}\n" |
|
|
output += f"- **LinkedIn:** {linkedin}\n\n" |
|
|
|
|
|
|
|
|
additional_fields = {k: v for k, v in contact.items() |
|
|
if k not in ['name', 'full_name', 'contact_name', 'email', 'email_address', |
|
|
'title', 'job_title', 'position', 'company', 'organization', |
|
|
'phone', 'phone_number', 'linkedin', 'linkedin_url']} |
|
|
|
|
|
if additional_fields: |
|
|
output += "**Additional Information:**\n" |
|
|
for key, value in additional_fields.items(): |
|
|
output += f"- **{key.replace('_', ' ').title()}:** {value}\n" |
|
|
output += "\n" |
|
|
|
|
|
output += "---\n\n" |
|
|
|
|
|
else: |
|
|
output += "β No contacts found for this URL.\n\n" |
|
|
|
|
|
elif isinstance(results, list): |
|
|
if len(results) > 0: |
|
|
output += f"**Found {len(results)} contact(s):**\n\n" |
|
|
for i, contact in enumerate(results, 1): |
|
|
output += f"## π€ Contact {i}\n" |
|
|
output += f"{contact}\n\n" |
|
|
else: |
|
|
output += "β No contacts found for this URL.\n\n" |
|
|
else: |
|
|
output += f"**Result:** {results}\n\n" |
|
|
|
|
|
output += f"*Search completed at: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}*" |
|
|
|
|
|
return output |
|
|
|
|
|
def create_sample_data(): |
|
|
"""Create sample data for demonstration""" |
|
|
return '''# π Contact Search Results for: example.com |
|
|
|
|
|
**Found 2 contact(s):** |
|
|
|
|
|
## π€ Contact 1 |
|
|
- **Name:** John Smith |
|
|
- **Email:** john.smith@example.com |
|
|
- **Title:** CEO |
|
|
- **Company:** Example Corp |
|
|
- **Phone:** (555) 123-4567 |
|
|
- **LinkedIn:** linkedin.com/in/johnsmith |
|
|
|
|
|
--- |
|
|
|
|
|
## π€ Contact 2 |
|
|
- **Name:** Jane Doe |
|
|
- **Email:** jane.doe@example.com |
|
|
- **Title:** VP of Sales |
|
|
- **Company:** Example Corp |
|
|
- **Phone:** (555) 123-4568 |
|
|
- **LinkedIn:** linkedin.com/in/janedoe |
|
|
|
|
|
--- |
|
|
|
|
|
*Search completed at: 2025-01-15 10:30:00*''' |
|
|
|
|
|
|
|
|
with gr.Blocks(theme=theme, title="Contact Search - Kwekel Companies", css=""" |
|
|
.gradio-container { |
|
|
max-width: 1200px !important; |
|
|
margin: 0 auto !important; |
|
|
} |
|
|
.header { |
|
|
background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%); |
|
|
color: white; |
|
|
padding: 2rem; |
|
|
border-radius: 0.5rem; |
|
|
margin-bottom: 2rem; |
|
|
text-align: center; |
|
|
} |
|
|
.header h1 { |
|
|
margin: 0; |
|
|
font-size: 2.5rem; |
|
|
font-weight: 700; |
|
|
margin-bottom: 0.5rem; |
|
|
} |
|
|
.header p { |
|
|
margin: 0; |
|
|
font-size: 1.2rem; |
|
|
opacity: 0.9; |
|
|
} |
|
|
.info-box { |
|
|
background: #f0f9ff; |
|
|
border: 1px solid #0ea5e9; |
|
|
border-radius: 0.5rem; |
|
|
padding: 1rem; |
|
|
margin: 1rem 0; |
|
|
} |
|
|
.footer { |
|
|
text-align: center; |
|
|
margin-top: 2rem; |
|
|
padding-top: 2rem; |
|
|
border-top: 1px solid #e2e8f0; |
|
|
color: #64748b; |
|
|
} |
|
|
""") as demo: |
|
|
|
|
|
|
|
|
gr.HTML(""" |
|
|
<div class="header"> |
|
|
<h1>π Contact Search Tool</h1> |
|
|
<p>Professional contact discovery powered by AI</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
|
|
|
gr.HTML(f""" |
|
|
<div class="info-box"> |
|
|
<h3>π Instructions:</h3> |
|
|
<ol> |
|
|
<li>Enter the website URL you want to search for contacts</li> |
|
|
<li>Click "Search Contacts" to get results</li> |
|
|
<li>View formatted results and raw JSON response</li> |
|
|
</ol> |
|
|
<p><strong>API Endpoint:</strong> {API_ENDPOINT}</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=2): |
|
|
url_input = gr.Textbox( |
|
|
label="π Website URL", |
|
|
placeholder="example.com or https://example.com", |
|
|
info="Enter the website URL to search for contacts", |
|
|
lines=1 |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
search_btn = gr.Button("π Search Contacts", variant="primary", scale=2) |
|
|
demo_btn = gr.Button("π Show Demo", variant="secondary", scale=1) |
|
|
clear_btn = gr.Button("ποΈ Clear", variant="secondary", scale=1) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
results_output = gr.Markdown( |
|
|
label="π Contact Results", |
|
|
value="Enter a URL and click 'Search Contacts' to see results here.", |
|
|
height=400 |
|
|
) |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
json_output = gr.Code( |
|
|
label="π Raw JSON Response", |
|
|
language="json", |
|
|
value="", |
|
|
height=400 |
|
|
) |
|
|
|
|
|
|
|
|
search_btn.click( |
|
|
fn=search_contacts, |
|
|
inputs=[url_input], |
|
|
outputs=[results_output, json_output] |
|
|
) |
|
|
|
|
|
demo_btn.click( |
|
|
fn=lambda: (create_sample_data(), '{"contacts": [{"name": "John Smith", "email": "john.smith@example.com", "title": "CEO"}]}'), |
|
|
outputs=[results_output, json_output] |
|
|
) |
|
|
|
|
|
clear_btn.click( |
|
|
fn=lambda: ("", ""), |
|
|
outputs=[url_input, results_output] |
|
|
) |
|
|
|
|
|
|
|
|
gr.HTML(""" |
|
|
<div class="footer"> |
|
|
<p>Β© 2025 Contact Search Tool - Professional contact discovery solution</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch( |
|
|
share=True, |
|
|
server_name="0.0.0.0", |
|
|
server_port=7860, |
|
|
show_error=True, |
|
|
debug=True |
|
|
) |