Yassir Bendou commited on
Commit
8c899ae
1 Parent(s): 3e08f91
README.md CHANGED
@@ -6,7 +6,7 @@ colorTo: white
6
  sdk: streamlit
7
  sdk_version: 1.26.0
8
  app_file: app.py
9
- duplicated_from: nt3awnou/Nt3awnou-rescue-map
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
6
  sdk: streamlit
7
  sdk_version: 1.26.0
8
  app_file: app.py
9
+ pinned: true
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
__pycache__/utils.cpython-310.pyc ADDED
Binary file (3.08 kB). View file
 
app.py CHANGED
@@ -1,374 +1,298 @@
1
  import os
2
  import time
3
- import requests
 
4
  import folium
5
  import pandas as pd
6
- from datetime import datetime
7
  import streamlit as st
8
- from streamlit_folium import st_folium
9
- from utils import legend_macro
10
  from huggingface_hub import HfApi
11
- from folium import plugins
12
- import leafmap.foliumap as leafmap
13
-
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  TOKEN = os.environ.get("HF_TOKEN", None)
16
- api = HfApi(token=TOKEN)
17
- st.set_page_config(layout="wide", initial_sidebar_state="collapsed")
 
 
 
18
 
 
 
 
 
 
19
  if "sleep_time" not in st.session_state:
20
  st.session_state.sleep_time = 2
21
-
22
  if "auto_refresh" not in st.session_state:
23
  st.session_state.auto_refresh = False
24
 
25
  auto_refresh = st.sidebar.checkbox("Auto Refresh?", st.session_state.auto_refresh)
26
-
27
  if auto_refresh:
28
  number = st.sidebar.number_input("Refresh rate in seconds", value=st.session_state.sleep_time)
29
  st.session_state.sleep_time = number
30
 
31
- #Logo
32
- st.markdown(
33
- """
34
- <div style="text-align: center;">
35
- <img src="https://storage.googleapis.com/storage-annarabic/Nt3awnou(1).png" width="200" height="200">
36
- </div>
37
- """,
38
- unsafe_allow_html=True,
39
- )
40
 
41
- st.title("Nt3awnou نتعاونو ")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
- # middle slogan
44
- st.markdown(
45
- """
46
- <div style="text-align: center;">
47
- <h4>وَمَنْ أَحْيَاهَا فَكَأَنَّمَا أَحْيَا النَّاسَ جَمِيعاً</h4>
48
- </div>
49
- """,
50
- unsafe_allow_html=True,
51
- )
 
 
 
52
 
53
- # st.markdown(
54
- # """
55
- # <div style="text-align: right;">
56
-
57
- # نتعاونو هي منصة تعاونية لمساعدة الأفراد المتضررين من الزلزال الأخير في المغرب. مهمتنا هي تسهيل تقديم المساعدة في الوقت المناسب و بفاعلية و تنظيم لجميع المتضررين. كيفاش؟ كنعاونو الناس لي محتاجين للمساعدة إعلمونا بمكانهم و نوع المساعدة لي محتاجين ليها سواء عن طريق ملأ الاستمارة أو عن طريق إرسال تسجيل صوتي عبر واتساب إلى رقم مخصص. بعد معالجة هاد المعلومات، كنجمعوهم فخريطة كتمكن الجمعيات من تنظيم و استهداف تدخلاتهم بدقة باش توصل المساعدة للناس لي محتاجين في وقت أسرع. و كل جمعية قامت باللازم في منطقة معينة تقدر تعلمنا عن طريق ملأ استمارة مخصصة لهاد الأمر. هاد المعلومات كذلك كتضاف للخريطة باش باقي الجمعيات يتاجهو لمناطق أخرى مازال ماوصلاتهم مساعدة.
58
- # تحذير : نظرا لخطورة الطرقان بسبب الحجر اللي كيطيح من الجبال، ننصح المتطوعين اللي بغاو يساعدو المناطق المتضررة يتبرعو عن طريق الجمعيات المختصة.
59
-
60
- # nt3awnou@annarabic.com المتطوعين ليبغاو يعاونوا يقدرو يتصلوا معنا عبر البريد ✉️
61
- # https://forms.gle/nZNCUVog9ka2Vdqu6 : ساعدونا نبلغو الناس ليمحتاجين فهاد الاستمارة 📝<br>
62
- # https://forms.gle/PsNSuHHjTTgwQMmVA : الجمعيات لي عندهم تدخلات يقدرو يبلغونا عبر هاد الاستمار ة📝
63
- # </div>
64
- # """,
65
- # unsafe_allow_html=True,
66
- # )
67
-
68
-
69
- # st.markdown("""
70
- # <div style="text-align: left;">
71
- # Nt3awnou نتعاونو is a collaborative platform dedicated to aiding individuals impacted by the recent earthquake in Morocco. Our core mission is to streamline and coordinate timely assistance for everyone affected. How do we achieve this? We assist those in need by allowing them to communicate their location and the specific aid they require, either by completing a form or sending a voice message via WhatsApp to a designated number. Once we receive and process this information, it can be viewed in our dashboard, which allows NGOs to organize and precisely target their interventions, ensuring swift assistance reaches those in need. Any organization that has taken initiative in a particular area can notify us by completing a dedicated form. This data is also incorporated into the dashboard so that other NGOs can help affected areas that still haven't received help.
72
- # Warning : There are still rocks falling down the mountains, making the roads to the affected areas very dangerous. We advise volunteers to donate directly to specialized NGOs.
73
-
74
- # ✉️ You can contact us via email at nt3awnou@annarabic.com <br>
75
- # 📝 Help us report more people in need by filling this form: https://forms.gle/nZNCUVog9ka2Vdqu6 <br>
76
- # 📝 NGOs can report their interventions by filling this form: https://forms.gle/PsNSuHHjTTgwQMmVA
77
- # </div>
78
- # """,
79
- # unsafe_allow_html=True,
80
- # )
81
-
82
- # Have different streamlit tabs for each language
83
- st.sidebar.title("Language / اللغة")
84
- tab_ar, tab_en, tab_fr = st.tabs(["العربية", "English", "Français"])
85
 
