RozzaCreat commited on
Commit
dd3d387
·
verified ·
1 Parent(s): 6592fb8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +201 -51
app.py CHANGED
@@ -19,62 +19,193 @@ class ShabbatCandleLightingAgent:
19
 
20
  def get_user_location_simple(self) -> Optional[Dict[str, Any]]:
21
  """
22
- Get user's approximate location using free IP geolocation services
23
  """
24
  try:
25
- # Try multiple free IP geolocation services
26
  services = [
27
- "http://ip-api.com/json/",
28
- "https://ipapi.co/json/",
29
- "http://www.geoplugin.net/json.gp"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  ]
31
 
32
- for service_url in services:
 
 
33
  try:
34
- response = requests.get(service_url, timeout=5)
 
 
 
 
 
 
35
  if response.status_code == 200:
36
  data = response.json()
 
 
 
 
 
37
 
38
- # Handle different API response formats
39
- if service_url.startswith("http://ip-api.com"):
40
- if data.get('status') == 'success':
41
- return {
42
- 'city': data.get('city'),
43
- 'country': data.get('country'),
44
- 'region': data.get('regionName'),
45
- 'lat': data.get('lat'),
46
- 'lng': data.get('lon'),
47
- 'timezone': data.get('timezone')
48
- }
49
- elif service_url.startswith("https://ipapi.co"):
50
- if data.get('city'):
51
- return {
52
- 'city': data.get('city'),
53
- 'country': data.get('country_name'),
54
- 'region': data.get('region'),
55
- 'lat': data.get('latitude'),
56
- 'lng': data.get('longitude'),
57
- 'timezone': data.get('timezone')
58
- }
59
- elif service_url.startswith("http://www.geoplugin.net"):
60
- if data.get('geoplugin_city'):
61
- return {
62
- 'city': data.get('geoplugin_city'),
63
- 'country': data.get('geoplugin_countryName'),
64
- 'region': data.get('geoplugin_region'),
65
- 'lat': data.get('geoplugin_latitude'),
66
- 'lng': data.get('geoplugin_longitude'),
67
- 'timezone': data.get('geoplugin_timezone')
68
- }
69
  except Exception as e:
70
- print(f"Service {service_url} failed: {e}")
71
  continue
72
-
 
 
 
 
 
 
73
  except Exception as e:
74
  print(f"Location detection error: {e}")
75
 
76
  return None
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  def search_city_on_chabad(self, city_name: str) -> Optional[str]:
79
  """
80
  Search for a city on Chabad.org and get the specific URL for candle lighting times
@@ -270,20 +401,30 @@ class ShabbatCandleLightingAgent:
270
  """
271
  try:
272
  city_name = city_input.strip()
 
273
 
274
  # Auto-detect location if requested and no city provided
275
  if auto_detect and not city_name:
 
276
  location = self.get_user_location_simple()
277
  if location and location.get('city'):
278
  city_name = location['city']
279
  if location.get('country') and location['country'] not in city_name:
280
  city_name += f", {location['country']}"
 
 
 
 
 
 
 
 
281
 
282
  if not city_name:
283
- return self._format_error_response("❌ Please provide a city name or enable location detection.")
284
 
285
  # Search Chabad.org for the city
286
- print(f"Searching for Shabbat times in: {city_name}")
287
  city_url = self.search_city_on_chabad(city_name)
288
 
289
  result_data = None
@@ -298,14 +439,14 @@ class ShabbatCandleLightingAgent:
298
 
299
  if response.status_code == 200:
300
  result_data = self.extract_candle_lighting_times(response.text, city_name)
301
- print(f"Extraction result: {result_data}")
302
 
303
  except Exception as e:
304
- print(f"Chabad.org fetch error: {e}")
305
 
306
  # Try backup sources if Chabad.org didn't work well
307
  if not result_data or not result_data.get('found_data'):
308
- print("Trying backup sources...")
309
  backup_data = self.get_backup_times(city_name)
310
  if backup_data and backup_data.get('found_data'):
311
  result_data = backup_data
@@ -313,9 +454,11 @@ class ShabbatCandleLightingAgent:
313
 
314
  # Format and return the response
315
  if result_data and result_data.get('found_data'):
316
- return self._format_success_response(result_data, source, city_url)
 
317
  else:
318
- return self._format_not_found_response(city_name, city_url)
 
319
 
320
  except Exception as e:
321
  print(f"Main function error: {e}")
@@ -460,10 +603,17 @@ def create_shabbat_interface():
460
  )
461
 
462
  gr.Markdown("""
463
- ### 💡 **Tips:**
464
- - Use full city names for better results
465
- - Include country for common city names
466
- - Try nearest major city if yours isn't found
 
 
 
 
 
 
 
467
  """)
468
 
