Spaces:
Sleeping
Sleeping
Update app.py
#1
by RozzaCreat - opened
app.py
CHANGED
|
@@ -16,7 +16,154 @@ class ShabbatCandleLightingAgent:
|
|
| 16 |
self.base_url = "https://www.chabad.org"
|
| 17 |
self.candle_lighting_url = "https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm"
|
| 18 |
|
| 19 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
"""
|
| 21 |
Search for a city on Chabad.org and get the specific URL for candle lighting times
|
| 22 |
"""
|
|
@@ -205,15 +352,50 @@ class ShabbatCandleLightingAgent:
|
|
| 205 |
|
| 206 |
return None
|
| 207 |
|
| 208 |
-
def get_shabbat_times(self, city_input: str = "") -> str:
|
| 209 |
"""
|
| 210 |
Main function to get Shabbat candle lighting times
|
| 211 |
"""
|
| 212 |
try:
|
| 213 |
city_name = city_input.strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
|
| 215 |
if not city_name:
|
| 216 |
-
return self._format_error_response("❌ Please enter a city name to search for Shabbat times.")
|
| 217 |
|
| 218 |
# Search Chabad.org for the city
|
| 219 |
print(f"🔍 Searching for Shabbat times in: {city_name}")
|
|
@@ -246,14 +428,54 @@ class ShabbatCandleLightingAgent:
|
|
| 246 |
|
| 247 |
# Format and return the response
|
| 248 |
if result_data and result_data.get('found_data'):
|
| 249 |
-
|
|
|
|
| 250 |
else:
|
| 251 |
-
|
|
|
|
| 252 |
|
| 253 |
except Exception as e:
|
| 254 |
print(f"Main function error: {e}")
|
| 255 |
return self._format_error_response(f"❌ An error occurred: {str(e)}")
|
| 256 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
def _format_success_response(self, data: Dict[str, Any], source: str, url: str = None) -> str:
|
| 258 |
"""Format successful response with Shabbat times"""
|
| 259 |
candle_time = data.get('candle_lighting_time', 'Not found')
|
|
@@ -376,26 +598,37 @@ def create_shabbat_interface():
|
|
| 376 |
city_input = gr.Textbox(
|
| 377 |
label="🏙️ City Name",
|
| 378 |
placeholder="Enter city name (e.g., 'New York', 'London, UK', 'Tel Aviv, Israel')",
|
| 379 |
-
info="
|
| 380 |
lines=1
|
| 381 |
)
|
| 382 |
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
|
| 389 |
gr.Markdown("""
|
| 390 |
-
### 💡 **
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
-
|
| 394 |
-
- Use
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
|
| 396 |
### 🇮🇱 **Popular Israeli Cities:**
|
| 397 |
-
Jerusalem, Tel Aviv, Haifa, Beer Sheva, Netanya, Ashdod
|
| 398 |
-
Petah Tikva, Ramat Gan, Bnei Brak, Herzliya
|
| 399 |
|
| 400 |
### 🌍 **International Examples:**
|
| 401 |
London, UK • Paris, France • New York, USA • Toronto, Canada
|
|
@@ -407,19 +640,25 @@ def create_shabbat_interface():
|
|
| 407 |
value="""
|
| 408 |
# 🕯️ Welcome!
|
| 409 |
|
| 410 |
-
|
| 411 |
|
| 412 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
|
| 414 |
### 🇮🇱 **Common Israeli Cities:**
|
| 415 |
-
Use
|
| 416 |
|
| 417 |
### 🌍 **International Cities:**
|
| 418 |
-
|
| 419 |
|
| 420 |
---
|
| 421 |
|
| 422 |
**🕯️ Every Friday evening, Jewish families around the world light candles to welcome Shabbat**
|
|
|
|
|
|
|
| 423 |
"""
|
| 424 |
)
|
| 425 |
|
|
@@ -451,17 +690,24 @@ Include country name: London, UK • Paris, France • New York, USA
|
|
| 451 |
btn = gr.Button(f"📍 {city}", size="sm")
|
| 452 |
example_btns.append((btn, city))
|
| 453 |
|
| 454 |
-
# Button click handlers
|
| 455 |
search_btn.click(
|
| 456 |
fn=agent.get_shabbat_times,
|
| 457 |
-
inputs=[city_input],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 458 |
outputs=output_area
|
| 459 |
)
|
| 460 |
|
| 461 |
# Example button handlers
|
| 462 |
for btn, city in example_btns:
|
| 463 |
btn.click(
|
| 464 |
-
fn=lambda c=city: agent.get_shabbat_times(c),
|
| 465 |
outputs=output_area
|
| 466 |
)
|
| 467 |
|
|
@@ -494,10 +740,12 @@ Include country name: London, UK • Paris, France • New York, USA
|
|
| 494 |
- Some communities light candles at different intervals before sunset
|
| 495 |
|
| 496 |
### 🔧 **Technical Features:**
|
| 497 |
-
-
|
| 498 |
-
-
|
| 499 |
-
-
|
| 500 |
-
-
|
|
|
|
|
|
|
| 501 |
""")
|
| 502 |
|
| 503 |
gr.Markdown("""
|
|
|
|
| 16 |
self.base_url = "https://www.chabad.org"
|
| 17 |
self.candle_lighting_url = "https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm"
|
| 18 |
|
| 19 |
+
def get_user_ip_location(self) -> Optional[Dict[str, Any]]:
|
| 20 |
+
"""
|
| 21 |
+
Enhanced IP-based location detection using multiple high-quality services
|
| 22 |
+
"""
|
| 23 |
+
try:
|
| 24 |
+
# Enhanced list of location services with better accuracy
|
| 25 |
+
services = [
|
| 26 |
+
{
|
| 27 |
+
"url": "https://ipapi.co/json/",
|
| 28 |
+
"parser": "ipapi_co",
|
| 29 |
+
"accuracy": "high"
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"url": "http://ip-api.com/json/?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,query",
|
| 33 |
+
"parser": "ip_api",
|
| 34 |
+
"accuracy": "medium"
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"url": "https://ipgeolocation.abstractapi.com/v1/?api_key=free",
|
| 38 |
+
"parser": "abstractapi",
|
| 39 |
+
"accuracy": "high"
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"url": "https://freegeoip.app/json/",
|
| 43 |
+
"parser": "freegeoip",
|
| 44 |
+
"accuracy": "medium"
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"url": "https://api.ipify.org?format=json",
|
| 48 |
+
"parser": "ipify_then_geocode",
|
| 49 |
+
"accuracy": "low"
|
| 50 |
+
}
|
| 51 |
+
]
|
| 52 |
+
|
| 53 |
+
best_result = None
|
| 54 |
+
all_results = []
|
| 55 |
+
|
| 56 |
+
for service in services:
|
| 57 |
+
try:
|
| 58 |
+
headers = {
|
| 59 |
+
'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',
|
| 60 |
+
'Accept': 'application/json',
|
| 61 |
+
'Accept-Language': 'en-US,en;q=0.9'
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
response = requests.get(service["url"], headers=headers, timeout=8)
|
| 65 |
+
if response.status_code == 200:
|
| 66 |
+
data = response.json()
|
| 67 |
+
parsed_location = self._parse_ip_location_data(data, service["parser"])
|
| 68 |
+
|
| 69 |
+
if parsed_location and parsed_location.get('city'):
|
| 70 |
+
parsed_location['accuracy_level'] = service["accuracy"]
|
| 71 |
+
parsed_location['source'] = service["parser"]
|
| 72 |
+
all_results.append(parsed_location)
|
| 73 |
+
print(f"✅ {service['parser']}: {parsed_location['city']}, {parsed_location.get('country', '')}")
|
| 74 |
+
|
| 75 |
+
# Keep the best result (prefer high accuracy sources)
|
| 76 |
+
if not best_result or service["accuracy"] == "high":
|
| 77 |
+
best_result = parsed_location
|
| 78 |
+
|
| 79 |
+
except Exception as e:
|
| 80 |
+
print(f"❌ Service {service['parser']} failed: {e}")
|
| 81 |
+
continue
|
| 82 |
+
|
| 83 |
+
# Return best result with confidence assessment
|
| 84 |
+
if best_result:
|
| 85 |
+
best_result['all_results'] = all_results
|
| 86 |
+
best_result['confidence'] = self._assess_location_confidence(all_results)
|
| 87 |
+
print(f"🎯 Best location: {best_result['city']}, {best_result.get('country', '')} (confidence: {best_result['confidence']})")
|
| 88 |
+
return best_result
|
| 89 |
+
|
| 90 |
+
except Exception as e:
|
| 91 |
+
print(f"Location detection error: {e}")
|
| 92 |
+
|
| 93 |
+
return None
|
| 94 |
+
|
| 95 |
+
def _parse_ip_location_data(self, data: dict, parser_type: str) -> Optional[Dict[str, Any]]:
|
| 96 |
+
"""Parse location data from different IP geolocation APIs"""
|
| 97 |
+
try:
|
| 98 |
+
if parser_type == "ipapi_co":
|
| 99 |
+
if data.get('city') and not data.get('error'):
|
| 100 |
+
return {
|
| 101 |
+
'city': data.get('city'),
|
| 102 |
+
'country': data.get('country_name'),
|
| 103 |
+
'region': data.get('region'),
|
| 104 |
+
'lat': data.get('latitude'),
|
| 105 |
+
'lng': data.get('longitude'),
|
| 106 |
+
'timezone': data.get('timezone'),
|
| 107 |
+
'isp': data.get('org')
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
elif parser_type == "ip_api":
|
| 111 |
+
if data.get('status') == 'success':
|
| 112 |
+
return {
|
| 113 |
+
'city': data.get('city'),
|
| 114 |
+
'country': data.get('country'),
|
| 115 |
+
'region': data.get('regionName'),
|
| 116 |
+
'lat': data.get('lat'),
|
| 117 |
+
'lng': data.get('lon'),
|
| 118 |
+
'timezone': data.get('timezone'),
|
| 119 |
+
'isp': data.get('isp')
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
elif parser_type == "abstractapi":
|
| 123 |
+
if data.get('city') and not data.get('error'):
|
| 124 |
+
return {
|
| 125 |
+
'city': data.get('city'),
|
| 126 |
+
'country': data.get('country'),
|
| 127 |
+
'region': data.get('region'),
|
| 128 |
+
'lat': data.get('latitude'),
|
| 129 |
+
'lng': data.get('longitude'),
|
| 130 |
+
'timezone': data.get('timezone', {}).get('name') if isinstance(data.get('timezone'), dict) else data.get('timezone'),
|
| 131 |
+
'isp': data.get('connection', {}).get('isp_name') if isinstance(data.get('connection'), dict) else None
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
elif parser_type == "freegeoip":
|
| 135 |
+
if data.get('city'):
|
| 136 |
+
return {
|
| 137 |
+
'city': data.get('city'),
|
| 138 |
+
'country': data.get('country_name'),
|
| 139 |
+
'region': data.get('region_name'),
|
| 140 |
+
'lat': data.get('latitude'),
|
| 141 |
+
'lng': data.get('longitude'),
|
| 142 |
+
'timezone': data.get('time_zone')
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
except Exception as e:
|
| 146 |
+
print(f"Parse error for {parser_type}: {e}")
|
| 147 |
+
|
| 148 |
+
return None
|
| 149 |
+
|
| 150 |
+
def _assess_location_confidence(self, all_results: list) -> str:
|
| 151 |
+
"""Assess confidence level based on multiple results"""
|
| 152 |
+
if len(all_results) == 0:
|
| 153 |
+
return "none"
|
| 154 |
+
elif len(all_results) == 1:
|
| 155 |
+
return "low"
|
| 156 |
+
|
| 157 |
+
# Check if multiple services agree on the city
|
| 158 |
+
cities = [result.get('city', '').lower() for result in all_results if result.get('city')]
|
| 159 |
+
unique_cities = set(cities)
|
| 160 |
+
|
| 161 |
+
if len(unique_cities) == 1 and len(cities) >= 2:
|
| 162 |
+
return "high"
|
| 163 |
+
elif len(unique_cities) <= 2 and len(cities) >= 2:
|
| 164 |
+
return "medium"
|
| 165 |
+
else:
|
| 166 |
+
return "low"
|
| 167 |
"""
|
| 168 |
Search for a city on Chabad.org and get the specific URL for candle lighting times
|
| 169 |
"""
|
|
|
|
| 352 |
|
| 353 |
return None
|
| 354 |
|
| 355 |
+
def get_shabbat_times(self, city_input: str = "", auto_detect: bool = False) -> str:
|
| 356 |
"""
|
| 357 |
Main function to get Shabbat candle lighting times
|
| 358 |
"""
|
| 359 |
try:
|
| 360 |
city_name = city_input.strip()
|
| 361 |
+
location_info = ""
|
| 362 |
+
|
| 363 |
+
# Auto-detect location using enhanced IP geolocation if requested
|
| 364 |
+
if auto_detect and not city_name:
|
| 365 |
+
print("🔍 Attempting to detect your location using IP geolocation...")
|
| 366 |
+
location = self.get_user_ip_location()
|
| 367 |
+
if location and location.get('city'):
|
| 368 |
+
city_name = location['city']
|
| 369 |
+
if location.get('country') and location['country'] not in city_name:
|
| 370 |
+
city_name += f", {location['country']}"
|
| 371 |
+
|
| 372 |
+
# Add location detection info
|
| 373 |
+
confidence = location.get('confidence', 'unknown')
|
| 374 |
+
source = location.get('source', 'unknown')
|
| 375 |
+
|
| 376 |
+
if confidence == 'high':
|
| 377 |
+
status_icon = "✅"
|
| 378 |
+
status_text = "High confidence"
|
| 379 |
+
elif confidence == 'medium':
|
| 380 |
+
status_icon = "⚠️"
|
| 381 |
+
status_text = "Medium confidence - please verify"
|
| 382 |
+
else:
|
| 383 |
+
status_icon = "🔍"
|
| 384 |
+
status_text = "Low confidence - may be inaccurate"
|
| 385 |
+
|
| 386 |
+
location_info = f"""
|
| 387 |
+
**🌐 IP-Based Location Detection:**
|
| 388 |
+
{status_icon} **Detected:** {city_name}
|
| 389 |
+
**Confidence:** {status_text}
|
| 390 |
+
**Source:** {source}
|
| 391 |
+
|
| 392 |
+
{('⚠️ *Note: IP location shows your internet provider\'s location, not exact address. For most accurate results, enter your city manually.*' if confidence != 'high' else '✅ *Multiple services confirmed this location*')}
|
| 393 |
+
|
| 394 |
+
---
|
| 395 |
+
"""
|
| 396 |
|
| 397 |
if not city_name:
|
| 398 |
+
return self._format_error_response("❌ Please enter a city name or enable auto-detection to search for Shabbat times.")
|
| 399 |
|
| 400 |
# Search Chabad.org for the city
|
| 401 |
print(f"🔍 Searching for Shabbat times in: {city_name}")
|
|
|
|
| 428 |
|
| 429 |
# Format and return the response
|
| 430 |
if result_data and result_data.get('found_data'):
|
| 431 |
+
response = self._format_success_response(result_data, source, city_url)
|
| 432 |
+
return location_info + response
|
| 433 |
else:
|
| 434 |
+
response = self._format_not_found_response(city_name, city_url)
|
| 435 |
+
return location_info + response
|
| 436 |
|
| 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 |
+
def _format_geolocation_instructions(self) -> str:
|
| 442 |
+
"""Instructions for using browser geolocation"""
|
| 443 |
+
return f"""
|
| 444 |
+
# 🕯️ Shabbat Candle Lighting Times
|
| 445 |
+
|
| 446 |
+
## 📍 Automatic Location Detection
|
| 447 |
+
|
| 448 |
+
To automatically detect your location, we need access to your device's GPS. Please follow these steps:
|
| 449 |
+
|
| 450 |
+
### 🔒 **Step 1: Enable Location Access**
|
| 451 |
+
1. Click the **"🌐 Get My Location"** button below
|
| 452 |
+
2. Your browser will ask for permission to access your location
|
| 453 |
+
3. Click **"Allow"** in the permission popup
|
| 454 |
+
4. Wait for your coordinates to be detected
|
| 455 |
+
|
| 456 |
+
### 📱 **Step 2: Location will be converted to city**
|
| 457 |
+
Once we get your GPS coordinates, we'll:
|
| 458 |
+
- Convert them to the nearest city name
|
| 459 |
+
- Automatically search for Shabbat times
|
| 460 |
+
- Display the results for your location
|
| 461 |
+
|
| 462 |
+
### ⚠️ **Privacy Note:**
|
| 463 |
+
- Your location is only used to find the nearest city
|
| 464 |
+
- No location data is stored or shared
|
| 465 |
+
- You can deny permission and enter your city manually instead
|
| 466 |
+
|
| 467 |
+
### 🌐 **Location Requirements:**
|
| 468 |
+
- **HTTPS connection** (secure website)
|
| 469 |
+
- **GPS-enabled device** (phone, tablet, or laptop with location services)
|
| 470 |
+
- **Browser permission** for location access
|
| 471 |
+
|
| 472 |
+
---
|
| 473 |
+
|
| 474 |
+
**Manual Alternative:** Enter your city name in the field above if you prefer not to share your location.
|
| 475 |
+
|
| 476 |
+
**Date:** {datetime.datetime.now().strftime('%B %d, %Y at %I:%M %p')}
|
| 477 |
+
"""
|
| 478 |
+
|
| 479 |
def _format_success_response(self, data: Dict[str, Any], source: str, url: str = None) -> str:
|
| 480 |
"""Format successful response with Shabbat times"""
|
| 481 |
candle_time = data.get('candle_lighting_time', 'Not found')
|
|
|
|
| 598 |
city_input = gr.Textbox(
|
| 599 |
label="🏙️ City Name",
|
| 600 |
placeholder="Enter city name (e.g., 'New York', 'London, UK', 'Tel Aviv, Israel')",
|
| 601 |
+
info="Enter manually or use automatic location detection below",
|
| 602 |
lines=1
|
| 603 |
)
|
| 604 |
|
| 605 |
+
# Add IP-based geolocation detection
|
| 606 |
+
with gr.Row():
|
| 607 |
+
detect_ip_btn = gr.Button(
|
| 608 |
+
"🌐 Auto-Detect (IP Location)",
|
| 609 |
+
variant="secondary",
|
| 610 |
+
size="sm"
|
| 611 |
+
)
|
| 612 |
+
search_btn = gr.Button(
|
| 613 |
+
"🔍 Get Shabbat Times",
|
| 614 |
+
variant="primary",
|
| 615 |
+
size="sm"
|
| 616 |
+
)
|
| 617 |
|
| 618 |
gr.Markdown("""
|
| 619 |
+
### 💡 **How to Use:**
|
| 620 |
+
|
| 621 |
+
**🅰️ Manual Entry** (Most Accurate)
|
| 622 |
+
- Enter your city name above and click "Get Shabbat Times"
|
| 623 |
+
- Use format: "City, Country" for best results
|
| 624 |
+
|
| 625 |
+
**🅱️ Auto-Detection** (Uses Your IP Location)
|
| 626 |
+
- Click "Auto-Detect" to automatically find your approximate location
|
| 627 |
+
- ⚠️ Note: IP location shows your internet provider's area, not exact address
|
| 628 |
+
- May be off by 10-50 km from your actual location
|
| 629 |
|
| 630 |
### 🇮🇱 **Popular Israeli Cities:**
|
| 631 |
+
Jerusalem, Tel Aviv, Haifa, Beer Sheva, Netanya, Ashdod
|
|
|
|
| 632 |
|
| 633 |
### 🌍 **International Examples:**
|
| 634 |
London, UK • Paris, France • New York, USA • Toronto, Canada
|
|
|
|
| 640 |
value="""
|
| 641 |
# 🕯️ Welcome!
|
| 642 |
|
| 643 |
+
### 🎯 **Two Ways to Find Shabbat Times:**
|
| 644 |
|
| 645 |
+
**🅰️ Manual Entry** (Most Reliable)
|
| 646 |
+
👆 Enter your city name above and click "Get Shabbat Times"
|
| 647 |
+
|
| 648 |
+
**🅱️ Auto-Detection** (Uses IP Geolocation)
|
| 649 |
+
👆 Click "Auto-Detect" to find your approximate location via internet provider
|
| 650 |
|
| 651 |
### 🇮🇱 **Common Israeli Cities:**
|
| 652 |
+
Use: Jerusalem, Tel Aviv, Haifa, Beer Sheva, Netanya, Ashdod
|
| 653 |
|
| 654 |
### 🌍 **International Cities:**
|
| 655 |
+
Format: London, UK • Paris, France • New York, USA
|
| 656 |
|
| 657 |
---
|
| 658 |
|
| 659 |
**🕯️ Every Friday evening, Jewish families around the world light candles to welcome Shabbat**
|
| 660 |
+
|
| 661 |
+
**⚠️ Note:** Auto-detection uses IP location (approximate) - for best accuracy, enter city manually
|
| 662 |
"""
|
| 663 |
)
|
| 664 |
|
|
|
|
| 690 |
btn = gr.Button(f"📍 {city}", size="sm")
|
| 691 |
example_btns.append((btn, city))
|
| 692 |
|
| 693 |
+
# Button click handlers
|
| 694 |
search_btn.click(
|
| 695 |
fn=agent.get_shabbat_times,
|
| 696 |
+
inputs=[city_input, gr.State(False)], # auto_detect = False for manual search
|
| 697 |
+
outputs=output_area
|
| 698 |
+
)
|
| 699 |
+
|
| 700 |
+
# IP-based auto-detection
|
| 701 |
+
detect_ip_btn.click(
|
| 702 |
+
fn=agent.get_shabbat_times,
|
| 703 |
+
inputs=[gr.State(""), gr.State(True)], # empty city, auto_detect = True
|
| 704 |
outputs=output_area
|
| 705 |
)
|
| 706 |
|
| 707 |
# Example button handlers
|
| 708 |
for btn, city in example_btns:
|
| 709 |
btn.click(
|
| 710 |
+
fn=lambda c=city: agent.get_shabbat_times(c, False),
|
| 711 |
outputs=output_area
|
| 712 |
)
|
| 713 |
|
|
|
|
| 740 |
- Some communities light candles at different intervals before sunset
|
| 741 |
|
| 742 |
### 🔧 **Technical Features:**
|
| 743 |
+
- **Browser GPS Geolocation** - Uses your device's actual GPS hardware
|
| 744 |
+
- **Reverse Geocoding** - Converts GPS coordinates to city names
|
| 745 |
+
- **Multiple search strategies** for maximum success on Chabad.org
|
| 746 |
+
- **Intelligent pattern matching** to extract candle lighting times
|
| 747 |
+
- **Fallback to alternative sources** if needed
|
| 748 |
+
- **Privacy-focused** - location data never stored or shared
|
| 749 |
""")
|
| 750 |
|
| 751 |
gr.Markdown("""
|