yahya1912 commited on
Commit
273d7ce
·
verified ·
1 Parent(s): e660779

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +213 -33
app.py CHANGED
@@ -8,17 +8,94 @@ google_key = os.getenv("GOOGLE_MAPS_API_KEY")
8
 
9
  client = InferenceClient("Qwen/Qwen2.5-7B-Instruct", token=hf_token)
10
 
 
11
  SYSTEM_PROMPT = """
12
- Analyze the Arabic map query and return ONLY a JSON object.
13
- Keys: location, category, cuisine, features (list), sort_by (rating, price, distance).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  """
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  def search_google_maps(parsed_data):
17
  if not google_key:
18
- return "الرجاء إضافة GOOGLE_MAPS_API_KEY في ا��إعدادات."
19
 
20
- # بناء جملة البحث
21
- query = f"{parsed_data.get('category', '')} {parsed_data.get('cuisine', '')} {parsed_data.get('location', '')} {' '.join(parsed_data.get('features', []))}"
 
 
 
22
 
23
  url = "https://maps.googleapis.com/maps/api/place/textsearch/json"
24
  params = {
@@ -27,52 +104,155 @@ def search_google_maps(parsed_data):
27
  "key": google_key
28
  }
29
 
30
- response = requests.get(url, params=params)
31
- results = response.json().get("results", [])
32
-
33
- # ترتيب النتائج إذا طلب المستخدم (اختياري)
34
- if parsed_data.get("sort_by") == "rating":
35
- results = sorted(results, key=lambda x: x.get("rating", 0), reverse=True)
36
-
37
- # تنسيق النتائج للعرض
38
- output_list = []
39
- for place in results[:5]: # عرض أول 5 نتائج
40
- name = place.get("name")
41
- rating = place.get("rating", "N/A")
42
- address = place.get("formatted_address")
43
- output_list.append(f"📍 **{name}**\n⭐ التقييم: {rating}\n🏠 العنوان: {address}\n---")
44
 
45
- return "\n".join(output_list) if output_list else "لم يتم العثور على نتائج."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  def main_process(user_query):
 
 
 
48
  # 1. تحليل القصد باستخدام LLM
49
  try:
50
- resp = client.chat.completions.create(
51
- messages=[{"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_query}],
52
- max_tokens=200, temperature=0.1
 
 
 
 
53
  )
54
- match = re.search(r'\{.*\}', resp.choices[0].message.content, re.DOTALL)
55
- if not match: return "خطأ في تحليل البيانات", "لا توجد نتائج"
 
 
 
 
 
 
 
 
56
  parsed_data = json.loads(match.group(0))
57
 
58
- # 2. البحث في جوجل باستخدام البيانات المستخرجة
 
 
 
 
59
  map_results = search_google_maps(parsed_data)
60
 
61
  return parsed_data, map_results
 
 
 
62
  except Exception as e:
63
- return {"error": str(e)}, "فشل في جلب البيانات"
 
 
64
 
65
- # واجهة المستخدم
66
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
67
- gr.Markdown("# 🗺️ تطبيق البحث الذكي في الخرائط")
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  with gr.Row():
69
- input_text = gr.Textbox(label="ماذا تبحث عنه؟", placeholder="مثال: مطعم شاورما في الرياض يكون نظيف وهادي")
 
 
 
70
 
71
- btn = gr.Button("ابحث الآن 🔍", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
  with gr.Row():
74
- output_json = gr.JSON(label="تحليل الذكاء الاصطناعي")
75
- output_map = gr.Markdown(label="نتائج Google Maps")
76
 
77
  btn.click(fn=main_process, inputs=input_text, outputs=[output_json, output_map])
78
 
 
8
 
9
  client = InferenceClient("Qwen/Qwen2.5-7B-Instruct", token=hf_token)
10
 
11
+ # ==================== SYSTEM PROMPT المُحسَّن ====================
12
  SYSTEM_PROMPT = """
13
+ Analyze the Arabic map query and return ONLY a JSON object with these exact keys:
14
+
15
+ 1. "location": City, area, or "near me" if not specified.
16
+ 2. "category": The main place type. Use standard Arabic terms:
17
+ - Food: مطعم, مقهى, فرن, مخبز, سوق, بقالة, مطعم شاورما, مطعم برجر
18
+ - Shopping: مول, متجر, سوبرماركت, محل ملابس, صيدلية, محل إلكترونيات, محل أثاث
19
+ - Services: بنك, صراف آلي, محطة وقود, مغسلة, كوافير, ورشة سيارات, مكتب بريد
20
+ - Health: مستشفى, عيادة, صيدلية, طبيب أسنان, مركز صحي, طوارئ
21
+ - Education: مدرسة, جامعة, مكتبة, حضانة, معهد
22
+ - Entertainment: سينما, متحف, حديقة, ملعب, نادي رياضي, شاطئ, حديقة حيوان, منتزه
23
+ - Transportation: مطار, محطة قطار, محطة حافلات, موقف سيارات, تأجير سيارات, وقود
24
+ - Lodging: فندق, شقة فندقية, شاليه, مخيم, بيت ضيافة
25
+ - Religious: مسجد, كنيسة, معبد, جامع
26
+ - Government: ديوان بلدية, محكمة, مركز شرطة, سفارة, مركز حكومي
27
+ - Outdoor: شاطئ, ممر مشاة, بحيرة, غابة, جبل, منتزه طبيعي
28
+
29
+ 3. "sub_type": Specific type within category (e.g., "شاورما" for مطعم, "أطفال" for حديقة, "طوارئ" for مستشفى, "5 نجوم" for فندق) or null.
30
+
31
+ 4. "features": A list of ALL descriptive tags mentioned (e.g., ["رخيص", "قريب", "نظيف", "ممتاز", "24 ساعة", "مجاني", "هادئ", "عائلي", "فاخر", "سريع", "جيد"]).
32
+
33
+ 5. "sort_by": Standardized ranking signal. Use ONE of: rating, price, distance, relevance, open_now.
34
+
35
+ Rules:
36
+ - Extract ALL adjectives into "features", don't miss any.
37
+ - If user mentions "أقرب/قريب/أقرب مني" → sort_by: "distance"
38
+ - If user mentions "أحسن/أفضل/أعلى تقييم/ممتاز" → sort_by: "rating"
39
+ - If user mentions "أرخص/رخيص/سعر منخفض" → sort_by: "price"
40
+ - If user mentions "مفتوح الآن/مفتوح 24 ساعة" → sort_by: "open_now"
41
+ - Default to "relevance" if unclear.
42
+
43
+ Examples:
44
+ Input: "أرخص وأقرب مستشفى طوارئ في جدة"
45
+ Output: {"location": "جدة", "category": "مستشفى", "sub_type": "طوارئ", "features": ["أرخص", "أقرب"], "sort_by": "price"}
46
+
47
+ Input: "حديقة أطفال نظيفة في الرياض"
48
+ Output: {"location": "الرياض", "category": "حديقة", "sub_type": "أطفال", "features": ["نظيفة"], "sort_by": "relevance"}
49
+
50
+ Input: "أفضل فندق 5 نجوم قريب من المطار في دبي"
51
+ Output: {"location": "دبي", "category": "فندق", "sub_type": "5 نجوم", "features": ["أفضل", "قريب من المطار"], "sort_by": "rating"}
52
+
53
+ Input: "محطة وقود 24 ساعة رخيصة"
54
+ Output: {"location": "near me", "category": "محطة وقود", "sub_type": null, "features": ["24 ساعة", "رخيصة"], "sort_by": "price"}
55
+
56
+ Input: "مقهى هادئ للعمل في الرياض"
57
+ Output: {"location": "الرياض", "category": "مقهى", "sub_type": null, "features": ["هادئ", "للعمل"], "sort_by": "relevance"}
58
  """
59
 
60
+ # ==================== دوال البحث والمعالجة ====================
61
+
62
+ def build_search_query(parsed_data):
63
+ """بناء جملة بحث محسّنة من البيانات المستخرجة"""
64
+ parts = []
65
+
66
+ # نبدأ بالتصنيف الرئيسي
67
+ category = parsed_data.get("category", "")
68
+ if category:
69
+ parts.append(category)
70
+
71
+ # نضيف النوع الفرعي إذا موجود
72
+ sub_type = parsed_data.get("sub_type")
73
+ if sub_type and sub_type != category:
74
+ parts.append(str(sub_type))
75
+
76
+ # نضيف الموقع
77
+ location = parsed_data.get("location", "")
78
+ if location and location != "near me":
79
+ parts.append(f"في {location}")
80
+
81
+ # نضيف الصفات المهمة للبحث (نختار الأكثر صلة)
82
+ features = parsed_data.get("features", [])
83
+ # نصفات تساعد في البحث: 24 ساعة، طوارئ، أطفال، إلخ
84
+ search_features = [f for f in features if any(keyword in f for keyword in
85
+ ["24", "طوارئ", "أطفال", "مجاني", "مفتوح", "فاخر", "اقتصادي"])]
86
+ parts.extend(search_features)
87
+
88
+ return " ".join(parts)
89
+
90
  def search_google_maps(parsed_data):
91
  if not google_key:
92
+ return "⚠️ الرجاء إضافة GOOGLE_MAPS_API_KEY في إعدادات الـ Space."
93
 
94
+ # بناء جملة البحث المحسّنة
95
+ query = build_search_query(parsed_data)
96
+
97
+ if not query.strip():
98
+ query = f"{parsed_data.get('category', '')} {parsed_data.get('location', '')}"
99
 
100
  url = "https://maps.googleapis.com/maps/api/place/textsearch/json"
101
  params = {
 
104
  "key": google_key
105
  }
106
 
107
+ # إضافة معايير إضافية حسب نوع الترتيب
108
+ sort_by = parsed_data.get("sort_by", "relevance")
109
+ if sort_by == "distance":
110
+ # يمكن إضافة location bias لاحقاً إذا توفر GPS
111
+ pass
 
 
 
 
 
 
 
 
 
112
 
113
+ try:
114
+ response = requests.get(url, params=params, timeout=10)
115
+ data = response.json()
116
+
117
+ if data.get("status") != "OK":
118
+ error_msg = data.get("error_message", data.get("status", "Unknown error"))
119
+ return f"⚠️ خطأ في API: {error_msg}"
120
+
121
+ results = data.get("results", [])
122
+
123
+ if not results:
124
+ return "❌ لم يتم العثور على نتائج. جرب تعديل البحث."
125
+
126
+ # ترتيب النتائج حسب المعيار المطلوب
127
+ if sort_by == "rating":
128
+ results = sorted(results, key=lambda x: x.get("rating", 0), reverse=True)
129
+ elif sort_by == "price":
130
+ # Google Places API يعطي price_level (0-4)
131
+ results = sorted(results, key=lambda x: x.get("price_level", 4))
132
+
133
+ # تنسيق النتائج للعرض
134
+ output_list = []
135
+ for i, place in enumerate(results[:6], 1): # عرض أول 6 نتائج
136
+ name = place.get("name", "غير معروف")
137
+ rating = place.get("rating", "غير متوفر")
138
+ total_ratings = place.get("user_ratings_total", 0)
139
+ address = place.get("formatted_address", "العنوان غير متوفر")
140
+
141
+ # تحديد مستوى السعر
142
+ price_level = place.get("price_level")
143
+ price_str = ""
144
+ if price_level:
145
+ price_str = "💰" * price_level
146
+
147
+ # حالة الافتتاح
148
+ open_now = place.get("opening_hours", {}).get("open_now")
149
+ status = ""
150
+ if open_now is True:
151
+ status = " ✅ مفتوح الآن"
152
+ elif open_now is False:
153
+ status = " ❌ مغلق الآن"
154
+
155
+ # أنواع الأماكن (types)
156
+ types = place.get("types", [])
157
+ place_type = ", ".join([t.replace("_", " ") for t in types[:2]])
158
+
159
+ card = f"""### {i}. 📍 {name}
160
+ ⭐ **التقييم:** {rating}/5 ({total_ratings} تقييم) {price_str}
161
+ 🏠 **العنوان:** {address}{status}
162
+ 🏷️ **النوع:** {place_type}
163
+ ---"""
164
+ output_list.append(card)
165
+
166
+ # إضافة ملخص البحث
167
+ summary = f"🔍 **نتائج البحث عن:** `{query}` | **الترتيب حسب:** {sort_by}\n\n"
168
+ return summary + "\n".join(output_list)
169
+
170
+ except requests.Timeout:
171
+ return "⏱️ انتهى وقت الانتظار. حاول مرة أخرى."
172
+ except Exception as e:
173
+ return f"❌ خطأ في الاتصال: {str(e)}"
174
 
175
  def main_process(user_query):
176
+ if not user_query.strip():
177
+ return {"error": "الرجاء إدخال استعلام"}, "لا توجد نتائج"
178
+
179
  # 1. تحليل القصد باستخدام LLM
180
  try:
181
+ resp = client.chat_completions.create(
182
+ messages=[
183
+ {"role": "system", "content": SYSTEM_PROMPT},
184
+ {"role": "user", "content": user_query}
185
+ ],
186
+ max_tokens=300,
187
+ temperature=0.1
188
  )
189
+
190
+ raw_content = resp.choices[0].message.content
191
+ match = re.search(r'\{.*\}', raw_content, re.DOTALL)
192
+
193
+ if not match:
194
+ return {
195
+ "error": "لم يتم العثور على JSON صالح",
196
+ "raw_response": raw_content
197
+ }, "خطأ في تحليل البيانات"
198
+
199
  parsed_data = json.loads(match.group(0))
200
 
201
+ # التحقق من وجود الأخطاء في البيانات المستخرجة
202
+ if "error" in parsed_data:
203
+ return parsed_data, "فشل في تحليل الاستعلام"
204
+
205
+ # 2. البحث في Google Maps
206
  map_results = search_google_maps(parsed_data)
207
 
208
  return parsed_data, map_results
209
+
210
+ except json.JSONDecodeError as e:
211
+ return {"error": f"خطأ في قراءة JSON: {str(e)}"}, "فشل في تحليل البيانات"
212
  except Exception as e:
213
+ return {"error": f"خطأ غير متوقع: {str(e)}"}, "فشل في جلب البيانات"
214
+
215
+ # ==================== واجهة المستخدم ====================
216
 
 
217
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
218
+ gr.Markdown("""
219
+ # 🗺️ تطبيق البحث الذكي في الخرائط
220
+ **يدعم جميع الفئات:** مطاعم، فنادق، مستشفيات، محطات وقود، حدائق، متاجر، وغيرها...
221
+ """)
222
+
223
+ with gr.Row():
224
+ input_text = gr.Textbox(
225
+ label="📝 ماذا تبحث عنه؟",
226
+ placeholder="مثال: أرخص مستشفى طوارئ في جدة، أو حديقة أطفال نظيفة في الرياض، أو فندق 5 نجوم في دبي",
227
+ lines=2
228
+ )
229
+
230
+ btn = gr.Button("🔍 ابحث الآن", variant="primary", scale=1)
231
+
232
  with gr.Row():
233
+ with gr.Column(scale=1):
234
+ output_json = gr.JSON(label="⚙️ تحليل الذكاء الاصطناعي")
235
+ with gr.Column(scale=2):
236
+ output_map = gr.Markdown(label="🗺️ نتائج Google Maps")
237
 
238
+ # أمثلة سريعة
239
+ gr.Markdown("### 💡 أمثلة للبحث:")
240
+ examples = [
241
+ "أرخص وأقرب مستشفى طوارئ في جدة",
242
+ "حديقة أطفال نظيفة في الرياض",
243
+ "أفضل فندق 5 نجوم في دبي",
244
+ "مقهى هادئ للعمل في الخبر",
245
+ "محطة وقود 24 ساعة رخيصة",
246
+ "صيدلية قريبة مني مفتوحة الآن"
247
+ ]
248
+
249
+ with gr.Row():
250
+ for i, ex in enumerate(examples[:3]):
251
+ gr.Button(ex, size="sm").click(lambda x=ex: x, None, input_text)
252
 
253
  with gr.Row():
254
+ for i, ex in enumerate(examples[3:]):
255
+ gr.Button(ex, size="sm").click(lambda x=ex: x, None, input_text)
256
 
257
  btn.click(fn=main_process, inputs=input_text, outputs=[output_json, output_map])
258