86
- with tab_en:
87
- st.markdown("""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  <div style="text-align: left;">
89
- Nt3awnou نتعاونو is a collaborative platform dedicated to aiding individuals impacted by the recent earthquake in Morocco. Our core mission is to streamline and coordinate timely assistance for everyone affected. How do we achieve this? We assist those in need by allowing them to communicate their location and the specific aid they require, either by completing a form or sending a voice message via WhatsApp to a designated number. Once we receive and process this information, it can be viewed in our dashboard, which allows NGOs to organize and precisely target their interventions, ensuring swift assistance reaches those in need. Any organization that has taken initiative in a particular area can notify us by completing a dedicated form. This data is also incorporated into the dashboard so that other NGOs can help affected areas that still haven't received help.
90
- Warning : There are still rocks falling down the mountains, making the roads to the affected areas very dangerous. We advise volunteers to donate directly to specialized NGOs.
91
- ✉️ You can contact us via email at nt3awnou@annarabic.com <br>
92
- 📝 Help us report more people in need by filling this form: https://forms.gle/nZNCUVog9ka2Vdqu6 <br>
93
- 📝 NGOs can report their interventions by filling this form: https://forms.gle/PsNSuHHjTTgwQMmVA
94
  </div>
95
  """,
96
- unsafe_allow_html=True,
97
- )
98
- with tab_ar:
99
- st.markdown(
100
- """
101
- <div style="text-align: right;">
102
- نتعاونو هي منصة تعاونية لمساعدة الأفراد المتضررين من الزلزال الأخير في المغرب. مهمتنا هي تسهيل تقديم المساعدة في الوقت المناسب و بفاعلية و تنظيم لجميع المتضررين. كيفاش؟ كنعاونو الناس لي محتاجين للمساعدة إعلمونا بمكانهم و نوع المساعدة لي محتاجين ليها سواء عن طريق ملأ الاستمارة أو عن طريق إرسال تسجيل صوتي عبر واتساب إلى رقم مخصص. بعد معالجة هاد المعلومات، كنجمعوهم فخريطة كتمكن الجمعيات من تنظيم و استهداف تدخلاتهم بدقة باش توصل المساعدة للناس لي محتاجين في وقت أسرع. و كل جمعية قامت باللازم في منطقة معينة تقدر تعلمنا عن طريق ملأ استمارة مخصصة لهاد الأمر. هاد المعلومات كذلك كتضاف للخريطة باش باقي الجمعيات يتاجهو لمناطق أخرى مازال ماوصلاتهم مساعدة.
103
- تحذير : نظرا لخطورة الطرقان بسبب الحجر اللي كيطيح من الجبال، ننصح المتطوعين اللي بغاو يساعدو المناطق المتضررة يتبرعو عن طريق الجمعيات المختصة.
104
- nt3awnou@annarabic.com المتطوعين ليبغاو يعاونوا يقدرو يتصلوا معنا عبر البريد ✉️
105
- https://forms.gle/nZNCUVog9ka2Vdqu6 : ساعدونا نبلغو الناس ليمحتاجين فهاد الاستمارة 📝<br>
106
- https://forms.gle/PsNSuHHjTTgwQMmVA : الجمعيات لي عندهم تدخلات يقدرو يبلغونا عبر هاد الاستمار ة📝
107
- </div>
108
- """,
109
  unsafe_allow_html=True,
110
  )
111
 
112
- with tab_fr:
113
- st.markdown("""
114
- <div style="text-align: left;">
115
- Nt3awnou نتعاونو est une plateforme collaborative dédiée à l'aide aux personnes touchées par le récent tremblement de terre au Maroc. Notre mission principale est de rationaliser et de coordonner une assistance rapide pour toutes les personnes touchées. Comment y parvenons-nous ? Nous aidons les personnes dans le besoin en leur permettant de communiquer leur localisation et l'aide spécifique dont elles ont besoin, soit en remplissant un formulaire, soit en envoyant un message vocal via WhatsApp à un numéro désigné. Une fois reçues et traitées, ces informations peuvent être consultées dans notre tableau de bord, qui permet aux associations d'organiser et de cibler précisément leurs interventions, afin que l'aide parvienne rapidement à ceux qui en ont besoin. Toute organisation ayant pris une initiative dans une zone particulière peut nous en informer en remplissant un formulaire prévu à cet effet. Ces données sont également intégrées au tableau de bord afin que d'autres associations puissent aider les zones touchées qui n'ont pas encore reçu d'aide.
116
- Avertissement : Il y a encore des chutes de pierres dans les montagnes, ce qui rend les routes vers les zones touchées très dangereuses. Nous conseillons aux volontaires de faire des dons directement aux associations spécialisées.
117
-
118
- Vous pouvez nous contacter par courrier électronique à l'adresse suivante : nt3awnou@annarabic.com
119
- Aidez-nous à signaler plus de personnes dans le besoin en remplissant ce formulaire : https://forms.gle/nZNCUVog9ka2Vdqu6
120
- Les associations peuvent signaler leurs interventions en remplissant ce formulaire : https://forms.gle/PsNSuHHjTTgwQMmVA
121
- </div>
122
- """
123
- , unsafe_allow_html=True)
124
-
125
-
126
- session = requests.Session()
127
- @st.cache_data(persist=True)
128
- def parse_latlng_from_link(url):
129
- try:
130
- # extract latitude and longitude from gmaps link
131
- if "@" not in url:
132
- # We first need to get the redirect URL
133
- resp = session.head(url, allow_redirects=True)
134
- url = resp.url
135
- latlng = url.split('@')[1].split(',')[0:2]
136
- return [float(latlng[0]), float(latlng[1])]
137
- except Exception as e:
138
- print(f"Error parsing latlng from link: {e}")
139
- return None
140
-
141
- def parse_gg_sheet_interventions(url):
142
- url = url.replace("edit#gid=", "export?format=csv&gid=")
143
- print(url)
144
- df = pd.read_csv(url, on_bad_lines='skip')
145
- return df.assign(latlng=df.iloc[:, 3].apply(parse_latlng_from_link))
146
-
147
- def parse_gg_sheet(url):
148
- url = url.replace("edit#gid=", "export?format=csv&gid=")
149
- print(url)
150
- df = pd.read_csv(url, on_bad_lines='warn')
151
-
152
- # parse latlng (column 4) to [lat, lng]
153
- def parse_latlng(latlng):
154
- try:
155
- lat, lng = latlng.split(",")
156
- return [float(lat), float(lng)]
157
- except Exception as e:
158
- print(f"Error parsing latlng: {e}")
159
- return None
160
- if df.shape[1] > 4:
161
- df = df.assign(latlng=df.iloc[:, 4].apply(parse_latlng))
162
- return df
163
-
164
- df = parse_gg_sheet(
165
- "https://docs.google.com/spreadsheets/d/1gYoBBiBo1L18IVakHkf3t1fOGvHWb23loadyFZUeHJs/edit#gid=966953708"
166
- )
167
- interventions_df = parse_gg_sheet_interventions(
168
- "https://docs.google.com/spreadsheets/d/1eXOTqunOWWP8FRdENPs4cU9ulISm4XZWYJJNR1-SrwY/edit#gid=2089222765"
169
- )
170
 
171
- # select requests
172
- with tab_ar:
173
- headers_mapping = {
174
- "إغاثة": "Rescue/إغاثة",
175
- "مساعدة طبية": "Medical Assistance/مساعدة طبية",
176
- "مأوى": "Shelter/مأوى",
177
- "طعام وماء": "Food & Water/طعام وماء",
178
- "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Danger/مخاطر (تسرب الغاز، تلف في الخدمات العامة...)",
179
- }
180
- with tab_en:
181
- headers_mapping = {
182
- "إغاثة" : "Rescue | إغاثة | Secours",
183
- "مساعدة طبية": "Medical Assistance | مساعدة طبية | Assistance médicale",
184
- "مأوى": "Shelter | مأوى | Abri",
185
- "طعام وماء": "Food & Water | طعام وماء | Nourriture et eau",
186
- "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Danger | مخاطر (تسرب الغاز، تلف في الخدمات العامة...) | Danger",
187
- }
188
-
189
-
190
- colors_mapping = {
191
- "إغاثة": "red",
192
- "مساعدة طبية": "orange",
193
- "مأوى": "beige",
194
- "طعام وماء": "blue",
195
- "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "gray",
196
- }
197
- icon_mapping = {
198
- "إغاثة": "bell", # life ring icon for rescue
199
- "مساعدة طبية": "heart", # medical kit for medical assistance
200
- "مأوى": "home", # home icon for shelter
201
- "طعام وماء": "cutlery", # cutlery (fork and knife) for food & water
202
- "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Warning" # warning triangle for dangers
203
- }
204
- options = ["إغاثة", "مساعدة طبية", "مأوى", "طعام وماء", "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)"]
 
 
 
 
 
 
 
 
 
