JayBene1 commited on
Commit
9ebdf6f
·
verified ·
1 Parent(s): 37db499

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +227 -68
app.py CHANGED
@@ -4,9 +4,10 @@ import json
4
  from typing import List, Dict, Any
5
  import pandas as pd
6
 
 
7
  API_ENDPOINT = "https://jaybene1-testapicontacts.hf.space/contacts"
8
 
9
- # Updated dark theme with white text
10
  theme = gr.themes.Soft(
11
  primary_hue="blue",
12
  secondary_hue="slate",
@@ -15,54 +16,96 @@ theme = gr.themes.Soft(
15
  spacing_size="md",
16
  radius_size="md"
17
  ).set(
18
- body_background_fill="#0f172a",
19
- body_background_fill_dark="#0f172a",
20
- block_background_fill="#1e293b",
21
- block_background_fill_dark="#1e293b",
22
- block_border_color="#334155",
23
- block_border_color_dark="#334155",
24
- input_background_fill="#1e293b",
25
- input_background_fill_dark="#1e293b",
26
- input_border_color="#475569",
27
- input_border_color_dark="#475569",
28
  button_primary_background_fill="#1e40af",
29
  button_primary_background_fill_hover="#1d4ed8",
30
  button_primary_text_color="#ffffff",
31
- button_secondary_background_fill="#334155",
32
- button_secondary_background_fill_hover="#475569",
33
- button_secondary_text_color="#f8fafc",
34
- text_color="#f8fafc",
35
- text_color_dark="#f8fafc"
36
  )
37
 
38
  def search_contacts(url: str) -> tuple[str, str]:
 
 
 
 
 
 
 
 
 
 
39
  if not url.strip():
40
  return "❌ Please enter a website URL", ""
 
 
41
  url = url.strip()
42
  if not url.startswith(('http://', 'https://')):
43
  url = 'https://' + url
44
-
45
  try:
46
- response = requests.get(API_ENDPOINT, params={'url': url}, timeout=30)
 
 
 
 
 
 
 
 
 
 
 
47
  if response.status_code == 200:
48
- result = response.json()
49
- request_url = f"{API_ENDPOINT}?url={url}"
50
- actual_result = result.get('data', [])[0] if isinstance(result, dict) and 'data' in result else result
51
- if isinstance(actual_result, list):
52
- actual_result = filter_contacts_by_url(actual_result, url)
53
- elif isinstance(actual_result, dict) and 'contacts' in actual_result:
54
- actual_result['contacts'] = filter_contacts_by_url(actual_result['contacts'], url)
55
- formatted_output = format_contact_results(actual_result, url)
56
- formatted_output += (
57
- f"\n\n**Debug Info:**\n"
58
- f"- Request URL: {API_ENDPOINT}\n"
59
- f"- Search URL: {url}\n"
60
- f"- Response contains {len(actual_result) if isinstance(actual_result, list) else 'N/A'} items\n"
61
- f"- Client-side filtering applied\n"
62
- )
63
- return formatted_output, json.dumps(result, indent=2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  else:
65
  return f"❌ API Error ({response.status_code}): {response.text}", ""
 
66
  except requests.exceptions.Timeout:
67
  return "❌ Request timeout. Please try again.", ""
68
  except requests.exceptions.ConnectionError:
@@ -71,12 +114,21 @@ def search_contacts(url: str) -> tuple[str, str]:
71
  return f"❌ Error: {str(e)}", ""
72
 
73
  def filter_contacts_by_url(contacts, search_url):
 
 
 
 
74
  if not isinstance(contacts, list):
75
  return contacts
 
 
76
  search_domain = search_url.replace('https://', '').replace('http://', '').replace('www.', '').split('/')[0].lower()
 
77
  filtered_contacts = []
78
  for contact in contacts:
 
79
  if isinstance(contact, dict):
 
80
  fields_to_check = [
81
  contact.get('website', ''),
82
  contact.get('company_website', ''),
@@ -86,54 +138,126 @@ def filter_contacts_by_url(contacts, search_url):
86
  str(contact.get('url', '')),
87
  str(contact.get('source', ''))
88
  ]
 
 
89
  for field in fields_to_check:
90
  if field and search_domain in str(field).lower():
91
  filtered_contacts.append(contact)
92
  break
 
93
  return filtered_contacts if filtered_contacts else contacts
94
 
95
  def format_contact_results(results: Dict[Any, Any], url: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
96
  output = f"# Contact Search Results for: {url}\n\n"
 
 
97
  if isinstance(results, dict):
98
- contacts = results.get('contacts') or results.get('results') or results.get('data') or results
99
- if isinstance(contacts, list) and contacts:
 
 
 
 
 
 
 
 
100
  output += f"**Found {len(contacts)} contact(s):**\n\n"
 
101
  for i, contact in enumerate(contacts, 1):
102
  output += f"## Contact {i}\n"
 
 
103
  name = contact.get('name') or contact.get('full_name') or contact.get('contact_name') or "N/A"
104
  email = contact.get('email') or contact.get('email_address') or "N/A"
105
  title = contact.get('title') or contact.get('job_title') or contact.get('position') or "N/A"
106
  company = contact.get('company') or contact.get('organization') or "N/A"
107
  phone = contact.get('phone') or contact.get('phone_number') or "N/A"
108
  linkedin = contact.get('linkedin') or contact.get('linkedin_url') or "N/A"
109
- output += f"- **Name:** {name}\n- **Email:** {email}\n- **Title:** {title}\n- **Company:** {company}\n- **Phone:** {phone}\n- **LinkedIn:** {linkedin}\n\n"
110
- 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']}
 
 
 
 
 
 
 
 
 
 
 
 
111
  if additional_fields:
112
  output += "**Additional Information:**\n"
113
  for key, value in additional_fields.items():
114
  output += f"- **{key.replace('_', ' ').title()}:** {value}\n"
115
  output += "\n"
 
116
  output += "---\n\n"
 
117
  else:
118
  output += "No contacts found for this URL.\n\n"
 
119
  elif isinstance(results, list):
120
- if results:
121
  output += f"**Found {len(results)} contact(s):**\n\n"
122
  for i, contact in enumerate(results, 1):
123
- output += f"## Contact {i}\n{contact}\n\n"
 
124
  else:
125
  output += "No contacts found for this URL.\n\n"
126
  else:
127
  output += f"**Result:** {results}\n\n"
 
128
  output += f"*Search completed at: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}*"
 
129
  return output
130
 
131
- with gr.Blocks(theme=theme, title="Kwekel Companies Contact Search Tool", css="""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  .gradio-container {
133
  max-width: 1200px !important;
134
  margin: 0 auto !important;
135
- background-color: #0f172a !important;
136
- color: #f8fafc !important;
137
  }
138
  .header {
139
  background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
@@ -147,68 +271,103 @@ with gr.Blocks(theme=theme, title="Kwekel Companies Contact Search Tool", css=""
147
  margin: 0;
148
  font-size: 2.5rem;
149
  font-weight: 700;
150
- color: #ffffff;
 
 
 
 
 
151
  }
152
  .info-box {
153
- background: #1e293b;
154
- border: 1px solid #3b82f6;
155
  border-radius: 0.5rem;
156
  padding: 1rem;
157
  margin: 1rem 0;
158
- color: #f8fafc;
159
- }
160
- label, h3, p, li, ol {
161
- color: #f8fafc !important;
162
  }
163
  .footer {
164
  text-align: center;
165
  margin-top: 2rem;
166
  padding-top: 2rem;
167
- border-top: 1px solid #475569;
168
- color: #94a3b8 !important;
169
  }
170
  """) as demo:
171
 
 
172
  gr.HTML("""
173
  <div class="header">
174
- <h1>Kwekel Companies Contact Search Tool</h1>
 
175
  </div>
176
  """)
177
-
 
178
  gr.HTML(f"""
179
  <div class="info-box">
180
  <h3>Instructions:</h3>
181
  <ol>
182
- <li>Enter the website URL you want to search for contacts.</li>
183
- <li>Click "Search Contacts" to get results.</li>
184
- <li>View formatted results and raw JSON response.</li>
185
  </ol>
186
  <p><strong>API Endpoint:</strong> {API_ENDPOINT}</p>
187
  </div>
188
  """)
189
-
190
  with gr.Row():
191
  with gr.Column(scale=2):
192
- url_input = gr.Textbox(label="Website URL", placeholder="example.com or https://example.com", lines=1)
 
 
 
 
 
 
193
  with gr.Row():
194
  search_btn = gr.Button("Search Contacts", variant="primary", scale=2)
 
195
  clear_btn = gr.Button("Clear", variant="secondary", scale=1)
196
-
197
  with gr.Row():
198
  with gr.Column(scale=1):
199
- results_output = gr.Markdown(label="Contact Results", value="Enter a URL and click 'Search Contacts' to see results here.")
 
 
 
 
200
  with gr.Column(scale=1):
201
- json_output = gr.Code(label="Raw JSON Response", language="json", value="")
202
-
203
- search_btn.click(fn=search_contacts, inputs=[url_input], outputs=[results_output, json_output])
204
- clear_btn.click(fn=lambda: ("", ""), outputs=[url_input, results_output])
205
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  gr.HTML("""
207
  <div class="footer">
208
- <p>© 2025 Kwekel Companies Contact Search Tool</p>
209
  </div>
210
  """)
211
 
 
212
  if __name__ == "__main__":
213
  demo.launch(
214
  share=True,
@@ -216,4 +375,4 @@ if __name__ == "__main__":
216
  server_port=7860,
217
  show_error=True,
218
  debug=True
219
- )
 
4
  from typing import List, Dict, Any
5
  import pandas as pd
6
 
7
+ # Hardcoded API endpoint - Updated to correct endpoint
8
  API_ENDPOINT = "https://jaybene1-testapicontacts.hf.space/contacts"
9
 
10
+ # Professional color scheme inspired by corporate/real estate companies
11
  theme = gr.themes.Soft(
12
  primary_hue="blue",
13
  secondary_hue="slate",
 
16
  spacing_size="md",
17
  radius_size="md"
18
  ).set(
19
+ body_background_fill="#f8fafc",
20
+ body_background_fill_dark="#1e293b",
21
+ block_background_fill="#ffffff",
22
+ block_background_fill_dark="#334155",
23
+ block_border_color="#e2e8f0",
24
+ block_border_color_dark="#475569",
25
+ input_background_fill="#ffffff",
26
+ input_background_fill_dark="#334155",
27
+ input_border_color="#cbd5e1",
28
+ input_border_color_dark="#64748b",
29
  button_primary_background_fill="#1e40af",
30
  button_primary_background_fill_hover="#1d4ed8",
31
  button_primary_text_color="#ffffff",
32
+ button_secondary_background_fill="#f1f5f9",
33
+ button_secondary_background_fill_hover="#e2e8f0",
34
+ button_secondary_text_color="#334155"
 
 
35
  )
36
 
37
  def search_contacts(url: str) -> tuple[str, str]:
38
+ """
39
+ Search for contacts associated with a given URL using the Hugging Face API.
40
+
41
+ Args:
42
+ url: The website URL to search for contacts
43
+
44
+ Returns:
45
+ Tuple of (formatted_results, raw_json)
46
+ """
47
+
48
  if not url.strip():
49
  return "❌ Please enter a website URL", ""
50
+
51
+ # Clean and validate URL
52
  url = url.strip()
53
  if not url.startswith(('http://', 'https://')):
54
  url = 'https://' + url
55
+
56
  try:
57
+ # Use GET request with URL parameter
58
+ params = {
59
+ 'url': url
60
+ }
61
+
62
+ # Make the API request to the hardcoded endpoint using GET
63
+ response = requests.get(
64
+ API_ENDPOINT,
65
+ params=params,
66
+ timeout=30
67
+ )
68
+
69
  if response.status_code == 200:
70
+ try:
71
+ result = response.json()
72
+
73
+ # Add debugging info
74
+ request_url = f"{API_ENDPOINT}?url={url}"
75
+
76
+ # Hugging Face Spaces API returns data in a different format
77
+ if isinstance(result, dict) and 'data' in result:
78
+ actual_result = result['data'][0] if result['data'] else {}
79
+ else:
80
+ actual_result = result
81
+
82
+ # Apply client-side filtering
83
+ if isinstance(actual_result, list):
84
+ actual_result = filter_contacts_by_url(actual_result, url)
85
+ elif isinstance(actual_result, dict) and 'contacts' in actual_result:
86
+ actual_result['contacts'] = filter_contacts_by_url(actual_result['contacts'], url)
87
+
88
+ # Format the results for display
89
+ formatted_output = format_contact_results(actual_result, url)
90
+
91
+ # Add debugging info to the formatted output
92
+ debug_info = f"\n\n**Debug Info:**\n"
93
+ debug_info += f"- Request URL: {API_ENDPOINT}\n"
94
+ debug_info += f"- Search URL: {url}\n"
95
+ debug_info += f"- Response contains {len(actual_result) if isinstance(actual_result, list) else 'N/A'} items\n"
96
+ debug_info += f"- Client-side filtering applied\n"
97
+
98
+ formatted_output += debug_info
99
+ raw_json = json.dumps(result, indent=2)
100
+
101
+ return formatted_output, raw_json
102
+
103
+ except json.JSONDecodeError:
104
+ return f"❌ Error: Invalid JSON response from API", response.text
105
+
106
  else:
107
  return f"❌ API Error ({response.status_code}): {response.text}", ""
108
+
109
  except requests.exceptions.Timeout:
110
  return "❌ Request timeout. Please try again.", ""
111
  except requests.exceptions.ConnectionError:
 
114
  return f"❌ Error: {str(e)}", ""
115
 
116
  def filter_contacts_by_url(contacts, search_url):
117
+ """
118
+ Filter contacts based on the search URL.
119
+ This is a client-side filter in case the API doesn't filter properly.
120
+ """
121
  if not isinstance(contacts, list):
122
  return contacts
123
+
124
+ # Extract domain from search URL
125
  search_domain = search_url.replace('https://', '').replace('http://', '').replace('www.', '').split('/')[0].lower()
126
+
127
  filtered_contacts = []
128
  for contact in contacts:
129
+ # Check if contact is associated with the search domain
130
  if isinstance(contact, dict):
131
+ # Check various fields that might contain the domain
132
  fields_to_check = [
133
  contact.get('website', ''),
134
  contact.get('company_website', ''),
 
138
  str(contact.get('url', '')),
139
  str(contact.get('source', ''))
140
  ]
141
+
142
+ # Check if any field contains the search domain
143
  for field in fields_to_check:
144
  if field and search_domain in str(field).lower():
145
  filtered_contacts.append(contact)
146
  break
147
+
148
  return filtered_contacts if filtered_contacts else contacts
149
 
150
  def format_contact_results(results: Dict[Any, Any], url: str) -> str:
151
+ """
152
+ Format the API results into a readable format.
153
+
154
+ Args:
155
+ results: The JSON response from the API
156
+ url: The searched URL
157
+
158
+ Returns:
159
+ Formatted string with contact information
160
+ """
161
+
162
  output = f"# Contact Search Results for: {url}\n\n"
163
+
164
+ # Handle different possible result structures
165
  if isinstance(results, dict):
166
+ if 'contacts' in results:
167
+ contacts = results['contacts']
168
+ elif 'results' in results:
169
+ contacts = results['results']
170
+ elif 'data' in results:
171
+ contacts = results['data']
172
+ else:
173
+ contacts = results
174
+
175
+ if isinstance(contacts, list) and len(contacts) > 0:
176
  output += f"**Found {len(contacts)} contact(s):**\n\n"
177
+
178
  for i, contact in enumerate(contacts, 1):
179
  output += f"## Contact {i}\n"
180
+
181
+ # Handle different contact field names
182
  name = contact.get('name') or contact.get('full_name') or contact.get('contact_name') or "N/A"
183
  email = contact.get('email') or contact.get('email_address') or "N/A"
184
  title = contact.get('title') or contact.get('job_title') or contact.get('position') or "N/A"
185
  company = contact.get('company') or contact.get('organization') or "N/A"
186
  phone = contact.get('phone') or contact.get('phone_number') or "N/A"
187
  linkedin = contact.get('linkedin') or contact.get('linkedin_url') or "N/A"
188
+
189
+ output += f"- **Name:** {name}\n"
190
+ output += f"- **Email:** {email}\n"
191
+ output += f"- **Title:** {title}\n"
192
+ output += f"- **Company:** {company}\n"
193
+ output += f"- **Phone:** {phone}\n"
194
+ output += f"- **LinkedIn:** {linkedin}\n\n"
195
+
196
+ # Add any additional fields
197
+ additional_fields = {k: v for k, v in contact.items()
198
+ if k not in ['name', 'full_name', 'contact_name', 'email', 'email_address',
199
+ 'title', 'job_title', 'position', 'company', 'organization',
200
+ 'phone', 'phone_number', 'linkedin', 'linkedin_url']}
201
+
202
  if additional_fields:
203
  output += "**Additional Information:**\n"
204
  for key, value in additional_fields.items():
205
  output += f"- **{key.replace('_', ' ').title()}:** {value}\n"
206
  output += "\n"
207
+
208
  output += "---\n\n"
209
+
210
  else:
211
  output += "No contacts found for this URL.\n\n"
212
+
213
  elif isinstance(results, list):
214
+ if len(results) > 0:
215
  output += f"**Found {len(results)} contact(s):**\n\n"
216
  for i, contact in enumerate(results, 1):
217
+ output += f"## Contact {i}\n"
218
+ output += f"{contact}\n\n"
219
  else:
220
  output += "No contacts found for this URL.\n\n"
221
  else:
222
  output += f"**Result:** {results}\n\n"
223
+
224
  output += f"*Search completed at: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}*"
225
+
226
  return output
227
 
228
+ def create_sample_data():
229
+ """Create sample data for demonstration"""
230
+ return '''# Contact Search Results for: example.com
231
+
232
+ **Found 2 contact(s):**
233
+
234
+ ## Contact 1
235
+ - **Name:** John Smith
236
+ - **Email:** john.smith@example.com
237
+ - **Title:** CEO
238
+ - **Company:** Example Corp
239
+ - **Phone:** (555) 123-4567
240
+ - **LinkedIn:** linkedin.com/in/johnsmith
241
+
242
+ ---
243
+
244
+ ## Contact 2
245
+ - **Name:** Jane Doe
246
+ - **Email:** jane.doe@example.com
247
+ - **Title:** VP of Sales
248
+ - **Company:** Example Corp
249
+ - **Phone:** (555) 123-4568
250
+ - **LinkedIn:** linkedin.com/in/janedoe
251
+
252
+ ---
253
+
254
+ *Search completed at: 2025-01-15 10:30:00*'''
255
+
256
+ # Create the Gradio interface
257
+ with gr.Blocks(theme=theme, title="Contact Search - Kwekel Companies", css="""
258
  .gradio-container {
259
  max-width: 1200px !important;
260
  margin: 0 auto !important;
 
 
261
  }
262
  .header {
263
  background: linear-gradient(135deg, #1e40af 0%, #3b82f6 100%);
 
271
  margin: 0;
272
  font-size: 2.5rem;
273
  font-weight: 700;
274
+ margin-bottom: 0.5rem;
275
+ }
276
+ .header p {
277
+ margin: 0;
278
+ font-size: 1.2rem;
279
+ opacity: 0.9;
280
  }
281
  .info-box {
282
+ background: #f0f9ff;
283
+ border: 1px solid #0ea5e9;
284
  border-radius: 0.5rem;
285
  padding: 1rem;
286
  margin: 1rem 0;
 
 
 
 
287
  }
288
  .footer {
289
  text-align: center;
290
  margin-top: 2rem;
291
  padding-top: 2rem;
292
+ border-top: 1px solid #e2e8f0;
293
+ color: #64748b;
294
  }
295
  """) as demo:
296
 
297
+ # Header
298
  gr.HTML("""
299
  <div class="header">
300
+ <h1>Contact Search Tool</h1>
301
+ <p>Professional contact discovery powered by AI</p>
302
  </div>
303
  """)
304
+
305
+ # Info box
306
  gr.HTML(f"""
307
  <div class="info-box">
308
  <h3>Instructions:</h3>
309
  <ol>
310
+ <li>Enter the website URL you want to search for contacts</li>
311
+ <li>Click "Search Contacts" to get results</li>
312
+ <li>View formatted results and raw JSON response</li>
313
  </ol>
314
  <p><strong>API Endpoint:</strong> {API_ENDPOINT}</p>
315
  </div>
316
  """)
317
+
318
  with gr.Row():
319
  with gr.Column(scale=2):
320
+ url_input = gr.Textbox(
321
+ label="Website URL",
322
+ placeholder="example.com or https://example.com",
323
+ info="Enter the website URL to search for contacts",
324
+ lines=1
325
+ )
326
+
327
  with gr.Row():
328
  search_btn = gr.Button("Search Contacts", variant="primary", scale=2)
329
+ demo_btn = gr.Button("Show Demo", variant="secondary", scale=1)
330
  clear_btn = gr.Button("Clear", variant="secondary", scale=1)
331
+
332
  with gr.Row():
333
  with gr.Column(scale=1):
334
+ results_output = gr.Markdown(
335
+ label="Contact Results",
336
+ value="Enter a URL and click 'Search Contacts' to see results here."
337
+ )
338
+
339
  with gr.Column(scale=1):
340
+ json_output = gr.Code(
341
+ label="Raw JSON Response",
342
+ language="json",
343
+ value=""
344
+ )
345
+
346
+ # Event handlers
347
+ search_btn.click(
348
+ fn=search_contacts,
349
+ inputs=[url_input],
350
+ outputs=[results_output, json_output]
351
+ )
352
+
353
+ demo_btn.click(
354
+ fn=lambda: (create_sample_data(), '{"contacts": [{"name": "John Smith", "email": "john.smith@example.com", "title": "CEO"}]}'),
355
+ outputs=[results_output, json_output]
356
+ )
357
+
358
+ clear_btn.click(
359
+ fn=lambda: ("", ""),
360
+ outputs=[url_input, results_output]
361
+ )
362
+
363
+ # Footer
364
  gr.HTML("""
365
  <div class="footer">
366
+ <p>© 2025 Contact Search Tool - Professional contact discovery solution</p>
367
  </div>
368
  """)
369
 
370
+ # Launch the app
371
  if __name__ == "__main__":
372
  demo.launch(
373
  share=True,
 
375
  server_port=7860,
376
  show_error=True,
377
  debug=True
378
+ )