JayBene1 commited on
Commit
e4ac4c8
Β·
verified Β·
1 Parent(s): 838157a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +289 -577
app.py CHANGED
@@ -1,628 +1,340 @@
1
  import gradio as gr
2
  import requests
3
- import re
4
  import json
5
- import csv
6
- import io
7
- from urllib.parse import urlparse, urljoin
8
- import time
9
- import random
10
 
11
- def extract_domain(url):
12
- """Extract domain from URL"""
13
- try:
14
- if not url.startswith(('http://', 'https://')):
15
- url = 'https://' + url
16
- parsed = urlparse(url)
17
- domain = parsed.netloc.lower()
18
- # Remove www. if present
19
- if domain.startswith('www.'):
20
- domain = domain[4:]
21
- return domain
22
- except:
23
- return ""
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- def simulate_website_scraping(url):
26
- """Fetch contacts using your Hugging Face Space API"""
27
- try:
28
- response = requests.get(
29
- "https://jaybene1-testapicontacts.hf.space/contacts",
30
- params={"website": url},
31
- timeout=10
32
- )
33
- response.raise_for_status()
34
- data = response.json()
35
-
36
- # Wrap in list if it's a single dict
37
- if isinstance(data, dict):
38
- data = [data]
39
-
40
- # Normalize structure to match the app's expectations
41
- contacts = []
42
- for item in data:
43
- contact = {
44
- "first_name": item.get("name", "").split()[0],
45
- "last_name": " ".join(item.get("name", "").split()[1:]),
46
- "email": item.get("email", ""),
47
- "phone": item.get("phone", ""),
48
- "job_title": item.get("position", ""),
49
- "company": item.get("company", ""),
50
- "website": item.get("website", "")
51
- }
52
- contacts.append(contact)
53
-
54
- return contacts
55
 
56
- except Exception as e:
57
- print(f"Error contacting API for {url}: {e}")
58
- return []
59
-
60
- def is_valid_url(url):
61
- """Check if a string looks like a valid URL"""
62
- url = url.strip()
63
- if not url:
64
- return False
65
 
66
- # Check if it contains common URL patterns
67
- url_patterns = [
68
- r'^\w+\.\w+', # domain.com
69
- r'^\w+\.\w+\.\w+', # subdomain.domain.com
70
- r'^https?://', # starts with http/https
71
- r'www\.', # contains www
72
- ]
73
 
74
- for pattern in url_patterns:
75
- if re.search(pattern, url.lower()):
76
- return True
77
 
78
- return False
79
-
80
- def parse_csv_file(file_obj):
81
- """Parse CSV file and extract website URLs from column H (or auto-detect)"""
82
- websites = []
83
- debug_info = []
84
 
85
  try:
86
- # Handle different file input types
87
- if hasattr(file_obj, 'name'):
88
- # This is a file path (NamedString from Gradio)
89
- with open(file_obj.name, 'r', encoding='utf-8') as f:
90
- content = f.read()
91
- elif isinstance(file_obj, str):
92
- # This is already a string
93
- content = file_obj
94
- else:
95
- # This might be bytes
96
- content = file_obj.decode('utf-8')
97
-
98
- # Parse CSV
99
- csv_reader = csv.reader(io.StringIO(content))
100
- rows = list(csv_reader)
101
-
102
- if not rows:
103
- debug_info.append("CSV file is empty")
104
- return [], debug_info
105
-
106
- debug_info.append(f"Total rows in CSV: {len(rows)}")
107
-
108
- # Analyze the first few rows to understand structure
109
- if len(rows) > 0:
110
- debug_info.append(f"First row has {len(rows[0])} columns: {rows[0]}")
111
- if len(rows) > 1:
112
- debug_info.append(f"Second row has {len(rows[1])} columns: {rows[1]}")
113
-
114
- # Check if first row looks like headers
115
- first_row = rows[0]
116
- has_headers = any(col.lower() in ['website', 'url', 'domain', 'site', 'web'] for col in first_row)
117
-
118
- # Try to find website column
119
- website_column_index = None
120
 
121
- # First, try column H (index 7)
122
- if len(first_row) >= 8:
123
- website_column_index = 7
124
- debug_info.append(f"Using column H (index 7) as specified")
 
 
 
 
 
 
 
 
 
125
  else:
126
- # Auto-detect website column
127
- for i, col in enumerate(first_row):
128
- if col.lower() in ['website', 'url', 'domain', 'site', 'web']:
129
- website_column_index = i
130
- debug_info.append(f"Auto-detected website column at index {i}: '{col}'")
131
- break
132
-
133
- if website_column_index is None:
134
- # If no obvious column found, scan all columns for URLs
135
- debug_info.append("No obvious website column found, scanning all columns for URLs...")
136
- for row_idx, row in enumerate(rows[1:] if has_headers else rows, start=1):
137
- for col_idx, cell in enumerate(row):
138
- if is_valid_url(cell):
139
- website_column_index = col_idx
140
- debug_info.append(f"Found URLs in column {col_idx} (row {row_idx}): '{cell}'")
141
- break
142
- if website_column_index is not None:
143
- break
144
-
145
- if website_column_index is None:
146
- debug_info.append("ERROR: Could not find any column with website URLs")
147
- return [], debug_info
148
-
149
- # Extract websites from the identified column
150
- start_row = 1 if has_headers else 0
151
- for row_idx, row in enumerate(rows[start_row:], start=start_row + 1):
152
- if len(row) > website_column_index:
153
- website_url = row[website_column_index].strip()
154
- if website_url and is_valid_url(website_url):
155
- websites.append(website_url)
156
- debug_info.append(f"Found website in row {row_idx}: {website_url}")
157
- elif website_url:
158
- debug_info.append(f"Row {row_idx}: '{website_url}' doesn't look like a valid URL")
159
- else:
160
- debug_info.append(f"Row {row_idx}: Empty cell in website column")
161
- else:
162
- debug_info.append(f"Row {row_idx}: Has only {len(row)} columns, need at least {website_column_index + 1}")
163
-
164
- debug_info.append(f"Total websites extracted: {len(websites)}")
165
- if websites:
166
- debug_info.append(f"Sample websites: {websites[:5]}")
167
-
168
- return websites, debug_info
169
-
170
  except Exception as e:
171
- debug_info.append(f"Error parsing CSV: {e}")
172
- return [], debug_info
173
 
174
- def search_csv_websites(csv_file, max_results=10):
175
- """Search for contacts from websites listed in CSV file"""
176
- if csv_file is None:
177
- return "Please upload a CSV file", ""
178
 
179
- try:
180
- # Parse CSV file
181
- websites, debug_info = parse_csv_file(csv_file)
182
-
183
- debug_text = "\n".join(debug_info)
184
-
185
- if not websites:
186
- error_msg = "No websites found in the CSV file.\n\n"
187
- error_msg += "DEBUG INFORMATION:\n" + debug_text + "\n\n"
188
- error_msg += "TROUBLESHOOTING:\n"
189
- error_msg += "1. Ensure your CSV has website URLs in column H (8th column)\n"
190
- error_msg += "2. Or have a column header named 'website', 'url', 'domain', etc.\n"
191
- error_msg += "3. Check that URLs are properly formatted (e.g., example.com or https://example.com)\n"
192
- error_msg += "4. Verify the CSV file is not corrupted\n"
193
- return error_msg, ""
194
-
195
- all_contacts = []
196
- processed_websites = []
197
-
198
- # Search each website
199
- for website in websites[:20]: # Limit to first 20 websites
200
- print(f"Processing website: {website}")
201
- contacts = simulate_website_scraping(website)
202
- if contacts:
203
- all_contacts.extend(contacts)
204
- processed_websites.append(website)
205
- print(f"Found {len(contacts)} contacts for {website}")
206
- else:
207
- print(f"No contacts found for {website}")
208
-
209
- # Remove duplicates based on email
210
- unique_contacts = []
211
- seen_emails = set()
212
- for contact in all_contacts:
213
- if contact['email'] not in seen_emails:
214
- unique_contacts.append(contact)
215
- seen_emails.add(contact['email'])
216
-
217
- # Limit results
218
- unique_contacts = unique_contacts[:max_results]
219
 