205
  selected_options = []
206
 
207
- with tab_en:
208
- st.markdown("👉 **Choose request type**")
209
- with tab_ar:
210
- st.markdown("👉 **اختر نوع الطلب**")
211
- with tab_fr:
212
- st.markdown("👉 **Choisissez le type de demande**")
213
 
 
214
  col1, col2, col3, col4, col5 = st.columns([2, 3, 2, 3, 4])
215
  cols = [col1, col2, col3, col4, col5]
216
 
217
  for i, option in enumerate(options):
218
- checked = cols[i].checkbox(headers_mapping[option], value=True)
219
  if checked:
220
  selected_options.append(option)
221
 
222
- df['id'] = df.index
223
- filtered_df = df[df['ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)'].isin(selected_options)]
224
- selected_headers = [headers_mapping[request] for request in selected_options]
225
-
226
- # select interventions
227
- show_interventions = st.checkbox("Display Interventions | عرض عمليات المساعدة | Afficher les interventions", value=True)
228
-
229
- m = leafmap.Map(
230
- center=[31.228674, -7.992047],
231
- zoom=8.5,
232
- height="800px",
233
- max_bounds=True,
234
- min_zoom=8.5,
235
- max_lat=35.628674,
236
- min_lat=29.628674,
237
- max_lng=-4.992047,
238
- min_lng=-10.992047
239
- )
240
 
241
- # Add a search bar to the map
242
- plugins.Geocoder(
243
- collapsed=False,
244
- position="topright",
245
- placeholder="Search | البحث",
246
- ).add_to(m)
247
-
248
- # Add Fullscreen button to the map
249
- plugins.Fullscreen(
250
- position='topright',
251
- title='Expand me | تكبير الخريطة',
252
- title_cancel='Exit me | تصغير الخريطة',
253
- force_separate_button=True
254
- ).add_to(m)
255
-
256
- #Satellite View from Mapbox
257
- tileurl = 'https://api.mapbox.com/styles/v1/phd2020/clmer2mra01d001pbgjkictpt/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoicGhkMjAyMCIsImEiOiJja29lZzFwZmUwNHkzMm5wMjZnYjVvcGltIn0.tE0ritrelQOyLdKUH6hgOw'
258
-
259
- folium.TileLayer(
260
- tiles = tileurl,
261
- attr = 'Satellite View',
262
- name = 'Satellite View | عرض القمر الصناعي',
263
- overlay = False,
264
- control = True
265
- ).add_to(m)
266
-
267
- # Add a LayerControl to the map to toggle between layers (Satellite View and Default One)
268
- folium.LayerControl().add_to(m)
269
-
270
- # Json with refreshed images from maxar
271
- mosaics = ['10300100CB8C5600'] #,'10300100CBAF7D00'] #,'10300100E5404900','10300100E5A0A100'] #,'10300100EB8B4500','10300100EBC1A000','10300100ECC53700','10300100ED11EA00','10300500CFF91800','10300500E4F91400','10300500E4F91500','10300500E4F91700','10300500E4F91800','10300500E4F91900','10400100797DAC00','1040010083289000','10400100889ABF00','104001008A8E9800','104001008B75BB00','1040050047DC6400','1040050057DC8500','1050050030DE8900']
272
- for i, mosaic_id in enumerate(mosaics):
273
- mosaic = f'https://open.gishub.org/maxar-open-data/datasets/Morocco-Earthquake-Sept-2023/{mosaic_id}.json'
274
- #m.add_stac_layer(mosaic, name=f'mosaic_id_{i}')
275
 
276
  if show_interventions:
277
- for index, row in interventions_df.iterrows():
278
- status = "Done ✅" if row[interventions_df.columns[5]]!="Intervention prévue dans le futur / Planned future intervention" else "Planned ⌛"
279
- color_mk = "green" if row[interventions_df.columns[5]]!="Intervention prévue dans le futur / Planned future intervention" else "pink"
280
- intervention_type = row[interventions_df.columns[6]].split("/")[0].strip()
281
- org = row[interventions_df.columns[1]]
282
- city = row[interventions_df.columns[9]]
283
- date = row[interventions_df.columns[4]]
284
- intervention_info = f"<b>Status:</b> {status}<br><b>Org:</b> {org}<br><b>Intervention:</b> {intervention_type}<br><b>📅 Date:</b> {date}"
285
- if row["latlng"] is None:
286
- continue
287
- folium.Marker(
288
- location=row["latlng"],
289
- tooltip=city,
290
- popup=folium.Popup(intervention_info, max_width=300),
291
- icon=folium.Icon(color=color_mk)
292
- ).add_to(m)
293
-
294
- for index, row in filtered_df.iterrows():
295
- request_type = row['ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)']
296
- long_lat = row["هل يمكنك تقديم الإحداثيات الدقيقة للموقع؟ (ادا كنت لا توجد بعين المكان) متلاً \n31.01837503440344, -6.781405948842175"]
297
- maps_url = f"https://maps.google.com/?q={long_lat}"
298
- display_text = f'<b>Request Type:</b> {request_type}<br><b>Id:</b> {row["id"]}<br><a href="{maps_url}" target="_blank" rel="noopener noreferrer"><b>Google Maps</b></a>'
299
- icon_name = icon_mapping.get(request_type, 'info-sign')
300
- if row["latlng"] is None:
301
- continue
302
-
303
- folium.Marker(
304
- location=row["latlng"],
305
- tooltip=row[' لأي جماعة / قيادة / دوار تنتمون ؟'] if not pd.isna(row[' لأي جماعة / قيادة / دوار تنتمون ؟']) else None,
306
- popup=folium.Popup(display_text, max_width=300),
307
- icon=folium.Icon(color=colors_mapping.get(request_type, "blue"), icon=icon_name)
308
- ).add_to(m)
309
-
310
 
311
- # Macro to add legend
312
- m.get_root().add_child(legend_macro)
313
 
314
  st_data = st_folium(m, use_container_width=True)
 
315
 
316
- # Google Sheet Table
317
- st.subheader("📝 **Table of requests / جدول الطلبات** https://forms.gle/nZNCUVog9ka2Vdqu6")
318
- st.markdown(
319
- """
320
- <iframe src="https://docs.google.com/spreadsheets/d/1gYoBBiBo1L18IVakHkf3t1fOGvHWb23loadyFZUeHJs/" width="100%" height="600px"></iframe>
321
- <br>
322
- """,
323
- unsafe_allow_html=True,
324
- )
325
 
