RozzaCreat commited on
Commit
641d9ab
·
verified ·
1 Parent(s): 4209ee2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +37 -261
app.py CHANGED
@@ -17,189 +17,7 @@ class ShabbatCandleLightingAgent:
17
  self.base_url = "https://www.chabad.org"
18
  self.candle_lighting_url = "https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm"
19
 
20
- def get_user_location_simple(self) -> Optional[Dict[str, Any]]:
21
- """
22
- Get user's approximate location using multiple IP geolocation services with improved accuracy
23
- """
24
- try:
25
- # Enhanced list of IP geolocation services with better accuracy
26
- services = [
27
- {
28
- "url": "http://ip-api.com/json/?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp",
29
- "parser": "ip_api"
30
- },
31
- {
32
- "url": "https://ipapi.co/json/",
33
- "parser": "ipapi_co"
34
- },
35
- {
36
- "url": "https://freegeoip.app/json/",
37
- "parser": "freegeoip"
38
- },
39
- {
40
- "url": "http://www.geoplugin.net/json.gp",
41
- "parser": "geoplugin"
42
- },
43
- {
44
- "url": "https://ipgeolocation.abstractapi.com/v1/?api_key=free",
45
- "parser": "abstractapi"
46
- }
47
- ]
48
-
49
- location_results = []
50
-
51
- for service in services:
52
- try:
53
- headers = {
54
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
55
- 'Accept': 'application/json',
56
- 'Accept-Language': 'en-US,en;q=0.9'
57
- }
58
-
59
- response = requests.get(service["url"], headers=headers, timeout=8)
60
- if response.status_code == 200:
61
- data = response.json()
62
- parsed_location = self._parse_location_data(data, service["parser"])
63
-
64
- if parsed_location and parsed_location.get('city'):
65
- location_results.append(parsed_location)
66
- print(f"✅ {service['parser']}: {parsed_location['city']}, {parsed_location.get('country', '')}")
67
-
68
- except Exception as e:
69
- print(f"❌ Service {service['parser']} failed: {e}")
70
- continue
71
-
72
- # Choose the best result based on consensus or quality
73
- if location_results:
74
- best_location = self._choose_best_location(location_results)
75
- print(f"🎯 Selected location: {best_location['city']}, {best_location.get('country', '')}")
76
- return best_location
77
-
78
- except Exception as e:
79
- print(f"Location detection error: {e}")
80
-
81
- return None
82
-
83
- def _parse_location_data(self, data: dict, parser_type: str) -> Optional[Dict[str, Any]]:
84
- """Parse location data from different API response formats"""
85
- try:
86
- if parser_type == "ip_api":
87
- if data.get('status') == 'success':
88
- return {
89
- 'city': data.get('city'),
90
- 'country': data.get('country'),
91
- 'region': data.get('regionName'),
92
- 'lat': data.get('lat'),
93
- 'lng': data.get('lon'),
94
- 'timezone': data.get('timezone'),
95
- 'isp': data.get('isp'),
96
- 'source': 'ip-api.com',
97
- 'accuracy': 'medium'
98
- }
99
-
100
- elif parser_type == "ipapi_co":
101
- if data.get('city') and not data.get('error'):
102
- return {
103
- 'city': data.get('city'),
104
- 'country': data.get('country_name'),
105
- 'region': data.get('region'),
106
- 'lat': data.get('latitude'),
107
- 'lng': data.get('longitude'),
108
- 'timezone': data.get('timezone'),
109
- 'source': 'ipapi.co',
110
- 'accuracy': 'high'
111
- }
112
-
113
- elif parser_type == "freegeoip":
114
- if data.get('city'):
115
- return {
116
- 'city': data.get('city'),
117
- 'country': data.get('country_name'),
118
- 'region': data.get('region_name'),
119
- 'lat': data.get('latitude'),
120
- 'lng': data.get('longitude'),
121
- 'timezone': data.get('time_zone'),
122
- 'source': 'freegeoip.app',
123
- 'accuracy': 'medium'
124
- }
125
-
126
- elif parser_type == "geoplugin":
127
- if data.get('geoplugin_city') and data.get('geoplugin_city') != '':
128
- return {
129
- 'city': data.get('geoplugin_city'),
130
- 'country': data.get('geoplugin_countryName'),
131
- 'region': data.get('geoplugin_region'),
132
- 'lat': data.get('geoplugin_latitude'),
133
- 'lng': data.get('geoplugin_longitude'),
134
- 'timezone': data.get('geoplugin_timezone'),
135
- 'source': 'geoplugin.net',
136
- 'accuracy': 'low'
137
- }
138
-
139
- elif parser_type == "abstractapi":
140
- if data.get('city') and not data.get('error'):
141
- return {
142
- 'city': data.get('city'),
143
- 'country': data.get('country'),
144
- 'region': data.get('region'),
145
- 'lat': data.get('latitude'),
146
- 'lng': data.get('longitude'),
147
- 'timezone': data.get('timezone', {}).get('name'),
148
- 'source': 'abstractapi.com',
149
- 'accuracy': 'high'
150
- }
151
-
152
- except Exception as e:
153
- print(f"Parse error for {parser_type}: {e}")
154
-
155
- return None
156
-
157
- def _is_likely_datacenter_location(self, city: str, country: str) -> bool:
158
- """
159
- Check if the detected location is likely a data center rather than user's real location
160
- """
161
- datacenter_locations = [
162
- # Major US data centers
163
- ("ashburn", "united states"), ("ashburn", "usa"), ("ashburn", "us"),
164
- ("reston", "united states"), ("reston", "usa"), ("reston", "us"),
165
- ("santa clara", "united states"), ("santa clara", "usa"),
166
- ("fremont", "united states"), ("fremont", "usa"),
167
- ("dallas", "united states"), ("dallas", "usa"),
168
- # European data centers
169
- ("amsterdam", "netherlands"), ("amsterdam", "holland"),
170
- ("frankfurt", "germany"), ("dublin", "ireland"),
171
- # Asian data centers
172
- ("singapore", "singapore"), ("tokyo", "japan"),
173
- ("hong kong", "hong kong"), ("seoul", "south korea")
174
- ]
175
-
176
- city_lower = city.lower() if city else ""
177
- country_lower = country.lower() if country else ""
178
-
179
- return (city_lower, country_lower) in datacenter_locations
180
-
181
- def _suggest_likely_countries(self, detected_datacenter: str) -> list:
182
- """
183
- Suggest likely countries based on common data center routing patterns
184
- """
185
- suggestions = {
186
- "ashburn": ["Israel", "Turkey", "Eastern Europe", "Middle East"],
187
- "amsterdam": ["Eastern Europe", "Scandinavia", "Russia"],
188
- "singapore": ["Australia", "Southeast Asia", "India"],
189
- "tokyo": ["Asia-Pacific", "Australia", "Philippines"],
190
- "frankfurt": ["Europe", "Middle East", "Africa"]
191
- }
192
-
193
- datacenter = detected_datacenter.lower()
194
- for dc in suggestions:
195
- if dc in datacenter:
196
- return suggestions[dc]
197
-
198
- return best_location
199
-
200
- def _choose_best_location(self, locations: list) -> Dict[str, Any]:
201
 
