Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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
|
| 401 |
|
| 402 |
# Search Chabad.org for the city
|
| 403 |
-
print(f"
|
| 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"
|
| 419 |
|
| 420 |
except Exception as e:
|
| 421 |
-
print(f"
|
| 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("
|
| 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="
|
| 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 |
-
### 💡 **
|
| 576 |
- Use full city names for better results
|
| 577 |
-
-
|
| 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 |
-
|
| 596 |
|
| 597 |
-
Or
|
| 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 |
-
"
|
| 627 |
-
"
|
| 628 |
-
"
|
| 629 |
-
"
|
|
|
|
| 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 |
-
-
|
| 683 |
-
-
|
| 684 |
-
-
|
| 685 |
-
-
|
| 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("""
|