220
- if not unique_contacts:
221
- result_msg = f"No contacts found for the {len(websites)} websites from the CSV file.\n\n"
222
- result_msg += "DEBUG INFORMATION:\n" + debug_text + "\n\n"
223
- result_msg += f"Websites processed: {', '.join(websites[:10])}\n"
224
- result_msg += "This might be because the websites are not in our sample database."
225
- return result_msg, ""
226
-
227
- # Format results
228
- results_text = f"CONTACT DISCOVERY REPORT\n"
229
- results_text += f"CSV Processing Details:\n"
230
- results_text += f"Total Websites in CSV: {len(websites)}\n"
231
- results_text += f"Websites Processed: {len(processed_websites)}\n"
232
- results_text += f"Websites with Contacts: {len(processed_websites)}\n"
233
- results_text += f"Unique Contacts Found: {len(unique_contacts)}\n"
234
- results_text += f"Processed Websites: {', '.join(processed_websites)}\n"
235
- results_text += f"{'='*60}\n\n"
236
-
237
- # Add debug info
238
- results_text += "DEBUG INFORMATION:\n" + debug_text + "\n\n"
239
- results_text += f"{'='*60}\n\n"
240
-
241
- for i, contact in enumerate(unique_contacts, 1):
242
- results_text += f"CONTACT #{i}\n"
243
- results_text += f"Name: {contact['first_name']} {contact['last_name']}\n"
244
- results_text += f"Position: {contact['job_title']}\n"
245
- results_text += f"Email: {contact['email']}\n"
246
- results_text += f"Phone: {contact['phone']}\n"
247
- results_text += f"Company: {contact['company']}\n"
248
- results_text += f"Website: {contact['website']}\n\n"
249
-
250
- # Create CSV output
251
- csv_output = "First Name,Last Name,Job Title,Email,Phone,Company,Website\n"
252
- for contact in unique_contacts:
253
- csv_output += f"{contact['first_name']},{contact['last_name']},{contact['job_title']},{contact['email']},{contact['phone']},{contact['company']},{contact['website']}\n"
254
-
255
- return results_text, csv_output
256
-
257
- except Exception as e:
258
- return f"Error processing CSV file: {str(e)}", ""
259
-
260
- def search_website_contacts(website_url, max_results=10):
261
- """Main function to search for contacts on a website"""
262
- if not website_url:
263
- return "Please enter a website URL", ""
264
 
265
- # Clean up URL
266
- if not website_url.startswith(('http://', 'https://')):
267
- website_url = 'https://' + website_url
268
 
269
- try:
270
- # Simulate finding contacts
271
- contacts = simulate_website_scraping(website_url)
272
-
273
- if not contacts:
274
- return f"No contacts found on {website_url}. \n\nThis website is not in our contact database. Try one of the sample websites listed below, or the website might not have publicly available contact information.", ""
275
-
276
- # Limit results
277
- contacts = contacts[:max_results]
278
-
279
- # Format results
280
- results_text = f"CONTACT INTELLIGENCE REPORT\n"
281
- results_text += f"Website: {website_url}\n"
282
- results_text += f"Contacts Found: {len(contacts)}\n"
283
- results_text += f"{'='*60}\n\n"
284
-
285
- for i, contact in enumerate(contacts, 1):
286
- results_text += f"CONTACT #{i}\n"
287
- results_text += f"First Name: {contact['first_name']}\n"
288
- results_text += f"Last Name: {contact['last_name']}\n"
289
- results_text += f"Position: {contact['job_title']}\n"
290
- results_text += f"Email: {contact['email']}\n"
291
- results_text += f"Phone: {contact['phone']}\n"
292
- results_text += f"Company: {contact['company']}\n\n"
293
-
294
- # Create a simple table format for the second output
295
- table_text = "First Name,Last Name,Job Title,Email,Phone,Company\n"
296
- for contact in contacts:
297
- table_text += f"{contact['first_name']},{contact['last_name']},{contact['job_title']},{contact['email']},{contact['phone']},{contact['company']}\n"
298
-
299
- return results_text, table_text
300
-
301
- except Exception as e:
302
- return f"Error searching website: {str(e)}", ""
303
-
304
- def get_all_available_websites():
305
- """Return a static list of example websites for display"""
306
- return "\n".join([
307
- "kwekelcompanies.com",
308
- "techflowsolutions.com",
309
- "greenleafconsult.com",
310
- "blueskymarketing.net",
311
- "quantumdynamics.org",
312
- "stellarlogistics.biz",
313
- "nexusfinancial.pro",
314
- "horizonhealth.care",
315
- "phoenixmfg.com"
316
- ])
317
-
318
- # Custom CSS
319
- custom_css = """
320
- .gradio-container {
321
- background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
322
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
323
- }
324
-
325
- .main-header {
326
- background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 50%, #1e40af 100%);
327
- color: white;
328
- padding: 40px 20px;
329
- text-align: center;
330
- border-radius: 15px;
331
- margin-bottom: 30px;
332
- box-shadow: 0 10px 30px rgba(30, 58, 138, 0.3);
333
- }
334
-
335
- .main-header h1 {
336
- font-size: 2.5em;
337
- margin: 0;
338
- font-weight: 700;
339
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
340
- }
341
-
342
- .main-header p {
343
- font-size: 1.2em;
344
- margin: 15px 0 0 0;
345
- opacity: 0.9;
346
- }
347
-
348
- .corporate-card {
349
- background: white;
350
- border: 1px solid #d1d5db;
351
- border-radius: 12px;
352
- padding: 25px;
353
- margin: 15px 0;
354
- box-shadow: 0 4px 15px rgba(0,0,0,0.1);
355
- border-left: 5px solid #1e40af;
356
- }
357
-
358
- .tips-section {
359
- background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
360
- border: 2px solid #cbd5e1;
361
- border-radius: 15px;
362
- padding: 20px;
363
- margin: 10px 0;
364
- }
365
-
366
- .tips-section h3 {
367
- color: #1e40af;
368
- margin-top: 0;
369
- font-weight: 600;
370
- }
371
-
372
- .primary-btn {
373
- background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
374
- color: white;
375
- border: none;
376
- border-radius: 8px;
377
- padding: 15px 30px;
378
- font-weight: 600;
379
- font-size: 16px;
380
- transition: all 0.3s ease;
381
- }
382
 
383
- .primary-btn:hover {
384
- background: linear-gradient(135deg, #1e3a8a 0%, #2563eb 100%);
385
- transform: translateY(-2px);
386
- box-shadow: 0 6px 20px rgba(30, 64, 175, 0.4);
387
- }
388
 
389
- .secondary-btn {
390
- background: white;
391
- color: #374151;
392
- border: 2px solid #d1d5db;
393
- border-radius: 6px;
394
- padding: 8px 16px;
395
- font-weight: 500;
396
- transition: all 0.3s ease;
397
- }
398
 
399
- .secondary-btn:hover {
400
- border-color: #1e40af;
401
- color: #1e40af;
402
- background: #f8fafc;
403
- }
 
 
404
 
405
- .custom-input {
406
- border: 2px solid #d1d5db;
407
- border-radius: 8px;
408
- padding: 12px;
409
- font-size: 16px;
410
- transition: border-color 0.3s ease;
411
- }
412
 
413
- .custom-input:focus {
414
- border-color: #3b82f6;
415
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
416
- }
 
 
 
417
 
418
- .results-container {
419
- background: white;
420
- border: 1px solid #e5e7eb;
421
- border-radius: 10px;
422
- padding: 20px;
423
- margin: 15px 0;
424
- box-shadow: 0 2px 10px rgba(0,0,0,0.05);
425
- }
426
 
427
- .section-header {
428
- background: linear-gradient(135deg, #64748b 0%, #475569 100%);
429
- color: white;
430
- padding: 15px 20px;
431
- border-radius: 10px;
432
- margin: 20px 0 15px 0;
433
- font-weight: 600;
434
- }
435
- """
436
 