202
-
203
  def search_city_on_chabad(self, city_name: str) -> Optional[str]:
204
  """
205
  Search for a city on Chabad.org and get the specific URL for candle lighting times
@@ -389,18 +207,26 @@ class ShabbatCandleLightingAgent:
389
 
390
  return None
391
 
392
- def get_shabbat_times(self, city_input: str = "") -> str:
393
  """
394
  Main function to get Shabbat candle lighting times
395
  """
396
  try:
397
  city_name = city_input.strip()
398
 
 
 
 
 
 
 
 
 
399
  if not city_name:
400
- return self._format_error_response("❌ Please enter a city name to search for Shabbat times.")
401
 
402
  # Search Chabad.org for the city
403
- print(f"🔍 Searching for Shabbat times in: {city_name}")
404
  city_url = self.search_city_on_chabad(city_name)
405
 
406
  result_data = None
@@ -415,14 +241,14 @@ class ShabbatCandleLightingAgent:
415
 
416
  if response.status_code == 200:
417
  result_data = self.extract_candle_lighting_times(response.text, city_name)
418
- print(f"📊 Extraction result: {result_data}")
419
 
420
  except Exception as e:
421
- print(f"❌ Chabad.org fetch error: {e}")
422
 
423
  # Try backup sources if Chabad.org didn't work well
424
  if not result_data or not result_data.get('found_data'):
425
- print("🔄 Trying backup sources...")
426
  backup_data = self.get_backup_times(city_name)
427
  if backup_data and backup_data.get('found_data'):
428
  result_data = backup_data
@@ -437,7 +263,6 @@ class ShabbatCandleLightingAgent:
437
  except Exception as e:
438
  print(f"Main function error: {e}")
439
  return self._format_error_response(f"❌ An error occurred: {str(e)}")
440
-
441
 
442
  def _format_success_response(self, data: Dict[str, Any], source: str, url: str = None) -> str:
443
  """Format successful response with Shabbat times"""
@@ -470,44 +295,9 @@ class ShabbatCandleLightingAgent:
470
  **Retrieved:** {datetime.datetime.now().strftime('%B %d, %Y at %I:%M %p')}
471
  {f"**URL:** {url}" if url else ""}
472
 
473
- ---
474
-
475
- ## 🙏 **Shabbat Shalom!**
476
- *May your Shabbat be filled with peace, joy, and spiritual connection.*
477
-
478
- **🔔 Reminder:** Set an alarm 30 minutes before candle lighting time to prepare properly.
479
- """
480
- return response
481
-
482
- def _format_not_found_response(self, city_name: str, url: str = None) -> str:
483
- """Format response when times are not found"""
484
- return f"""
485
- # 🕯️ Shabbat Candle Lighting Times
486
-
487
- ## ❌ Times Not Found for {city_name}
488
-
489
- We searched multiple sources but couldn't find specific Shabbat candle lighting times for **{city_name}**.
490
-
491
- ### 🔍 **What you can try:**
492
- 1. **Check spelling** - Make sure the city name is spelled correctly
493
- 2. **Add country** - Try "City, Country" format (e.g., "Paris, France")
494
- 3. **Use major city** - Try the nearest major city instead
495
- 4. **Alternative spellings** - Try different variations of the city name
496
 
497
- ### 📍 **General Guidelines for {city_name}:**
498
- - 🕯️ **Candle lighting:** Typically 18 minutes before sunset on Friday
499
- - ⭐ **Havdalah:** Approximately 25-42 minutes after sunset on Saturday
500
- - 🌍 **Location matters:** Times vary significantly by geographic location
501
 
502
- ### 🌐 **Manual Resources:**
503
- - 📱 **Chabad.org:** [Direct search]({url or 'https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm'})
504
- - 📱 **Hebcal.com:** Alternative Jewish calendar
505
- - 📞 **Local contacts:** Your synagogue or Chabad house
506
 
507
- ### 📞 **Local Resources:**
508
- - Contact your local Chabad house or synagogue
509
- - Check with your Jewish community center
510
- - Consult your rabbi for precise local customs
511
 
512
  ---
513
 
@@ -561,10 +351,16 @@ def create_shabbat_interface():
561
  city_input = gr.Textbox(
562
  label="🏙️ City Name",
563
  placeholder="Enter city name (e.g., 'New York', 'London, UK', 'Tel Aviv, Israel')",
564
- info="Please enter your city name to get accurate Shabbat times",
565
  lines=1
566
  )
567
 
 
 
 
 
 
 
568
  search_btn = gr.Button(
569
  "🔍 Get Shabbat Times",
570
  variant="primary",
@@ -572,18 +368,10 @@ def create_shabbat_interface():
572
  )
573
 
574
  gr.Markdown("""