326
- # Google Sheet Table
327
- st.subheader("📝 **Table of interventions / جدول عمليات المساعدة** https://forms.gle/PsNSuHHjTTgwQMmVA")
328
- st.markdown(
329
- """
330
- <iframe src="https://docs.google.com/spreadsheets/d/1eXOTqunOWWP8FRdENPs4cU9ulISm4XZWYJJNR1-SrwY/?single=true&gid=419657423&range=f2:g6&widget=false&chrome=false" width="100%" height="600px"></iframe>
331
- <br>
332
- """,
333
- unsafe_allow_html=True,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  )
335
 
336
  # Submit an id for review
337
- st.subheader("🔍 Review of requests")
338
- st.markdown("**If a request should be reviewed or dropped submit its id here/ إذا كان يجب مراجعة أو حذف طلب، أدخل رقمه هنا:**")
339
- st.markdown("If you intervened to solve the request, please fill this [form](https://docs.google.com/forms/d/e/1FAIpQLSe8D6T__DJDTVGMrIWMT-H-hQ0qDUWVOncKnrSXgv4NbwHCrQ/viewform)")
340
- st.markdown("[form](https://docs.google.com/forms/d/e/1FAIpQLSe8D6T__DJDTVGMrIWMT-H-hQ0qDUWVOncKnrSXgv4NbwHCrQ/viewform) إذا تدخلت لحل الطلب، يرجى ملء هذا النموذج الرابط")
341
-
342
- id_to_review = st.number_input("Enter id / أدخل الرقم", min_value=0, max_value=len(df), value=0, step=1)
343
- reason_for_review = st.text_area("Explain why / أدخل سبب المراجعة")
344
- if st.button("Submit / أرسل"):
345
- if reason_for_review == "":
346
- st.error("Please enter a reason / الرجاء إدخال سبب")
347
- else:
348
- filename = f"review_id_{id_to_review}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt"
349
- with open(filename, "w") as f:
350
- f.write(f"id: {id_to_review}, explanation: {reason_for_review}\n")
351
- api.upload_file(
352
- path_or_fileobj=filename,
353
- path_in_repo=filename,
354
- repo_id="nt3awnou/review_requests",
355
- repo_type="dataset",
356
- )
357
- st.success("Submitted at https://huggingface.co/datasets/nt3awnou/review_requests/ تم الإرسال")
358
-
359
 
360
 
361
  # Credits
362
  st.markdown(
363
- """
364
- <hr>
365
- <div style="text-align: center;">
366
- <p>By <b>Moroccans</b> for <b>Moroccans</b> 🤝</p>
367
- <p>Bot powered by <a href="https://www.annarabic.com/">Annarabic</a></p>
368
- <p>Collaboration made possible thanks to <a href="https://summerschool.morocco.ai/">AI Summer School</a></p>
369
- """,
370
  unsafe_allow_html=True,
371
  )
372
  if auto_refresh:
373
  time.sleep(number)
374
- st.experimental_rerun()
 
1
  import os
2
  import time
3
+ from datetime import datetime
4
+
5
  import folium
6
  import pandas as pd
 
7
  import streamlit as st
 
 
8
  from huggingface_hub import HfApi
9
+ from streamlit_folium import st_folium
10
+ from src.utils import add_latlng_col
11
+
12
+ from src.text_content import (
13
+ COLOR_MAPPING,
14
+ CREDITS_TEXT,
15
+ HEADERS_MAPPING,
16
+ ICON_MAPPING,
17
+ INTRO_TEXT_AR,
18
+ INTRO_TEXT_EN,
19
+ INTRO_TEXT_FR,
20
+ LOGO,
21
+ REVIEW_TEXT,
22
+ SLOGAN,
23
+ )
24
+ from src.utils import init_map, parse_gg_sheet
25
 
26
  TOKEN = os.environ.get("HF_TOKEN", None)
27
+ REQUESTS_URL = "https://docs.google.com/spreadsheets/d/1gYoBBiBo1L18IVakHkf3t1fOGvHWb23loadyFZUeHJs/edit#gid=966953708"
28
+ INTERVENTIONS_URL = (
29
+ "https://docs.google.com/spreadsheets/d/1eXOTqunOWWP8FRdENPs4cU9ulISm4XZWYJJNR1-SrwY/edit#gid=2089222765"
30
+ )
31
+ api = HfApi(TOKEN)
32
 
33
+
34
+ # Initialize Streamlit Config
35
+ st.set_page_config(layout="wide", initial_sidebar_state="collapsed", page_icon="🤝", page_title="Nt3awnou نتعاونو")
36
+
37
+ # Initialize States
38
  if "sleep_time" not in st.session_state:
39
  st.session_state.sleep_time = 2
 
40
  if "auto_refresh" not in st.session_state:
41
  st.session_state.auto_refresh = False
42
 
43
  auto_refresh = st.sidebar.checkbox("Auto Refresh?", st.session_state.auto_refresh)
 
44
  if auto_refresh:
45
  number = st.sidebar.number_input("Refresh rate in seconds", value=st.session_state.sleep_time)
46
  st.session_state.sleep_time = number
47
 
 
 
 
 
 
 
 
 
 
48
 
49
+ # Streamlit functions
50
+ def display_interventions(interventions_df, m):
51
+ """Display NGO interventions on the map"""
52
+ for index, row in interventions_df.iterrows():
53
+ status = (
54
+ "Done ✅"
55
+ if row[interventions_df.columns[5]] != "Intervention prévue dans le futur / Planned future intervention"
56
+ else "Planned ⌛"
57
+ )
58
+ color_mk = (
59
+ "green"
60
+ if row[interventions_df.columns[5]] != "Intervention prévue dans le futur / Planned future intervention"
61
+ else "pink"
62
+ )
63
+ intervention_type = row[interventions_df.columns[6]].split("/")[0].strip()
64
+ org = row[interventions_df.columns[1]]
65
+ city = row[interventions_df.columns[9]]
66
+ date = row[interventions_df.columns[4]]
67
+ intervention_info = f"<b>Status:</b> {status}<br><b>Org:</b> {org}<br><b>Intervention:</b> {intervention_type}<br><b>📅 Date:</b> {date}"
68
+ if row["latlng"] is None:
69
+ continue
70
+ folium.Marker(
71
+ location=row["latlng"],
72
+ tooltip=city,
73
+ popup=folium.Popup(intervention_info, max_width=300),
74
+ icon=folium.Icon(color=color_mk),
75
+ ).add_to(m)
76
+
77
 
78
+ def show_requests(filtered_df, m):
79
+ """Display victim requests on the map"""
80
+ for index, row in filtered_df.iterrows():
81
+ request_type = row["ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)"]
82
+ long_lat = row[
83
+ "هل يمكنك تقديم الإحداثيات الدقيقة للموقع؟ (ادا كنت لا توجد بعين المكان) متلاً \n31.01837503440344, -6.781405948842175"
84
+ ]
85
+ maps_url = f"https://maps.google.com/?q={long_lat}"
86
+ display_text = f'<b>Request Type:</b> {request_type}<br><b>Id:</b> {row["id"]}<br><a href="{maps_url}" target="_blank" rel="noopener noreferrer"><b>Google Maps</b></a>'
87
+ icon_name = ICON_MAPPING.get(request_type, "info-sign")
88
+ if row["latlng"] is None:
89
+ continue
90
 