437
- # Create Gradio interface
438
- with gr.Blocks(css=custom_css, title="Contact Discovery Platform", theme=gr.themes.Base()) as app:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  gr.HTML("""
440
- <div class="main-header">
441
- <h1>Contact Discovery Platform</h1>
442
- <p>Professional Contact Discovery & Lead Generation Tool</p>
443
- <p style="font-size: 0.95em; opacity: 0.8;">Advanced website analysis for contact intelligence gathering</p>
444
  </div>
445
  """)
446
 
447
- with gr.Tabs():
448
- # Single Website Search Tab
449
- with gr.TabItem("Single Website Search"):
450
- with gr.Row():
451
- with gr.Column(scale=2):
452
- gr.HTML('<div class="section-header">Search Parameters</div>')
453
-
454
- website_input = gr.Textbox(
455
- label="Target Website URL",
456
- placeholder="Enter company website (e.g., techflowsolutions.com)",
457
- value="",
458
- elem_classes=["custom-input"]
459
- )
460
-
461
- with gr.Row():
462
- max_results = gr.Slider(
463
- minimum=1,
464
- maximum=20,
465
- value=8,
466
- step=1,
467
- label="Maximum Results",
468
- elem_classes=["custom-input"]
469
- )
470
-
471
- search_btn = gr.Button(
472
- "Execute Search",
473
- variant="primary",
474
- size="lg",
475
- elem_classes=["primary-btn"]
476
- )
477
-
478
- gr.HTML('<div class="section-header">Search Results</div>')
479
-
480
- with gr.Row():
481
- results_display = gr.Textbox(
482
- label="Contact Intelligence Report",
483
- lines=18,
484
- max_lines=35,
485
- show_copy_button=True,
486
- elem_classes=["results-container"]
487
- )
488
-
489
- csv_output = gr.Textbox(
490
- label="Export Data (CSV Format)",
491
- lines=18,
492
- max_lines=35,
493
- show_copy_button=True,
494
- elem_classes=["results-container"]
495
- )
496
-
497
- # CSV Upload Tab
498
- with gr.TabItem("CSV Bulk Search"):
499
- with gr.Row():
500
- with gr.Column(scale=2):
501
- gr.HTML('<div class="section-header">CSV Upload</div>')
502
-
503
- csv_file = gr.File(
504
- label="Upload CSV File",
505
- file_types=[".csv"],
506
- elem_classes=["custom-input"]
507
- )
508
-
509
- gr.HTML("""
510
- <div style="background: #f8fafc; padding: 15px; border-radius: 8px; border-left: 4px solid #1e40af; margin: 10px 0;">
511
- <strong>CSV Format - Multiple Options:</strong><br>
512
- <strong>Option 1:</strong> Website URLs in <strong>Column H (8th column)</strong><br>
513
- <strong>Option 2:</strong> Column header named 'website', 'url', 'domain', 'site', or 'web'<br>
514
- <strong>Option 3:</strong> System will auto-detect columns with valid URLs<br><br>
515
- <strong>Examples:</strong> techflowsolutions.com, https://example.com, www.company.com<br>
516
- <strong>Note:</strong> The system will show detailed debugging information about your CSV structure
517
- </div>
518
- """)
519
-
520
- with gr.Row():
521
- csv_max_results = gr.Slider(
522
- minimum=1,
523
- maximum=50,
524
- value=20,
525
- step=1,
526
- label="Maximum Results",
527
- elem_classes=["custom-input"]
528
- )
529
-
530
- csv_search_btn = gr.Button(
531
- "Process CSV",
532
- variant="primary",
533
- size="lg",
534
- elem_classes=["primary-btn"]
535
- )
536
 