575
- ### 💡 **City Input Tips:**
576
  - Use full city names for better results
577
- - Add country for common city names (e.g., "Paris, France")
578
  - Try nearest major city if yours isn't found
579
- - Use English city names for best search results
580
-
581
- ### 🇮🇱 **Popular Israeli Cities:**
582
- Jerusalem, Tel Aviv, Haifa, Beer Sheva, Netanya, Ashdod,
583
- Petah Tikva, Ramat Gan, Bnei Brak, Herzliya
584
-
585
- ### 🌍 **International Examples:**
586
- London, UK • Paris, France • New York, USA • Toronto, Canada
587
  """)
588
 
589
  with gr.Column(scale=2):
@@ -592,15 +380,9 @@ def create_shabbat_interface():
592
  value="""
593
  # 🕯️ Welcome!
594
 
595
- ���� **Enter your city name above and click 'Get Shabbat Times'**
596
 
597
- Or click one of the popular cities below for a quick search.
598
-
599
- ### 🇮🇱 **Common Israeli Cities:**
600
- Use any of these city names: Jerusalem, Tel Aviv, Haifa, Beer Sheva, Netanya, Ashdod
601
-
602
- ### 🌍 **International Cities:**
603
- Include country name: London, UK • Paris, France • New York, USA
604
 