91
+ folium.Marker(
92
+ location=row["latlng"],
93
+ tooltip=row[" لأي جماعة / قيادة / دوار تنتمون ؟"]
94
+ if not pd.isna(row[" لأي جماعة / قيادة / دوار تنتمون ؟"])
95
+ else None,
96
+ popup=folium.Popup(display_text, max_width=300),
97
+ icon=folium.Icon(color=COLOR_MAPPING.get(request_type, "blue"), icon=icon_name),
98
+ ).add_to(m)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+
101
+ def display_google_sheet_tables(data_url):
102
+ """Display the google sheet tables for requests and interventions"""
103
+ st.markdown(
104
+ f"""<iframe src="{data_url}" width="100%" height="600px"></iframe>""",
105
+ unsafe_allow_html=True,
106
+ )
107
+
108
+
109
+ def display_dataframe(df, drop_cols, data_url, search_id=True, status=False):
110
+ """Display the dataframe in a table"""
111
+ col_1, col_2 = st.columns([1, 1])
112
+
113
+ with col_1:
114
+ query = st.text_input(
115
+ "🔍 Search for information / بحث عن المعلومات",
116
+ key=f"search_requests_{int(search_id)}",
117
+ )
118
+ with col_2:
119
+ if search_id:
120
+ id_number = st.number_input(
121
+ "🔍 Search for an id / بحث عن رقم",
122
+ min_value=0,
123
+ max_value=len(filtered_df),
124
+ value=0,
125
+ step=1,
126
+ )
127
+ if status:
128
+ selected_status = st.selectbox(
129
+ "🗓️ Status / حالة",
130
+ ["all / الكل", "Done / تم", "Planned / مخطط لها"],
131
+ key="status",
132
+ )
133
+
134
+ if query:
135
+ # Filtering the dataframe based on the query
136
+ mask = df.apply(lambda row: row.astype(str).str.contains(query).any(), axis=1)
137
+ display_df = df[mask]
138
+ else:
139
+ display_df = df
140
+
141
+ display_df = display_df.drop(drop_cols, axis=1)
142
+
143
+ if search_id and id_number:
144
+ display_df = display_df[display_df["id"] == id_number]
145
+
146
+ if status:
147
+ target = "Pouvez-vous nous préciser si vous êtes déjà intervenus ou si vous prévoyez de le faire | Tell us if you already made the intervention, or if you're planning to do it"
148
+ if selected_status == "Done / تم":
149
+ display_df = display_df[display_df[target] == "Intervention déjà passée / Past intevention"]
150
+
151
+ elif selected_status == "Planned / مخطط لها":
152
+ display_df = display_df[display_df[target] != "Intervention déjà passée / Past intevention"]
153
+
154
+ st.dataframe(display_df, height=500)
155
+ st.markdown(
156
+ f"To view the full Google Sheet for advanced filtering go to: {data_url} **لعرض الورقة كاملة، اذهب إلى**"
157
+ )
158
+ # if we want to check hidden contact information
159
+ st.markdown(
160
+ f"We are hiding contact information to protect the privacy of the victims. If you are an NGO and want to contact the victims, please contact us at nt3awnoumorocco@gmail.com",
161
+ )
162
+ # arabic needs rtl
163
+ st.markdown(
164
+ f"""
165
  <div style="text-align: left;">
166
+ <a href="mailto:nt3awnoumorocco@gmail.com">nt3awnoumorocco@gmail.com</a> نحن نخفي معلومات الاتصال لحماية خصوصية الضحايا. إذا كنت جمعية وتريد الاتصال بالضحايا، يرجى الاتصال بنا على
 
 
 
 
167
  </div>
168
  """,
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  unsafe_allow_html=True,
170
  )
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
+ def id_review_submission():
174
+ """Id review submission form"""
175
+ st.subheader("🔍 Review of requests")
176
+ st.markdown(REVIEW_TEXT)
177
+
178
+ id_to_review = st.number_input("Enter id / أدخل الرقم", min_value=0, max_value=len(df), value=0, step=1)
179
+ reason_for_review = st.text_area("Explain why / أدخل سبب المراجعة")
180
+ if st.button("Submit / أرسل"):
181
+ if reason_for_review == "":
182
+ st.error("Please enter a reason / الرجاء إدخال سبب")
183
+ else:
184
+ filename = f"review_id_{id_to_review}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt"
185
+ with open(filename, "w") as f:
186
+ f.write(f"id: {id_to_review}, explanation: {reason_for_review}\n")
187
+ api.upload_file(
188
+ path_or_fileobj=filename,
189
+ path_in_repo=filename,
190
+ repo_id="nt3awnou/review_requests",
191
+ repo_type="dataset",
192
+ )
193
+ st.success("Submitted at https://huggingface.co/datasets/nt3awnou/review_requests/ تم الإرسال")
194
+
195
+
196
+ # Logo and Title
197
+ st.markdown(LOGO, unsafe_allow_html=True)
198
+ # st.title("Nt3awnou نتعاونو")
199
+ st.markdown(SLOGAN, unsafe_allow_html=True)
200
+
201
+ # Load data and initialize map with plugins
202
+ df = parse_gg_sheet(REQUESTS_URL)
203
+ df = add_latlng_col(df, process_column=15)
204
+ interventions_df = parse_gg_sheet(INTERVENTIONS_URL)
205
+ interventions_df = add_latlng_col(interventions_df, process_column=12)
206
+ m = init_map()
207
+
208
+ # Selection of requests
209
+ options = [
210
+ "إغاثة",
211
+ "مساعدة طبية",
212
+ "مأوى",
213
+ "طعام وماء",
214
+ "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)",
215
+ ]
216
  selected_options = []
217
 
218
+ # with tab_en:
219
+ # st.markdown("👉 **Choose request type**")
220
+ # with tab_ar:
221
+ # st.markdown("👉 **اختر نوع الطلب**")
222
+ # with tab_fr:
223
+ # st.markdown("👉 **Choisissez le type de demande**")
224
 
225
+ st.markdown("👉 **Choose request type | Choissisez le type de demande | اختر نوع الطلب**")
226
  col1, col2, col3, col4, col5 = st.columns([2, 3, 2, 3, 4])
227
  cols = [col1, col2, col3, col4, col5]
228
 
229
  for i, option in enumerate(options):
230
+ checked = cols[i].checkbox(HEADERS_MAPPING[option], value=True)
231
  if checked:
232
  selected_options.append(option)
233
 
234
+ df["id"] = df.index
235
+ filtered_df = df[df["ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)"].isin(selected_options)]
236
+ selected_headers = [HEADERS_MAPPING[request] for request in selected_options]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
 