537
- gr.HTML('<div class="section-header">CSV Results</div>')
 
 
 
 
 
538
 
539
  with gr.Row():
540
- csv_results_display = gr.Textbox(
541
- label="CSV Processing Report",
542
- lines=18,
543
- max_lines=35,
544
- show_copy_button=True,
545
- elem_classes=["results-container"]
546
- )
547
-
548
- csv_export_output = gr.Textbox(
549
- label="Export Data (CSV Format)",
550
- lines=18,
551
- max_lines=35,
552
- show_copy_button=True,
553
- elem_classes=["results-container"]
554
- )
555
-
556
- # Sample websites section
557
- with gr.Accordion("Sample Websites Database", open=False):
558
- gr.HTML('<div style="background: #f8fafc; padding: 15px; border-radius: 8px; border-left: 4px solid #1e40af;">')
559
- sample_websites = gr.Textbox(
560
- label="Available Websites in Database",
561
- value=get_all_available_websites(),
562
- lines=8,
563
- interactive=False,
564
- elem_classes=["custom-input"]
565
- )
566
- gr.HTML('</div>')
567
-
568
- # Quick search buttons
569
- gr.HTML('<div class="section-header">Quick Access Sample Websites</div>')
570
 
571
  with gr.Row():
572
- quick_btn1 = gr.Button("TechFlow Solutions", size="sm", elem_classes=["secondary-btn"])
573
- quick_btn2 = gr.Button("GreenLeaf Consulting", size="sm", elem_classes=["secondary-btn"])
574
- quick_btn3 = gr.Button("BlueSky Marketing", size="sm", elem_classes=["secondary-btn"])
575
- quick_btn4 = gr.Button("Quantum Dynamics", size="sm", elem_classes=["secondary-btn"])
576
-
577
- with gr.Row():
578
- quick_btn5 = gr.Button("Stellar Logistics", size="sm", elem_classes=["secondary-btn"])
579
- quick_btn6 = gr.Button("Nexus Financial", size="sm", elem_classes=["secondary-btn"])
580
- quick_btn7 = gr.Button("Horizon Health", size="sm", elem_classes=["secondary-btn"])
581
- quick_btn8 = gr.Button("Phoenix Manufacturing", size="sm", elem_classes=["secondary-btn"])
 
 
 
 
582
 
583
  # Event handlers
584
  search_btn.click(
585
- fn=search_website_contacts,
586
- inputs=[website_input, max_results],
587
- outputs=[results_display, csv_output]
588
  )
589
 
590
- csv_search_btn.click(
591
- fn=search_csv_websites,
592
- inputs=[csv_file, csv_max_results],
593
- outputs=[csv_results_display, csv_export_output]
594
  )
595
 
596
- # Quick search button handlers
597
- quick_btn1.click(lambda: "techflowsolutions.com", outputs=website_input)
598
- quick_btn2.click(lambda: "greenleafconsult.com", outputs=website_input)
599
- quick_btn3.click(lambda: "blueskymarketing.net", outputs=website_input)
600
- quick_btn4.click(lambda: "quantumdynamics.org", outputs=website_input)
601
- quick_btn5.click(lambda: "stellarlogistics.biz", outputs=website_input)
602
- quick_btn6.click(lambda: "nexusfinancial.pro", outputs=website_input)
603
- quick_btn7.click(lambda: "horizonhealth.care", outputs=website_input)
604
- quick_btn8.click(lambda: "phoenixmfg.com", outputs=website_input)
605
-
606
- # Examples
607
- gr.Examples(
608
- examples=[
609
- ["techflowsolutions.com", 5],
610
- ["greenleafconsult.com", 3],
611
- ["blueskymarketing.net", 4],
612
- ["quantumdynamics.org", 6]
613
- ],
614
- inputs=[website_input, max_results],
615
- label="Sample Searches"
616
  )