605
  ---
606
 
@@ -613,20 +395,14 @@ Include country name: London, UK • Paris, France • New York, USA
613
 
614
  with gr.Row():
615
  popular_cities = [
616
- # Israeli cities first for the detected user
617
- "Jerusalem, Israel",
618
- "Tel Aviv, Israel",
619
- "Haifa, Israel",
620
- "Beer Sheva, Israel",
621
- "Netanya, Israel",
622
- "Ashdod, Israel",
623
- # International cities
624
  "New York, NY",
 
625
  "London, UK",
626
- "Paris, France",
627
- "Toronto, Canada",
628
- "Los Angeles, CA",
629
- "Miami, FL"
 
630
  ]
631
 
632
  example_btns = []
@@ -639,14 +415,14 @@ Include country name: London, UK • Paris, France • New York, USA
639
  # Button click handlers
640
  search_btn.click(
641
  fn=agent.get_shabbat_times,
642
- inputs=[city_input],
643
  outputs=output_area
644
  )
645
 
646
  # Example button handlers
647
  for btn, city in example_btns:
648
  btn.click(
649
- fn=lambda c=city: agent.get_shabbat_times(c),
650
  outputs=output_area
651
  )
652
 
@@ -679,10 +455,10 @@ Include country name: London, UK • Paris, France • New York, USA
679
  - Some communities light candles at different intervals before sunset
680
 
681
  ### 🔧 **Technical Features:**
682
- - Multiple search strategies for maximum success on Chabad.org
683
- - Intelligent pattern matching to extract candle lighting times
684
- - Fallback to alternative Jewish calendar sources if needed
685
- - Manual city input for maximum accuracy and reliability
686
  """)
687
 
688
  gr.Markdown("""
 
17
  self.base_url = "https://www.chabad.org"
18
  self.candle_lighting_url = "https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm"
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
 
21
  def search_city_on_chabad(self, city_name: str) -> Optional[str]:
22
  """
23
  Search for a city on Chabad.org and get the specific URL for candle lighting times
 
207
 
208
  return None
209
 
210
+ def get_shabbat_times(self, city_input: str = "", auto_detect: bool = True) -> str:
211
  """
212
  Main function to get Shabbat candle lighting times