238
+ # Selection of interventions
239
+ show_interventions = st.checkbox(
240
+ "Display Interventions | عرض عمليات المساعدة | Afficher les interventions",
241
+ value=True,
242
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
 
244
  if show_interventions:
245
+ # print(interventions_df.columns)
246
+ display_interventions(interventions_df, m)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
 
248
+ # Show requests
249
+ show_requests(filtered_df, m)
250
 
251
  st_data = st_folium(m, use_container_width=True)
252
+ tab_ar, tab_en, tab_fr = st.tabs(["العربية", "English", "Français"])
253
 
 
 
 
 
 
 
 
 
 
254
 
255
+ with tab_en:
256
+ st.markdown(INTRO_TEXT_EN, unsafe_allow_html=True)
257
+ with tab_ar:
258
+ st.markdown(INTRO_TEXT_AR, unsafe_allow_html=True)
259
+ with tab_fr:
260
+ st.markdown(INTRO_TEXT_FR, unsafe_allow_html=True)
261
+
262
+ # Requests table
263
+ st.divider()
264
+ st.subheader("📝 **Table of requests / جدول الطلبات**")
265
+ drop_cols = [
266
+ "(عند الامكان) رقم هاتف شخص موجود في عين المكان",
267
+ "الرجاء الضغط على الرابط التالي لمعرفة موقعك إذا كان متاحا",
268
+ "GeoStamp",
269
+ "GeoCode",
270
+ "GeoAddress",
271
+ "Status",
272
+ ]
273
+ display_dataframe(filtered_df, drop_cols, REQUESTS_URL, search_id=True)
274
+
275
+ # Interventions table
276
+ st.divider()
277
+ st.subheader("📝 **Table of interventions / جدول التدخلات**")
278
+ display_dataframe(
279
+ interventions_df,
280
+ [], # We show NGOs contact information
281
+ INTERVENTIONS_URL,
282
+ search_id=False,
283
+ status=True,
284
  )
285
 
286
  # Submit an id for review
287
+ st.divider()
288
+ id_review_submission()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
 
290
 
291
  # Credits
292
  st.markdown(
293
+ CREDITS_TEXT,
 
 
 
 
 
 
294
  unsafe_allow_html=True,
295
  )
296
  if auto_refresh:
297
  time.sleep(number)
298
+ st.experimental_rerun()
src/__pycache__/map_utils.cpython-310.pyc ADDED
Binary file (3.09 kB). View file
 
src/__pycache__/text_content.cpython-310.pyc ADDED
Binary file (7.34 kB). View file
 
src/__pycache__/utils.cpython-310.pyc ADDED
Binary file (4.22 kB). View file
 
src/map_utils.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from branca.element import Template, MacroElement
2
+
3
+
4
+ template = """
5
+ {% macro html(this, kwargs) %}
6
+
7
+ <!doctype html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="utf-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1">
12
+ <title>jQuery UI Draggable - Default functionality</title>
13
+ <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
14
+
15
+ <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
16
+ <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
17
+
18
+ <script>
19
+ $( function() {
20
+ $( "#maplegend" ).draggable({
21
+ start: function (event, ui) {
22
+ $(this).css({
23
+ right: "auto",
24
+ top: "auto",
25
+ bottom: "auto"
26
+ });
27
+ }
28
+ });
29
+ });
30
+
31
+ </script>
32
+ </head>
33
+ <body>
34
+
35
+
36
+ <div id='maplegend' class='maplegend'
37
+ style='position: absolute; z-index:9999; border:2px solid grey; background-color:rgba(255, 255, 255, 0.8);
38
+ border-radius:6px; padding: 10px; font-size:14px; right: 20px; bottom: 20px;'>
39
+
40
+ <div class='legend-title'>Legend / مفتاح الخريطة</div>
41
+ <div class='legend-scale'>
42
+ <ul class='legend-labels'>
43
+ <li><span style='background:#CE3C28;opacity:0.7;'></span>Rescue / إغاثة</li>
44
+ <li><span style='background:#ED922E;opacity:0.7;'></span>Medical Assistance / مساعدة طبية</li>
45
+ <li><span style='background:#FFCA92;opacity:0.7;'></span>Shelter / مأوى</li>
46
+ <li><span style='background:#37A8DA;opacity:0.7;'></span>Food & Water / طعام وماء</li>
47
+ <li><span style='background:#575757;opacity:0.7;'></span>Danger / مخاطر (تسرب الغاز، تلف في الخدمات العامة...)</li>
48
+ <li><span style='background:#6EAA25;opacity:0.7;'></span>Done / تم</li>
49
+ <li><span style='background:#FF91E8;opacity:0.7;'></span>Planned / مخطط لها</li>
50
+ </ul>
51
+ </div>
52
+ </div>
53
+
54
+ </body>
55
+ </html>
56
+
57
+ <style type='text/css'>
58
+ .maplegend .legend-title {
59
+ text-align: left;
60
+ margin-bottom: 5px;
61
+ font-weight: bold;
62
+ font-size: 90%;
63
+ color: #333;
64
+ }
65
+ .maplegend .legend-scale ul {
66
+ margin: 0;
67
+ margin-bottom: 5px;
68
+ padding: 0;
69
+ float: left;
70
+ list-style: none;
71
+ }
72
+ .maplegend .legend-scale ul li {
73
+ font-size: 80%;
74
+ list-style: none;
75
+ margin-left: 0;
76
+ line-height: 18px;
77
+ margin-bottom: 2px;
78
+ color: #111;
79
+ }
80
+ .maplegend ul.legend-labels li span {
81
+ display: block;
82
+ float: left;
83
+ height: 16px;
84
+ width: 30px;
85
+ margin-right: 5px;
86
+ margin-left: 0;
87
+ border: 1px solid #999;
88
+ }
89
+ .maplegend .legend-source {
90
+ font-size: 80%;
91
+ color: #777;
92
+ clear: both;
93
+ }
94
+ .maplegend a {
95
+ color: #777;
96
+ }
97
+ </style>
98
+ {% endmacro %}"""
99
+ legend_macro = MacroElement()
100
+ legend_macro._template = Template(template)
101
+
102
+ __all__ = ["legend_macro"]
src/text_content.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ INTRO_TEXT_EN = """
2
+ <div style="text-align: left;">
3
+ Nt3awnou نتعاونو is a collaborative platform dedicated to aiding individuals impacted by the recent earthquake in Morocco. Our core mission is to streamline and coordinate timely assistance for everyone affected. How do we achieve this? We assist those in need by allowing them to communicate their location and the specific aid they require, either by completing a form or sending a voice message via WhatsApp to a designated number. Once we receive and process this information, it can be viewed in our dashboard, which allows NGOs to organize and precisely target their interventions, ensuring swift assistance reaches those in need. Any organization that has taken initiative in a particular area can notify us by completing a dedicated form. This data is also incorporated into the dashboard so that other NGOs can help affected areas that still haven't received help.
4
+ Warning : There are still rocks falling down the mountains, making the roads to the affected areas very dangerous. We advise volunteers to donate directly to specialized NGOs.<br>
5
+ <br>
6
+ <b>✉️ You can contact us via email at </b><a href="mailto:nt3awnoumorocco@gmail.com">nt3awnoumorocco@gmail.com</a> <br>
7
+ <b>📝 Help us report more people in need by filling this form </b><a href="https://forms.gle/nZNCUVog9ka2Vdqu6">https://forms.gle/nZNCUVog9ka2Vdqu6</a> <br>
8
+ <b>📝 NGOs can report their interventions by filling this form </b><a href="https://forms.gle/PsNSuHHjTTgwQMmVA">https://forms.gle/PsNSuHHjTTgwQMmVA</a>
9
+ </div>
10
+ <br>
11
+ """
12
+
13
+ INTRO_TEXT_AR = """
14
+ <div style="text-align: right;">
15
+
16
+ نتعاونو هي منصة تعاونية لمساعدة الأفراد المتضررين من الزلزال الأخير في المغرب. مهمتنا هي تسهيل تقديم المساعدة في الوقت المناسب و بفاعلية و تنظيم لجميع المتضررين. كيفاش؟ كنعاونو الناس لي محتاجين للمساعدة إعلمونا بمكانهم و نوع المساعدة لي محتاجين ليها سواء عن طريق ملأ الاستمارة أو عن طريق إرسال تسجيل صوتي عبر واتساب إلى رقم مخصص. بعد معالجة هاد المعلومات، كنجمعوهم فخريطة كتمكن الجمعيات من تنظيم و استهداف تدخلاتهم بدقة باش توصل المساعدة للناس لي محتاجين في وقت أسرع. و كل جمعية قامت باللازم في منطقة معينة تقدر تعلمنا عن طريق ملأ استمارة مخصصة لهاد الأمر. هاد المعلومات كذلك كتضاف للخريطة باش باقي الجمعيات يتاجهو لمناطق أخرى مازال ماوصلاتهم مساعدة.
17
+ تحذير : نظرا لخطورة الطرقان بسبب الحجر اللي كيطيح من الجبال، ننصح المتطوعين اللي بغاو يساعدو المناطق المتضررة يتبرعو عن طريق الجمعيات المختصة.
18
+ <br>
19
+ nt3awnoumorocco@gmail.com <b>المتطوعين ليبغاو يعاونوا يقدرو يتصلوا معنا عبر البريد ✉️ </b><br>
20
+ https://forms.gle/nZNCUVog9ka2Vdqu6 <b>: ساعدونا نبلغو الناس ليمحتاجين فهاد الاستمارة 📝 </b><br>
21
+ https://forms.gle/PsNSuHHjTTgwQMmVA <b>: الجمعيات لي عندهم تدخلات يقدرو يبلغونا عبر هاد الاستمار ة📝</b>
22
+ </div>
23
+ <br>
24
+ """
25
+
26
+ INTRO_TEXT_FR = """
27
+ <div style="text-align: left;">
28
+ Nt3awnou نتعاونو est une plateforme collaborative dédiée à l'aide aux personnes touchées par le récent tremblement de terre au Maroc. Notre mission principale est de rationaliser et de coordonner une assistance rapide pour toutes les personnes touchées. Comment y parvenons-nous ? Nous aidons les personnes dans le besoin en leur permettant de communiquer leur localisation et l'aide spécifique dont elles ont besoin, soit en remplissant un formulaire, soit en envoyant un message vocal via WhatsApp à un numéro désigné. Une fois reçues et traitées, ces informations peuvent être consultées dans notre tableau de bord, qui permet aux associations d'organiser et de cibler précisément leurs interventions, afin que l'aide parvienne rapidement à ceux qui en ont besoin. Toute organisation ayant pris une initiative dans une zone particulière peut nous en informer en remplissant un formulaire prévu à cet effet. Ces données sont également intégrées au tableau de bord afin que d'autres associations puissent aider les zones touchées qui n'ont pas encore reçu d'aide.
29
+ Avertissement : Il y a encore des chutes de pierres dans les montagnes, ce qui rend les routes vers les zones touchées très dangereuses. Nous conseillons aux volontaires de faire des dons directement aux associations spécialisées.
30
+ <br>
31
+ <b>✉️ Vous pouvez nous contacter par courrier électronique à l'adresse suivante : </b>nt3awnoumorocco@gmail.com <br>
32
+ <b>📝 Aidez-nous à signaler plus de personnes dans le besoin en remplissant ce formulaire : </b>https://forms.gle/nZNCUVog9ka2Vdqu6 <br>
33
+ <b>📝 Les associations peuvent signaler leurs interventions en remplissant ce formulaire :</b>https://forms.gle/PsNSuHHjTTgwQMmVA
34
+ </div>
35
+ <br>
36
+ """
37
+
38
+ SLOGAN = """
39
+ <div style="text-align: center;">
40
+ <h4>وَمَنْ أَحْيَاهَا فَكَأَنَّمَا أَحْيَا النَّاسَ جَمِيعاً</h4>
41
+ </div>
42
+ """
43
+
44
+ HEADERS_MAPPING = {
45
+ "إغاثة" : "Rescue | إغاثة | Secours",
46
+ "مساعدة طبية": "Medical Assistance | مساعدة طبية | Assistance médicale",
47
+ "مأوى": "Shelter | مأوى | Abri",
48
+ "طعام وماء": "Food & Water | طعام وماء | Nourriture et eau",
49
+ "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Danger | مخاطر (تسرب الغاز، تلف في الخدمات العامة...) | Danger",
50
+ }
51
+
52
+ COLOR_MAPPING = {
53
+ "إغاثة": "red",
54
+ "مساعدة طبية": "orange",
55
+ "مأوى": "beige",
56
+ "طعام وماء": "blue",
57
+ "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "gray",
58
+ }
59
+
60
+ ICON_MAPPING = {
61
+ "إغاثة": "bell", # life ring icon for rescue
62
+ "مساعدة طبية": "heart", # medical kit for medical assistance
63
+ "مأوى": "home", # home icon for shelter
64
+ "طعام وماء": "cutlery", # cutlery (fork and knife) for food & water
65
+ "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Warning" # warning triangle for dangers
66
+ }
67
+
68
+ CREDITS_TEXT = """
69
+ <hr>
70
+ <div style="text-align: center;">
71
+ <p>By <b>Moroccans</b> for <b>Moroccans</b> 🤝</p>
72
+ <p>Bot powered by <a href="https://www.annarabic.com/">Annarabic</a></p>
73
+ <p>Collaboration made possible thanks to <a href="https://summerschool.morocco.ai/">AI Summer School</a></p>
74
+ """
75
+
76
+ LOGO = """
77
+ <style>
78
+ .block-container {
79
+ padding-top: 1rem;
80
+ padding-bottom: 0rem;
81
+ }
82
+ </style>
83
+ <div style="text-align: center;">
84
+ <img src="https://storage.googleapis.com/storage-annarabic/Nt3awnou(1).png" width="200" height="200">
85
+ </div>
86
+ """
87
+
88
+ REVIEW_TEXT = """**If a request should be reviewed or dropped submit its id here/ إذا كان يجب مراجعة أو حذف طلب، أدخل رقمه هنا:**"""
src/utils.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import folium
2
+ import pandas as pd
3
+ from folium import plugins
4
+ from src.map_utils import legend_macro
5
+ import leafmap.foliumap as leafmap
6
+
7
+ EPICENTER_LOCATION = [31.12210171476489, -8.42945837915193]
8
+ BORDER_COLOR = "black"
9
+
10
+ def parse_gg_sheet(url):
11
+ url = url.replace("edit#gid=", "export?format=csv&gid=")
12
+ df = pd.read_csv(url, on_bad_lines="warn")
13
+ return df
14
+
15
+ # Session for Requests
16
+ # session = requests.Session()
17
+ # @st.cache_data(persist=True)
18
+ # def parse_latlng_from_link(url):
19
+ # try:
20
+ # # extract latitude and longitude from gmaps link
21
+ # if "@" not in url:
22
+ # resp = session.head(url, allow_redirects=True)
23
+ # url = resp.url
24
+ # latlng = url.split("@")[1].split(",")[0:2]
25
+ # return [float(latlng[0]), float(latlng[1])]
26
+ # except Exception as e:
27
+ # return None
28
+
29
+ def add_latlng_col(df, process_column):
30
+ """Add a latlng column to the dataframe"""
31
+ df = df.assign(latlng=df.iloc[:, process_column].apply(parse_latlng))
32
+ return df
33
+
34
+ # parse latlng (column 4) to [lat, lng]
35
+ import re
36
+ def parse_latlng(latlng):
37
+ if pd.isna(latlng):
38
+ return None
39
+ # lat, lng = latlng.split(",")
40
+ # return [float(lat), float(lng)]
41
+
42
+ try:
43
+ # check if it matches (30.9529832, -7.1010705) or (30.9529832,-7.1010705)
44
+ if re.match(r"\(\d+\.\d+,\s?-\d+\.\d+\)", latlng):
45
+ lat, lng = latlng[1:-1].split(",")
46
+ return [float(lat), float(lng)]
47
+ # check of it matches 30.9529832, -7.1010705 or 30.9529832,-7.1010705
48
+ elif re.match(r"\d+\.\d+,\s?-\d+\.\d+", latlng):
49
+ lat, lng = latlng.split(",")
50
+ return [float(lat), float(lng)]
51
+ # check if it matches 30,9529832, -7,1010705 or 30,9529832,-7,1010705, match1=30,9529832 and match2=-7,1010705
52
+ elif re.match(r"\d+,\d+,\s?-\d+,\d+", latlng):
53
+ d1, d2, d3, d4 = latlng.split(",")
54
+ return [float(".".join([d1, d2])), float(".".join([d3, d4]))]
55
+ except Exception as e:
56
+ print(f"Error parsing latlng: {latlng} Reason: {e}")
57
+ return None
58
+ print(f"Error parsing latlng: {latlng}")
59
+ return None
60
+
61
+ def add_epicentre_to_map(map_obj):
62
+ icon_epicentre = folium.plugins.BeautifyIcon(
63
+ icon='spinner',
64
+ spin=True,
65
+ border_color='#b3334f',
66
+ background_color='#b3334f',
67
+ text_color='white'
68
+ )
69
+ folium.Marker(location=EPICENTER_LOCATION,
70
+ popup="Epicenter مركز الزلزال",
71
+ icon=icon_epicentre).add_to(map_obj)
72
+
73
+
74
+ def add_danger_distances_to_map(map_obj):
75
+ Danger_Distances_group = folium.FeatureGroup(name='Danger distances - earthquake magnitude 7 | مسافات الخطر - قوة الزلازل 7').add_to(map_obj)
76
+
77
+ zones = [
78
+ {"radius": 100000, "fill_opacity": 0.1, "weight": 1, "fill_color": "yellow", "tooltip": "50 to 100 km - Moderate risk area | منطقة خطر معتدلة"},
79
+ {"radius": 50000, "fill_opacity": 0.1, "weight": 1, "fill_color": "orange", "tooltip": "30 to 50 km - High risk zone | منطقة عالية المخاطر"},
80
+ {"radius": 30000, "fill_opacity": 0.2, "weight": 1, "fill_color": "#FF0000", "tooltip": "10 to 30 km - Very high risk zone | منطقة شديدة الخطورة"},
81
+ {"radius": 10000, "fill_opacity": 0.2, "weight": 0.2, "fill_color": "#8B0000", "tooltip": "0 to 10km - direct impact zone | منطقة التأثير المباشر"}
82
+ ]
83
+
84
+ for zone in zones:
85
+ folium.Circle(
86
+ location=EPICENTER_LOCATION,
87
+ radius=zone["radius"],
88
+ color=BORDER_COLOR,
89
+ weight=zone["weight"],
90
+ fill_opacity=zone["fill_opacity"],
91
+ opacity=zone["fill_opacity"], # Assuming border opacity should match fill_opacity
92
+ fill_color=zone["fill_color"],
93
+ tooltip=zone["tooltip"],
94
+ ).add_to(Danger_Distances_group)
95
+
96
+
97
+ def init_map():
98
+ m = leafmap.Map(
99
+ center=[31.228674, -7.992047],
100
+ zoom=8.5,
101
+ height="800px",
102
+ max_bounds=True,
103
+ min_zoom=8.5,
104
+ max_lat=35.628674,
105
+ min_lat=29.628674,
106
+ max_lng=-4.992047,
107
+ min_lng=-10.992047
108
+ )
109
+ # Add a search bar to the map
110
+ plugins.Geocoder(
111
+ collapsed=False,
112
+ position="topright",
113
+ placeholder="Search | البحث",
114
+ ).add_to(m)
115
+
116
+ # Add Fullscreen button to the map
117
+ plugins.Fullscreen(
118
+ position="topright",
119
+ title="Expand me | تكبير الخريطة",
120
+ title_cancel="Exit me | تصغير الخريطة",
121
+ force_separate_button=True,
122
+ ).add_to(m)
123
+
124
+ # Satellite View from Mapbox
125
+ tileurl = "https://api.mapbox.com/styles/v1/phd2020/clmer2mra01d001pbgjkictpt/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoicGhkMjAyMCIsImEiOiJja29lZzFwZmUwNHkzMm5wMjZnYjVvcGltIn0.tE0ritrelQOyLdKUH6hgOw"
126
+ folium.TileLayer(
127
+ tiles=tileurl,
128
+ attr="Satellite View",
129
+ name="Satellite View | عرض القمر الصناعي",
130
+ overlay=False,
131
+ control=True,
132
+ ).add_to(m)
133
+
134
+ # Json with refreshed images from maxar
135
+ mosaics = ['10300100CB8C5600'] #,'10300100CBAF7D00'] #,'10300100E5404900','10300100E5A0A100'] #,'10300100EB8B4500','10300100EBC1A000','10300100ECC53700','10300100ED11EA00','10300500CFF91800','10300500E4F91400','10300500E4F91500','10300500E4F91700','10300500E4F91800','10300500E4F91900','10400100797DAC00','1040010083289000','10400100889ABF00','104001008A8E9800','104001008B75BB00','1040050047DC6400','1040050057DC8500','1050050030DE8900']
136
+ for i, mosaic_id in enumerate(mosaics):
137
+ mosaic = f'https://open.gishub.org/maxar-open-data/datasets/Morocco-Earthquake-Sept-2023/{mosaic_id}.json'
138
+ m.add_stac_layer(mosaic, name=f'mosaic_id_{i}')
139
+
140
+ # Add danger zones
141
+ add_epicentre_to_map(m)
142
+ add_danger_distances_to_map(m)
143
+
144
+ # Add a LayerControl to the map to toggle between layers (Satellite View and Default One)
145
+ folium.LayerControl().add_to(m)
146
+
147
+ # Macro to add legend
148
+ m.get_root().add_child(legend_macro)
149
+ return m