617
 
618
  # Footer
619
  gr.HTML("""
620
- <div style="text-align: center; padding: 30px 20px; background: linear-gradient(135deg, #64748b 0%, #475569 100%); color: white; border-radius: 15px; margin-top: 30px;">
621
- <h3 style="margin: 0 0 10px 0;">Contact Intelligence Platform</h3>
622
- <p style="margin: 0; opacity: 0.9;">Professional-grade contact discovery and lead generation technology</p>
623
- <p style="margin: 10px 0 0 0; font-size: 0.9em; opacity: 0.7;">Powered by advanced web intelligence algorithms</p>
624
  </div>
625
  """)
626
 
 
627
  if __name__ == "__main__":
628
- app.launch()
 
 
 
 
 
 
 
1
  import gradio as gr
2
  import requests
 
3
  import json
4
+ from typing import List, Dict, Any
5
+ import pandas as pd
 
 
 
6
 
7
+ # Professional color scheme inspired by corporate/real estate companies
8
+ theme = gr.themes.Soft(
9
+ primary_hue="blue",
10
+ secondary_hue="slate",
11
+ neutral_hue="slate",
12
+ text_size="md",
13
+ spacing_size="md",
14
+ radius_size="md"
15
+ ).set(
16
+ body_background_fill="#f8fafc",
17
+ body_background_fill_dark="#1e293b",
18
+ block_background_fill="#ffffff",
19
+ block_background_fill_dark="#334155",
20
+ block_border_color="#e2e8f0",
21
+ block_border_color_dark="#475569",
22
+ input_background_fill="#ffffff",
23
+ input_background_fill_dark="#334155",
24
+ input_border_color="#cbd5e1",
25
+ input_border_color_dark="#64748b",
26
+ button_primary_background_fill="#1e40af",
27
+ button_primary_background_fill_hover="#1d4ed8",
28
+ button_primary_text_color="#ffffff",
29
+ button_secondary_background_fill="#f1f5f9",
30
+ button_secondary_background_fill_hover="#e2e8f0",
31
+ button_secondary_text_color="#334155"
32
+ )
33
 
34
+ def search_contacts(url: str, api_endpoint: str = None) -> tuple[str, str]:
35
+ """
36
+ Search for contacts associated with a given URL using the Hugging Face API.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
+ Args:
39
+ url: The website URL to search for contacts
40
+ api_endpoint: Your Hugging Face API endpoint URL
41
+
42
+ Returns:
43
+ Tuple of (formatted_results, raw_json)
44
+ """
 
 
45
 
46
+ if not url.strip():
47
+ return "❌ Please enter a website URL", ""
 
 
 
 
 
48
 
49
+ if not api_endpoint or not api_endpoint.strip():
50
+ return "❌ Please provide your Hugging Face API endpoint URL", ""
 
51
 
52
+ # Clean and validate URL
53
+ url = url.strip()
54
+ if not url.startswith(('http://', 'https://')):
55
+ url = 'https://' + url
 
 
56
 
57
  try:
58
+ # Prepare the API request
59
+ headers = {
60
+ 'Content-Type': 'application/json',
61
+ 'Authorization': f'Bearer {api_endpoint.split("/")[-1]}' # Adjust based on your API auth
62
+ }
63
+
64
+ payload = {
65
+ "inputs": url,
66
+ "parameters": {
67
+ "task": "contact_search",
68
+ "url": url
69
+ }
70
+ }
71
+
72
+ # Make the API request
73
+ response = requests.post(
74
+ api_endpoint,
75
+ headers=headers,
76
+ json=payload,
77
+ timeout=30
78
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ if response.status_code == 200:
81
+ try:
82
+ result = response.json()
83
+
84
+ # Format the results for display
85
+ formatted_output = format_contact_results(result, url)
86
+ raw_json = json.dumps(result, indent=2)
87
+
88
+ return formatted_output, raw_json
89
+
90
+ except json.JSONDecodeError:
91
+ return f"❌ Error: Invalid JSON response from API", response.text
92
+
93
  else:
94
+ return f"❌ API Error ({response.status_code}): {response.text}", ""
95
+
96
+ except requests.exceptions.Timeout:
97
+ return "❌ Request timeout. Please try again.", ""
98
+ except requests.exceptions.ConnectionError:
99
+ return "❌ Connection error. Please check your API endpoint.", ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  except Exception as e:
101
+ return f"❌ Error: {str(e)}", ""
 
102
 
103
+ def format_contact_results(results: Dict[Any, Any], url: str) -> str:
104
+ """
105
+ Format the API results into a readable format.
 
106
 
107
+ Args:
108
+ results: The JSON response from the API
109
+ url: The searched URL
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ Returns:
112
+ Formatted string with contact information
113
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
+ output = f"# πŸ” Contact Search Results for: {url}\n\n"
 
 
116
 
117
+ # Handle different possible result structures
118
+ if isinstance(results, dict):
119
+ if 'contacts' in results:
120
+ contacts = results['contacts']
121
+ elif 'results' in results:
122
+ contacts = results['results']
123
+ elif 'data' in results:
124
+ contacts = results['data']
125
+ else:
126
+ contacts = results
127
+
128
+ if isinstance(contacts, list) and len(contacts) > 0:
129
+ output += f"**Found {len(contacts)} contact(s):**\n\n"
130
+
131
+ for i, contact in enumerate(contacts, 1):
132
+ output += f"## πŸ‘€ Contact {i}\n"
133
+
134
+ # Handle different contact field names
135
+ name = contact.get('name') or contact.get('full_name') or contact.get('contact_name') or "N/A"
136
+ email = contact.get('email') or contact.get('email_address') or "N/A"
137
+ title = contact.get('title') or contact.get('job_title') or contact.get('position') or "N/A"
138
+ company = contact.get('company') or contact.get('organization') or "N/A"
139
+ phone = contact.get('phone') or contact.get('phone_number') or "N/A"
140
+ linkedin = contact.get('linkedin') or contact.get('linkedin_url') or "N/A"
141
+
142
+ output += f"- **Name:** {name}\n"
143
+ output += f"- **Email:** {email}\n"
144
+ output += f"- **Title:** {title}\n"
145
+ output += f"- **Company:** {company}\n"
146
+ output += f"- **Phone:** {phone}\n"
147
+ output += f"- **LinkedIn:** {linkedin}\n\n"
148
+
149
+ # Add any additional fields
150
+ additional_fields = {k: v for k, v in contact.items()
151
+ if k not in ['name', 'full_name', 'contact_name', 'email', 'email_address',
152
+ 'title', 'job_title', 'position', 'company', 'organization',
153
+ 'phone', 'phone_number', 'linkedin', 'linkedin_url']}
154
+
155
+ if additional_fields:
156
+ output += "**Additional Information:**\n"
157
+ for key, value in additional_fields.items():
158
+ output += f"- **{key.replace('_', ' ').title()}:** {value}\n"
159
+ output += "\n"
160
+
161
+ output += "---\n\n"
162
+
163
+ else:
164
+ output += "❌ No contacts found for this URL.\n\n"
165
+
166
+ elif isinstance(results, list):
167
+ if len(results) > 0:
168
+ output += f"**Found {len(results)} contact(s):**\n\n"
169
+ for i, contact in enumerate(results, 1):
170
+ output += f"## πŸ‘€ Contact {i}\n"
171
+ output += f"{contact}\n\n"
172
+ else:
173
+ output += "❌ No contacts found for this URL.\n\n"
174
+ else:
175
+ output += f"**Result:** {results}\n\n"
176
+
177
+ output += f"*Search completed at: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}*"
178
+
179
+ return output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
+ def create_sample_data():
182
+ """Create sample data for demonstration"""
183
+ return '''# πŸ” Contact Search Results for: example.com
 
 
184
 
185
+ **Found 2 contact(s):**
 
 
 
 
 
 
 
 
186
 
187
+ ## πŸ‘€ Contact 1
188
+ - **Name:** John Smith
189
+ - **Email:** john.smith@example.com
190
+ - **Title:** CEO
191
+ - **Company:** Example Corp
192
+ - **Phone:** (555) 123-4567
193
+ - **LinkedIn:** linkedin.com/in/johnsmith
194
 
195
+ ---
 
 
 
 
 
 
196
 
197
+ ## πŸ‘€ Contact 2
198
+ - **Name:** Jane Doe
199
+ - **Email:** jane.doe@example.com
200
+ - **Title:** VP of Sales
201
+ - **Company:** Example Corp
202
+ - **Phone:** (555) 123-4568
203
+ - **LinkedIn:** linkedin.com/in/janedoe
204
 
205
+ ---
 
 
 
 
 
 
 
206
 
207
+ *Search completed at: 2025-01-15 10:30:00*'''
 
 
 
 
 
 
 
 
208
 
209
+ # Create the Gradio interface
210
+ with gr.Blocks(theme=theme, title="Contact Search - Kwekel Companies", css="""
211
+ .gradio-container {
212
+ max-width: 1200px !important;
213
+ margin: 0 auto !important;
214
+ }
215
+ .header {
216
+ background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
217
+ color: white;
218
+ padding: 2rem;
219
+ border-radius: 0.5rem;
220
+ margin-bottom: 2rem;
221
+ text-align: center;
222
+ }
223
+ .header h1 {
224
+ margin: 0;
225
+ font-size: 2.5rem;
226
+ font-weight: 700;
227
+ margin-bottom: 0.5rem;
228
+ }
229
+ .header p {
230
+ margin: 0;
231
+ font-size: 1.2rem;
232
+ opacity: 0.9;
233
+ }
234
+ .info-box {
235
+ background: #f0f9ff;
236
+ border: 1px solid #0ea5e9;
237
+ border-radius: 0.5rem;
238
+ padding: 1rem;
239
+ margin: 1rem 0;
240
+ }
241
+ .footer {
242
+ text-align: center;
243
+ margin-top: 2rem;
244
+ padding-top: 2rem;
245
+ border-top: 1px solid #e2e8f0;
246
+ color: #64748b;
247
+ }
248
+ """) as demo:
249
+
250
+ # Header
251
  gr.HTML("""
252
+ <div class="header">
253
+ <h1>πŸ” Contact Search Tool</h1>
254
+ <p>Professional contact discovery powered by AI</p>
 
255
  </div>
256
  """)
257
 
258
+ # Info box
259
+ gr.HTML("""
260
+ <div class="info-box">
261
+ <h3>πŸ“‹ Instructions:</h3>
262
+ <ol>
263
+ <li>Enter your Hugging Face API endpoint URL in the first field</li>
264
+ <li>Enter the website URL you want to search for contacts</li>
265
+ <li>Click "Search Contacts" to get results</li>
266
+ <li>View formatted results and raw JSON response</li>
267
+ </ol>
268
+ </div>
269
+ """)
270
+
271
+ with gr.Row():
272
+ with gr.Column(scale=2):
273
+ api_endpoint = gr.Textbox(
274
+ label="πŸ”— Hugging Face API Endpoint",
275
+ placeholder="https://your-model-endpoint.hf.space/api/v1/predict",
276
+ info="Enter your Hugging Face API endpoint URL",
277
+ lines=1
278
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
+ url_input = gr.Textbox(
281
+ label="🌐 Website URL",
282
+ placeholder="example.com or https://example.com",
283
+ info="Enter the website URL to search for contacts",
284
+ lines=1
285
+ )
286
 
287
  with gr.Row():
288
+ search_btn = gr.Button("πŸ” Search Contacts", variant="primary", scale=2)
289
+ demo_btn = gr.Button("πŸ“‹ Show Demo", variant="secondary", scale=1)
290
+ clear_btn = gr.Button("πŸ—‘οΈ Clear", variant="secondary", scale=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
  with gr.Row():
293
+ with gr.Column(scale=1):
294
+ results_output = gr.Markdown(
295
+ label="πŸ“Š Contact Results",
296
+ value="Enter a URL and click 'Search Contacts' to see results here.",
297
+ height=400
298
+ )
299
+
300
+ with gr.Column(scale=1):
301
+ json_output = gr.Code(
302
+ label="πŸ“ Raw JSON Response",
303
+ language="json",
304
+ value="",
305
+ height=400
306
+ )
307
 
308
  # Event handlers
309
  search_btn.click(
310
+ fn=search_contacts,
311
+ inputs=[url_input, api_endpoint],
312
+ outputs=[results_output, json_output]
313
  )
314
 
315
+ demo_btn.click(
316
+ fn=lambda: (create_sample_data(), '{"contacts": [{"name": "John Smith", "email": "john.smith@example.com", "title": "CEO"}]}'),
317
+ outputs=[results_output, json_output]
 
318
  )
319
 
320
+ clear_btn.click(
321
+ fn=lambda: ("", "", ""),
322
+ outputs=[url_input, results_output, json_output]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  )
324
 
325
  # Footer
326
  gr.HTML("""
327
+ <div class="footer">
328
+ <p>Β© 2025 Contact Search Tool - Professional contact discovery solution</p>
 
 
329
  </div>
330
  """)
331
 
332
+ # Launch the app
333
  if __name__ == "__main__":
334
+ demo.launch(
335
+ share=True,
336
+ server_name="0.0.0.0",
337
+ server_port=7860,
338
+ show_error=True,
339
+ debug=True
340
+ )