213
  """
214
  try:
215
  city_name = city_input.strip()
216
 
217
+ # Auto-detect location if requested and no city provided
218
+ if auto_detect and not city_name:
219
+ location = self.get_user_location_simple()
220
+ if location and location.get('city'):
221
+ city_name = location['city']
222
+ if location.get('country') and location['country'] not in city_name:
223
+ city_name += f", {location['country']}"
224
+
225
  if not city_name:
226
+ return self._format_error_response("❌ Please provide a city name or enable location detection.")
227
 
228
  # Search Chabad.org for the city
229
+ print(f"Searching for Shabbat times in: {city_name}")
230
  city_url = self.search_city_on_chabad(city_name)
231
 
232
  result_data = None
 
241
 
242
  if response.status_code == 200:
243
  result_data = self.extract_candle_lighting_times(response.text, city_name)
244
+ print(f"Extraction result: {result_data}")
245
 
246
  except Exception as e:
247
+ print(f"Chabad.org fetch error: {e}")
248
 
249
  # Try backup sources if Chabad.org didn't work well
250
  if not result_data or not result_data.get('found_data'):
251
+ print("Trying backup sources...")
252
  backup_data = self.get_backup_times(city_name)
253
  if backup_data and backup_data.get('found_data'):
254
  result_data = backup_data
 
263
  except Exception as e:
264
  print(f"Main function error: {e}")
265
  return self._format_error_response(f"❌ An error occurred: {str(e)}")
 
266
 
267
  def _format_success_response(self, data: Dict[str, Any], source: str, url: str = None) -> str:
268
  """Format successful response with Shabbat times"""
 
295
  **Retrieved:** {datetime.datetime.now().strftime('%B %d, %Y at %I:%M %p')}
296
  {f"**URL:** {url}" if url else ""}
297
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
 
 
 
 
 
299
 
 
 
 
 
300
 
 
 
 
 
301
 
302
  ---
303
 
 
351
  city_input = gr.Textbox(
352
  label="🏙️ City Name",
353
  placeholder="Enter city name (e.g., 'New York', 'London, UK', 'Tel Aviv, Israel')",
354
+ info="Leave empty to auto-detect your location",
355
  lines=1
356
  )
357
 
358
+ auto_detect = gr.Checkbox(
359
+ label="🌐 Auto-detect my location",
360
+ value=True,
361
+ info="Automatically find your location if no city is entered"
362
+ )
363
+
364
  search_btn = gr.Button(
365
  "🔍 Get Shabbat Times",
366
  variant="primary",
 
368
  )
369
 
370
  gr.Markdown("""
371
+ ### 💡 **Tips:**
372
  - Use full city names for better results
373
+ - Include country for common city names
374
  - Try nearest major city if yours isn't found
 
 
 
 
 
 
 
 
375
  """)
376
 
377
  with gr.Column(scale=2):
 
380
  value="""
381
  # 🕯️ Welcome!
382
 
383
+ 👆 **Enter a city name and click 'Get Shabbat Times' to begin**
384
 
385
+ Or try one of the popular cities below for a quick search.
 
 
 
 
 
 
386
 
387
  ---
388
 
 
395
 
396
  with gr.Row():
397
  popular_cities = [
 
 
 
 
 
 
 
 
398
  "New York, NY",
399
+ "Los Angeles, CA",
400
  "London, UK",
401
+ "Jerusalem, Israel",
402
+ "Tel Aviv, Israel",
403
+ "Miami, FL",
404
+ "Chicago, IL",
405
+ "Toronto, Canada"
406
  ]
407
 
408
  example_btns = []
 
415
  # Button click handlers
416
  search_btn.click(
417
  fn=agent.get_shabbat_times,
418
+ inputs=[city_input, auto_detect],
419
  outputs=output_area
420
  )
421
 
422
  # Example button handlers
423
  for btn, city in example_btns:
424
  btn.click(
425
+ fn=lambda c=city: agent.get_shabbat_times(c, False),
426
  outputs=output_area
427
  )
428
 
 
455
  - Some communities light candles at different intervals before sunset
456
 
457
  ### 🔧 **Technical Features:**
458
+ - Auto-location detection using IP geolocation
459
+ - Multiple search strategies for maximum success
460
+ - Intelligent pattern matching to extract times
461
+ - Fallback to alternative Jewish calendar sources
462
  """)
463
 
464
  gr.Markdown("""