469
  with gr.Column(scale=2):
 
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 _choose_best_location(self, locations: list) -> Dict[str, Any]:
158
+ """Choose the best location from multiple results using consensus and quality scoring"""
159
+ if len(locations) == 1:
160
+ return locations[0]
161
+
162
+ # Score locations based on various factors
163
+ scored_locations = []
164
+
165
+ for loc in locations:
166
+ score = 0
167
+
168
+ # Accuracy scoring
169
+ accuracy_scores = {'high': 3, 'medium': 2, 'low': 1}
170
+ score += accuracy_scores.get(loc.get('accuracy', 'low'), 1)
171
+
172
+ # Completeness scoring (more complete data = higher score)
173
+ if loc.get('city'): score += 2
174
+ if loc.get('country'): score += 1
175
+ if loc.get('region'): score += 1
176
+ if loc.get('lat') and loc.get('lng'): score += 2
177
+ if loc.get('timezone'): score += 1
178
+
179
+ # Prefer certain sources
180
+ trusted_sources = ['ipapi.co', 'ip-api.com']
181
+ if loc.get('source') in trusted_sources:
182
+ score += 2
183
+
184
+ scored_locations.append((score, loc))
185
+
186
+ # Sort by score (highest first)
187
+ scored_locations.sort(key=lambda x: x[0], reverse=True)
188
+
189
+ # Check for consensus among top results
190
+ top_cities = [loc[1]['city'] for loc in scored_locations[:3] if loc[1].get('city')]
191
+
192
+ # If multiple services agree on the city, prefer that
193
+ if len(set(top_cities)) == 1:
194
+ best_location = scored_locations[0][1]
195
+ else:
196
+ # Take the highest scored location
197
+ best_location = scored_locations[0][1]
198
+
199
+ # Add confidence level
200
+ if len(top_cities) >= 2 and len(set(top_cities)) == 1:
201
+ best_location['confidence'] = 'high'
202
+ elif len(top_cities) >= 2:
203
+ best_location['confidence'] = 'medium'
204
+ else:
205
+ best_location['confidence'] = 'low'
206
+
207
+ return best_location
208
+
209
  def search_city_on_chabad(self, city_name: str) -> Optional[str]:
210
  """
211
  Search for a city on Chabad.org and get the specific URL for candle lighting times
 
401
  """
402
  try:
403
  city_name = city_input.strip()
404
+ location_info = ""
405
 
406
  # Auto-detect location if requested and no city provided
407
  if auto_detect and not city_name:
408
+ print("🔍 Attempting to detect your location...")
409
  location = self.get_user_location_simple()
410
  if location and location.get('city'):
411
  city_name = location['city']
412
  if location.get('country') and location['country'] not in city_name:
413
  city_name += f", {location['country']}"
414
+
415
+ # Add location detection info
416
+ confidence = location.get('confidence', 'unknown')
417
+ source = location.get('source', 'unknown')
418
+ location_info = f"\n**🌐 Auto-detected location:** {city_name} (confidence: {confidence}, source: {source})\n"
419
+
420
+ if confidence == 'low':
421
+ location_info += "⚠️ *Low confidence detection - please verify the city is correct*\n"
422
 
423
  if not city_name:
424
+ return self._format_error_response("❌ Не удалось определить ваше местоположение автоматически. Пожалуйста, введите название города вручную.")
425
 
426
  # Search Chabad.org for the city
427
+ print(f"🔍 Searching for Shabbat times in: {city_name}")
428
  city_url = self.search_city_on_chabad(city_name)
429
 
430
  result_data = None
 
439
 
440
  if response.status_code == 200:
441
  result_data = self.extract_candle_lighting_times(response.text, city_name)
442
+ print(f"📊 Extraction result: {result_data}")
443
 
444
  except Exception as e:
445
+ print(f"❌ Chabad.org fetch error: {e}")
446
 
447
  # Try backup sources if Chabad.org didn't work well
448
  if not result_data or not result_data.get('found_data'):
449
+ print("🔄 Trying backup sources...")
450
  backup_data = self.get_backup_times(city_name)
451
  if backup_data and backup_data.get('found_data'):
452
  result_data = backup_data
 
454
 
455
  # Format and return the response
456
  if result_data and result_data.get('found_data'):
457
+ response = self._format_success_response(result_data, source, city_url)
458
+ return location_info + response
459
  else:
460
+ response = self._format_not_found_response(city_name, city_url)
461
+ return location_info + response
462
 
463
  except Exception as e:
464
  print(f"Main function error: {e}")
 
603
  )
604
 
605
  gr.Markdown("""
606
+ ### 💡 **Советы по вводу города:**
607
+ - Используйте полные названия городов для лучших результатов
608
+ - Добавляйте страну для распространенных названий (напри��ер, "Париж, Франция")
609
+ - Попробуйте ближайший крупный город, если ваш не найден
610
+ - **Автоопределение может быть неточным** - проверяйте результат
611
+
612
+ ### 🌐 **Почему автоопределение может ошибаться:**
613
+ - IP-геолокация показывает местоположение интернет-провайдера
614
+ - VPN/прокси искажают реальное местоположение
615
+ - Мобильные сети часто показывают центр обслуживания
616
+ - Точность: ±10-50 км от реального местоположения
617
  """)
618
 
619
  with gr.Column(scale=2):