Spaces:
Sleeping
Sleeping
Download app.py from RozzaCreat/Shabbat: direct link, hf CLI and curl.
- Browser
- Download file 32.6 kB
-
https://huggingface.co/spaces/RozzaCreat/Shabbat/resolve/refs%2Fpr%2F1/app.py
- Command line
-
hf download hf://spaces/RozzaCreat/Shabbat@refs/pr/1/app.py
-
curl -L -o app.py https://huggingface.co/spaces/RozzaCreat/Shabbat/resolve/refs%2Fpr%2F1/app.py
32.6 kB
| import gradio as gr | |
| import requests | |
| import json | |
| import datetime | |
| import re | |
| from bs4 import BeautifulSoup | |
| from typing import Optional, Dict, Any | |
| from urllib.parse import quote, urljoin | |
| class ShabbatCandleLightingAgent: | |
| """ | |
| AI Agent for finding Shabbat candle lighting times worldwide using Chabad.org | |
| """ | |
| def __init__(self): | |
| self.base_url = "https://www.chabad.org" | |
| self.candle_lighting_url = "https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm" | |
| def get_user_ip_location(self) -> Optional[Dict[str, Any]]: | |
| """ | |
| Enhanced IP-based location detection using multiple high-quality services | |
| """ | |
| try: | |
| # Enhanced list of location services with better accuracy | |
| services = [ | |
| { | |
| "url": "https://ipapi.co/json/", | |
| "parser": "ipapi_co", | |
| "accuracy": "high" | |
| }, | |
| { | |
| "url": "http://ip-api.com/json/?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,query", | |
| "parser": "ip_api", | |
| "accuracy": "medium" | |
| }, | |
| { | |
| "url": "https://ipgeolocation.abstractapi.com/v1/?api_key=free", | |
| "parser": "abstractapi", | |
| "accuracy": "high" | |
| }, | |
| { | |
| "url": "https://freegeoip.app/json/", | |
| "parser": "freegeoip", | |
| "accuracy": "medium" | |
| }, | |
| { | |
| "url": "https://api.ipify.org?format=json", | |
| "parser": "ipify_then_geocode", | |
| "accuracy": "low" | |
| } | |
| ] | |
| best_result = None | |
| all_results = [] | |
| for service in services: | |
| try: | |
| headers = { | |
| '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', | |
| 'Accept': 'application/json', | |
| 'Accept-Language': 'en-US,en;q=0.9' | |
| } | |
| response = requests.get(service["url"], headers=headers, timeout=8) | |
| if response.status_code == 200: | |
| data = response.json() | |
| parsed_location = self._parse_ip_location_data(data, service["parser"]) | |
| if parsed_location and parsed_location.get('city'): | |
| parsed_location['accuracy_level'] = service["accuracy"] | |
| parsed_location['source'] = service["parser"] | |
| all_results.append(parsed_location) | |
| print(f"✅ {service['parser']}: {parsed_location['city']}, {parsed_location.get('country', '')}") | |
| # Keep the best result (prefer high accuracy sources) | |
| if not best_result or service["accuracy"] == "high": | |
| best_result = parsed_location | |
| except Exception as e: | |
| print(f"❌ Service {service['parser']} failed: {e}") | |
| continue | |
| # Return best result with confidence assessment | |
| if best_result: | |
| best_result['all_results'] = all_results | |
| best_result['confidence'] = self._assess_location_confidence(all_results) | |
| print(f"🎯 Best location: {best_result['city']}, {best_result.get('country', '')} (confidence: {best_result['confidence']})") | |
| return best_result | |
| except Exception as e: | |
| print(f"Location detection error: {e}") | |
| return None | |
| def _parse_ip_location_data(self, data: dict, parser_type: str) -> Optional[Dict[str, Any]]: | |
| """Parse location data from different IP geolocation APIs""" | |
| try: | |
| if parser_type == "ipapi_co": | |
| if data.get('city') and not data.get('error'): | |
| return { | |
| 'city': data.get('city'), | |
| 'country': data.get('country_name'), | |
| 'region': data.get('region'), | |
| 'lat': data.get('latitude'), | |
| 'lng': data.get('longitude'), | |
| 'timezone': data.get('timezone'), | |
| 'isp': data.get('org') | |
| } | |
| elif parser_type == "ip_api": | |
| if data.get('status') == 'success': | |
| return { | |
| 'city': data.get('city'), | |
| 'country': data.get('country'), | |
| 'region': data.get('regionName'), | |
| 'lat': data.get('lat'), | |
| 'lng': data.get('lon'), | |
| 'timezone': data.get('timezone'), | |
| 'isp': data.get('isp') | |
| } | |
| elif parser_type == "abstractapi": | |
| if data.get('city') and not data.get('error'): | |
| return { | |
| 'city': data.get('city'), | |
| 'country': data.get('country'), | |
| 'region': data.get('region'), | |
| 'lat': data.get('latitude'), | |
| 'lng': data.get('longitude'), | |
| 'timezone': data.get('timezone', {}).get('name') if isinstance(data.get('timezone'), dict) else data.get('timezone'), | |
| 'isp': data.get('connection', {}).get('isp_name') if isinstance(data.get('connection'), dict) else None | |
| } | |
| elif parser_type == "freegeoip": | |
| if data.get('city'): | |
| return { | |
| 'city': data.get('city'), | |
| 'country': data.get('country_name'), | |
| 'region': data.get('region_name'), | |
| 'lat': data.get('latitude'), | |
| 'lng': data.get('longitude'), | |
| 'timezone': data.get('time_zone') | |
| } | |
| except Exception as e: | |
| print(f"Parse error for {parser_type}: {e}") | |
| return None | |
| def _assess_location_confidence(self, all_results: list) -> str: | |
| """Assess confidence level based on multiple results""" | |
| if len(all_results) == 0: | |
| return "none" | |
| elif len(all_results) == 1: | |
| return "low" | |
| # Check if multiple services agree on the city | |
| cities = [result.get('city', '').lower() for result in all_results if result.get('city')] | |
| unique_cities = set(cities) | |
| if len(unique_cities) == 1 and len(cities) >= 2: | |
| return "high" | |
| elif len(unique_cities) <= 2 and len(cities) >= 2: | |
| return "medium" | |
| else: | |
| return "low" | |
| """ | |
| Search for a city on Chabad.org and get the specific URL for candle lighting times | |
| """ | |
| try: | |
| # Clean and format city name | |
| city_clean = city_name.strip() | |
| search_query = quote(city_clean) | |
| headers = { | |
| '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', | |
| 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', | |
| 'Accept-Language': 'en-US,en;q=0.5', | |
| 'Accept-Encoding': 'gzip, deflate', | |
| 'Connection': 'keep-alive', | |
| 'Upgrade-Insecure-Requests': '1' | |
| } | |
| # Try different search approaches for Chabad.org | |
| search_strategies = [ | |
| # Direct city page approach | |
| f"https://www.chabad.org/calendar/candlelighting_cdo/jewish/{search_query}.htm", | |
| # Search with city parameter | |
| f"https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm?city={search_query}", | |
| # Alternative format | |
| f"https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Candle-Lighting-Times-{search_query}.htm", | |
| # General search | |
| f"https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm" | |
| ] | |
| for url in search_strategies: | |
| try: | |
| response = requests.get(url, headers=headers, timeout=15) | |
| if response.status_code == 200: | |
| # Check if the response contains candle lighting information | |
| content_lower = response.text.lower() | |
| if any(keyword in content_lower for keyword in ['candle lighting', 'light candles', 'shabbat', 'friday']): | |
| return url | |
| except Exception as e: | |
| print(f"Failed to fetch {url}: {e}") | |
| continue | |
| # Try POST request to the main search page | |
| try: | |
| post_data = { | |
| 'city': city_clean, | |
| 'submit': 'Get Times' | |
| } | |
| response = requests.post(self.candle_lighting_url, data=post_data, headers=headers, timeout=15) | |
| if response.status_code == 200 and 'candle lighting' in response.text.lower(): | |
| return response.url | |
| except Exception as e: | |
| print(f"POST request failed: {e}") | |
| except Exception as e: | |
| print(f"City search error: {e}") | |
| return self.candle_lighting_url # Return main page as fallback | |
| def extract_candle_lighting_times(self, html_content: str, city_name: str) -> Dict[str, Any]: | |
| """ | |
| Extract candle lighting times from Chabad.org HTML content using multiple strategies | |
| """ | |
| try: | |
| soup = BeautifulSoup(html_content, 'html.parser') | |
| text = soup.get_text().lower() | |
| # Multiple patterns to find candle lighting times | |
| candle_patterns = [ | |
| r'candle\s+lighting[:\s]*(\d{1,2}:\d{2}(?:\s*[ap]m)?)', | |
| r'light\s+candles[:\s]*(\d{1,2}:\d{2}(?:\s*[ap]m)?)', | |
| r'friday[:\s]*(\d{1,2}:\d{2}(?:\s*[ap]m)?)', | |
| r'(\d{1,2}:\d{2}\s*[ap]m?)(?=.*candle)', | |
| r'(\d{1,2}:\d{2}\s*[ap]m?)(?=.*light)', | |
| r'(\d{1,2}:\d{2}\s*[ap]m?)(?=.*friday)' | |
| ] | |
| # Patterns for Havdalah times | |
| havdalah_patterns = [ | |
| r'havdalah[:\s]*(\d{1,2}:\d{2}(?:\s*[ap]m)?)', | |
| r'saturday[:\s]*(\d{1,2}:\d{2}(?:\s*[ap]m)?)', | |
| r'(\d{1,2}:\d{2}\s*[ap]m?)(?=.*havdalah)', | |
| r'(\d{1,2}:\d{2}\s*[ap]m?)(?=.*saturday)' | |
| ] | |
| # Find candle lighting time | |
| candle_time = None | |
| for pattern in candle_patterns: | |
| match = re.search(pattern, text, re.IGNORECASE) | |
| if match: | |
| candle_time = match.group(1) | |
| break | |
| # Find Havdalah time | |
| havdalah_time = None | |
| for pattern in havdalah_patterns: | |
| match = re.search(pattern, text, re.IGNORECASE) | |
| if match: | |
| havdalah_time = match.group(1) | |
| break | |
| # Look for times in common HTML elements | |
| if not candle_time or not havdalah_time: | |
| # Check for time elements with specific classes or IDs | |
| time_selectors = [ | |
| '[class*="time"]', | |
| '[class*="candle"]', | |
| '[class*="light"]', | |
| '[id*="time"]', | |
| '[id*="candle"]', | |
| 'td', 'span', 'div' | |
| ] | |
| for selector in time_selectors: | |
| elements = soup.select(selector) | |
| for element in elements: | |
| element_text = element.get_text().strip() | |
| time_match = re.search(r'\d{1,2}:\d{2}(?:\s*[ap]m)?', element_text, re.IGNORECASE) | |
| if time_match: | |
| time_found = time_match.group() | |
| if not candle_time and any(word in element_text.lower() for word in ['candle', 'light', 'friday']): | |
| candle_time = time_found | |
| elif not havdalah_time and any(word in element_text.lower() for word in ['havdalah', 'saturday']): | |
| havdalah_time = time_found | |
| # Calculate current Shabbat dates | |
| now = datetime.datetime.now() | |
| days_until_friday = (4 - now.weekday()) % 7 | |
| if days_until_friday == 0 and now.hour >= 18: # If it's Friday evening, get next Friday | |
| days_until_friday = 7 | |
| friday = now + datetime.timedelta(days=days_until_friday) | |
| saturday = friday + datetime.timedelta(days=1) | |
| return { | |
| 'city': city_name, | |
| 'candle_lighting_time': candle_time, | |
| 'havdalah_time': havdalah_time, | |
| 'friday_date': friday.strftime('%B %d, %Y'), | |
| 'saturday_date': saturday.strftime('%B %d, %Y'), | |
| 'found_data': bool(candle_time or havdalah_time), | |
| 'search_successful': True | |
| } | |
| except Exception as e: | |
| print(f"Time extraction error: {e}") | |
| return { | |
| 'city': city_name, | |
| 'candle_lighting_time': None, | |
| 'havdalah_time': None, | |
| 'friday_date': None, | |
| 'saturday_date': None, | |
| 'found_data': False, | |
| 'search_successful': False, | |
| 'error': str(e) | |
| } | |
| def get_backup_times(self, city_name: str) -> Optional[Dict[str, Any]]: | |
| """ | |
| Try alternative sources for Shabbat times when Chabad.org fails | |
| """ | |
| try: | |
| # Alternative Jewish calendar websites | |
| backup_urls = [ | |
| f"https://www.hebcal.com/shabbat?city={quote(city_name)}", | |
| f"https://www.myzmanim.com/search.aspx?city={quote(city_name)}" | |
| ] | |
| headers = { | |
| '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' | |
| } | |
| for url in backup_urls: | |
| try: | |
| response = requests.get(url, headers=headers, timeout=10) | |
| if response.status_code == 200: | |
| times = self.extract_candle_lighting_times(response.text, city_name) | |
| if times.get('found_data'): | |
| times['source'] = 'backup' | |
| return times | |
| except Exception as e: | |
| print(f"Backup source {url} failed: {e}") | |
| continue | |
| except Exception as e: | |
| print(f"Backup sources error: {e}") | |
| return None | |
| def get_shabbat_times(self, city_input: str = "", auto_detect: bool = False) -> str: | |
| """ | |
| Main function to get Shabbat candle lighting times | |
| """ | |
| try: | |
| city_name = city_input.strip() | |
| location_info = "" | |
| # Auto-detect location using enhanced IP geolocation if requested | |
| if auto_detect and not city_name: | |
| print("🔍 Attempting to detect your location using IP geolocation...") | |
| location = self.get_user_ip_location() | |
| if location and location.get('city'): | |
| city_name = location['city'] | |
| if location.get('country') and location['country'] not in city_name: | |
| city_name += f", {location['country']}" | |
| # Add location detection info | |
| confidence = location.get('confidence', 'unknown') | |
| source = location.get('source', 'unknown') | |
| if confidence == 'high': | |
| status_icon = "✅" | |
| status_text = "High confidence" | |
| elif confidence == 'medium': | |
| status_icon = "⚠️" | |
| status_text = "Medium confidence - please verify" | |
| else: | |
| status_icon = "🔍" | |
| status_text = "Low confidence - may be inaccurate" | |
| location_info = f""" | |
| **🌐 IP-Based Location Detection:** | |
| {status_icon} **Detected:** {city_name} | |
| **Confidence:** {status_text} | |
| **Source:** {source} | |
| {('⚠️ *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*')} | |
| --- | |
| """ | |
| if not city_name: | |
| return self._format_error_response("❌ Please enter a city name or enable auto-detection to search for Shabbat times.") | |
| # Search Chabad.org for the city | |
| print(f"🔍 Searching for Shabbat times in: {city_name}") | |
| city_url = self.search_city_on_chabad(city_name) | |
| result_data = None | |
| source = "Chabad.org" | |
| if city_url: | |
| try: | |
| headers = { | |
| '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' | |
| } | |
| response = requests.get(city_url, headers=headers, timeout=15) | |
| if response.status_code == 200: | |
| result_data = self.extract_candle_lighting_times(response.text, city_name) | |
| print(f"📊 Extraction result: {result_data}") | |
| except Exception as e: | |
| print(f"❌ Chabad.org fetch error: {e}") | |
| # Try backup sources if Chabad.org didn't work well | |
| if not result_data or not result_data.get('found_data'): | |
| print("🔄 Trying backup sources...") | |
| backup_data = self.get_backup_times(city_name) | |
| if backup_data and backup_data.get('found_data'): | |
| result_data = backup_data | |
| source = "Alternative Jewish calendar source" | |
| # Format and return the response | |
| if result_data and result_data.get('found_data'): | |
| response = self._format_success_response(result_data, source, city_url) | |
| return location_info + response | |
| else: | |
| response = self._format_not_found_response(city_name, city_url) | |
| return location_info + response | |
| except Exception as e: | |
| print(f"Main function error: {e}") | |
| return self._format_error_response(f"❌ An error occurred: {str(e)}") | |
| def _format_geolocation_instructions(self) -> str: | |
| """Instructions for using browser geolocation""" | |
| return f""" | |
| # 🕯️ Shabbat Candle Lighting Times | |
| ## 📍 Automatic Location Detection | |
| To automatically detect your location, we need access to your device's GPS. Please follow these steps: | |
| ### 🔒 **Step 1: Enable Location Access** | |
| 1. Click the **"🌐 Get My Location"** button below | |
| 2. Your browser will ask for permission to access your location | |
| 3. Click **"Allow"** in the permission popup | |
| 4. Wait for your coordinates to be detected | |
| ### 📱 **Step 2: Location will be converted to city** | |
| Once we get your GPS coordinates, we'll: | |
| - Convert them to the nearest city name | |
| - Automatically search for Shabbat times | |
| - Display the results for your location | |
| ### ⚠️ **Privacy Note:** | |
| - Your location is only used to find the nearest city | |
| - No location data is stored or shared | |
| - You can deny permission and enter your city manually instead | |
| ### 🌐 **Location Requirements:** | |
| - **HTTPS connection** (secure website) | |
| - **GPS-enabled device** (phone, tablet, or laptop with location services) | |
| - **Browser permission** for location access | |
| --- | |
| **Manual Alternative:** Enter your city name in the field above if you prefer not to share your location. | |
| **Date:** {datetime.datetime.now().strftime('%B %d, %Y at %I:%M %p')} | |
| """ | |
| def _format_success_response(self, data: Dict[str, Any], source: str, url: str = None) -> str: | |
| """Format successful response with Shabbat times""" | |
| candle_time = data.get('candle_lighting_time', 'Not found') | |
| havdalah_time = data.get('havdalah_time', 'Not found') | |
| response = f""" | |
| # 🕯️ Shabbat Candle Lighting Times | |
| ## 📍 Location: {data.get('city', 'Unknown')} | |
| ### 🕯️ **Candle Lighting (Friday)** | |
| **Time:** {candle_time} | |
| **Date:** {data.get('friday_date', 'This Friday')} | |
| ### ⭐ **Havdalah (Saturday)** | |
| **Time:** {havdalah_time} | |
| **Date:** {data.get('saturday_date', 'This Saturday')} | |
| --- | |
| ## 📋 **Important Notes:** | |
| - 🕯️ Light Shabbat candles **18 minutes before sunset** on Friday | |
| - ⭐ Havdalah is performed after **3 stars appear** on Saturday night | |
| - 🌍 Times are specific to your geographic location | |
| - ⏰ Always verify times with your local rabbi or Jewish community | |
| ## 📚 **Source Information:** | |
| **Data from:** {source} | |
| **Retrieved:** {datetime.datetime.now().strftime('%B %d, %Y at %I:%M %p')} | |
| {f"**URL:** {url}" if url else ""} | |
| --- | |
| ## 🙏 **Shabbat Shalom!** | |
| *May your Shabbat be filled with peace, joy, and spiritual connection.* | |
| **🔔 Reminder:** Set an alarm 30 minutes before candle lighting time to prepare properly. | |
| """ | |
| return response | |
| def _format_not_found_response(self, city_name: str, url: str = None) -> str: | |
| """Format response when times are not found""" | |
| return f""" | |
| # 🕯️ Shabbat Candle Lighting Times | |
| ## ❌ Times Not Found for {city_name} | |
| We searched multiple sources but couldn't find specific Shabbat candle lighting times for **{city_name}**. | |
| ### 🔍 **What you can try:** | |
| 1. **Check spelling** - Make sure the city name is spelled correctly | |
| 2. **Add country** - Try "City, Country" format (e.g., "Paris, France") | |
| 3. **Use major city** - Try the nearest major city instead | |
| 4. **Alternative spellings** - Try different variations of the city name | |
| ### 📍 **General Guidelines for {city_name}:** | |
| - 🕯️ **Candle lighting:** Typically 18 minutes before sunset on Friday | |
| - ⭐ **Havdalah:** Approximately 25-42 minutes after sunset on Saturday | |
| - 🌍 **Location matters:** Times vary significantly by geographic location | |
| ### 🌐 **Manual Resources:** | |
| - 📱 **Chabad.org:** [Direct search]({url or 'https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm'}) | |
| - 📱 **Hebcal.com:** Alternative Jewish calendar | |
| - 📞 **Local contacts:** Your synagogue or Chabad house | |
| ### 📞 **Local Resources:** | |
| - Contact your local Chabad house or synagogue | |
| - Check with your Jewish community center | |
| - Consult your rabbi for precise local customs | |
| --- | |
| **Retrieved:** {datetime.datetime.now().strftime('%B %d, %Y at %I:%M %p')} | |
| """ | |
| def _format_error_response(self, error_message: str) -> str: | |
| """Format error response""" | |
| return f""" | |
| # 🕯️ Shabbat Candle Lighting Times | |
| ## ⚠️ Error | |
| {error_message} | |
| ### 🔄 **Please try:** | |
| 1. **Refresh** and try again | |
| 2. **Check internet connection** | |
| 3. **Use a different city name** | |
| 4. **Visit Chabad.org directly:** [Candle Lighting Times](https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm) | |
| ### 📞 **Alternative:** | |
| Contact your local synagogue or Chabad house for accurate times. | |
| --- | |
| **Time:** {datetime.datetime.now().strftime('%B %d, %Y at %I:%M %p')} | |
| """ | |
| def create_shabbat_interface(): | |
| """Create the Gradio interface for the Shabbat Candle Lighting Agent""" | |
| agent = ShabbatCandleLightingAgent() | |
| with gr.Blocks(title="🕯️ Shabbat Candle Lighting Times", theme=gr.themes.Soft()) as app: | |
| gr.Markdown(""" | |
| # 🕯️ Shabbat Candle Lighting Times AI Agent | |
| **Get accurate Shabbat candle lighting and Havdalah times for any city worldwide** | |
| This AI agent searches Chabad.org and other reliable Jewish sources to find precise ritual times for your location. | |
| --- | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 📍 Location Input") | |
| city_input = gr.Textbox( | |
| label="🏙️ City Name", | |
| placeholder="Enter city name (e.g., 'New York', 'London, UK', 'Tel Aviv, Israel')", | |
| info="Enter manually or use automatic location detection below", | |
| lines=1 | |
| ) | |
| # Add IP-based geolocation detection | |
| with gr.Row(): | |
| detect_ip_btn = gr.Button( | |
| "🌐 Auto-Detect (IP Location)", | |
| variant="secondary", | |
| size="sm" | |
| ) | |
| search_btn = gr.Button( | |
| "🔍 Get Shabbat Times", | |
| variant="primary", | |
| size="sm" | |
| ) | |
| gr.Markdown(""" | |
| ### 💡 **How to Use:** | |
| **🅰️ Manual Entry** (Most Accurate) | |
| - Enter your city name above and click "Get Shabbat Times" | |
| - Use format: "City, Country" for best results | |
| **🅱️ Auto-Detection** (Uses Your IP Location) | |
| - Click "Auto-Detect" to automatically find your approximate location | |
| - ⚠️ Note: IP location shows your internet provider's area, not exact address | |
| - May be off by 10-50 km from your actual location | |
| ### 🇮🇱 **Popular Israeli Cities:** | |
| Jerusalem, Tel Aviv, Haifa, Beer Sheva, Netanya, Ashdod | |
| ### 🌍 **International Examples:** | |
| London, UK • Paris, France • New York, USA • Toronto, Canada | |
| """) | |
| with gr.Column(scale=2): | |
| output_area = gr.Markdown( | |
| label="📋 Shabbat Times Results", | |
| value=""" | |
| # 🕯️ Welcome! | |
| ### 🎯 **Two Ways to Find Shabbat Times:** | |
| **🅰️ Manual Entry** (Most Reliable) | |
| 👆 Enter your city name above and click "Get Shabbat Times" | |
| **🅱️ Auto-Detection** (Uses IP Geolocation) | |
| 👆 Click "Auto-Detect" to find your approximate location via internet provider | |
| ### 🇮🇱 **Common Israeli Cities:** | |
| Use: Jerusalem, Tel Aviv, Haifa, Beer Sheva, Netanya, Ashdod | |
| ### 🌍 **International Cities:** | |
| Format: London, UK • Paris, France • New York, USA | |
| --- | |
| **🕯️ Every Friday evening, Jewish families around the world light candles to welcome Shabbat** | |
| **⚠️ Note:** Auto-detection uses IP location (approximate) - for best accuracy, enter city manually | |
| """ | |
| ) | |
| # Example buttons for popular cities | |
| gr.Markdown("### 🌍 Quick Search - Popular Cities:") | |
| with gr.Row(): | |
| # Israeli cities first for the detected user | |
| popular_cities = [ | |
| "Jerusalem, Israel", | |
| "Tel Aviv, Israel", | |
| "Haifa, Israel", | |
| "Beer Sheva, Israel", | |
| "Netanya, Israel", | |
| "Ashdod, Israel", | |
| # International cities | |
| "New York, NY", | |
| "London, UK", | |
| "Paris, France", | |
| "Toronto, Canada", | |
| "Los Angeles, CA", | |
| "Miami, FL" | |
| ] | |
| example_btns = [] | |
| for i in range(0, len(popular_cities), 4): | |
| with gr.Row(): | |
| for city in popular_cities[i:i+4]: | |
| btn = gr.Button(f"📍 {city}", size="sm") | |
| example_btns.append((btn, city)) | |
| # Button click handlers | |
| search_btn.click( | |
| fn=agent.get_shabbat_times, | |
| inputs=[city_input, gr.State(False)], # auto_detect = False for manual search | |
| outputs=output_area | |
| ) | |
| # IP-based auto-detection | |
| detect_ip_btn.click( | |
| fn=agent.get_shabbat_times, | |
| inputs=[gr.State(""), gr.State(True)], # empty city, auto_detect = True | |
| outputs=output_area | |
| ) | |
| # Example button handlers | |
| for btn, city in example_btns: | |
| btn.click( | |
| fn=lambda c=city: agent.get_shabbat_times(c, False), | |
| outputs=output_area | |
| ) | |
| # Resources and information | |
| with gr.Accordion("📚 Resources & Information", open=False): | |
| gr.Markdown(""" | |
| ### 🕯️ **About Shabbat Candle Lighting:** | |
| **Candle Lighting Time:** | |
| - Performed on Friday evening before sunset | |
| - Typically 18 minutes before sunset (varies by community) | |
| - Marks the beginning of Shabbat | |
| - Usually done by the woman of the house | |
| **Havdalah Time:** | |
| - Performed on Saturday evening after sunset | |
| - When three medium-sized stars are visible in the sky | |
| - Marks the end of Shabbat | |
| - Includes blessings over wine, spices, and candle | |
| ### 🌐 **Data Sources:** | |
| - **Primary:** [Chabad.org](https://www.chabad.org/calendar/candlelighting_cdo/aid/6226/jewish/Accurate-Shabbat-Candle-Lighting-Times.htm) | |
| - **Backup:** Hebcal.com and other Jewish calendar sites | |
| - **Method:** Intelligent web scraping with multiple fallback strategies | |
| ### ⚠️ **Important Disclaimers:** | |
| - Always consult your local rabbi for specific customs | |
| - Times may vary based on local Jewish community practices | |
| - This tool is for reference only - not a substitute for religious guidance | |
| - Some communities light candles at different intervals before sunset | |
| ### 🔧 **Technical Features:** | |
| - **Browser GPS Geolocation** - Uses your device's actual GPS hardware | |
| - **Reverse Geocoding** - Converts GPS coordinates to city names | |
| - **Multiple search strategies** for maximum success on Chabad.org | |
| - **Intelligent pattern matching** to extract candle lighting times | |
| - **Fallback to alternative sources** if needed | |
| - **Privacy-focused** - location data never stored or shared | |
| """) | |
| gr.Markdown(""" | |
| --- | |
| **🙏 Shabbat Shalom!** | Built with ❤️ for the global Jewish community | |
| *For questions about Jewish law or local customs, please consult your rabbi or local Jewish community.* | |
| """) | |
| return app | |
| # Main execution | |
| if __name__ == "__main__": | |
| app = create_shabbat_interface() | |
| # Launch the application | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| debug=False, | |
| show_error=True | |
| ) |