ahmedJaafari nouamanetazi HF staff commited on
Commit
9ed4a42
0 Parent(s):

Duplicate from nt3awnou/Nt3awnou-rescue-map

Browse files

Co-authored-by: Nouamane Tazi <nouamanetazi@users.noreply.huggingface.co>

Files changed (5) hide show
  1. .gitattributes +35 -0
  2. README.md +12 -0
  3. app.py +260 -0
  4. requirements.txt +2 -0
  5. utils.py +102 -0
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Nt3awnu Map
3
+ emoji: ❤️
4
+ colorFrom: white
5
+ 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
app.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
12
+
13
+ TOKEN = os.environ.get("HF_TOKEN", None)
14
+ api = HfApi(token=TOKEN)
15
+ st.set_page_config(layout="wide", initial_sidebar_state="collapsed")
16
+
17
+ if "sleep_time" not in st.session_state:
18
+ st.session_state.sleep_time = 2
19
+
20
+ if "auto_refresh" not in st.session_state:
21
+ st.session_state.auto_refresh = False
22
+
23
+ auto_refresh = st.sidebar.checkbox("Auto Refresh?", st.session_state.auto_refresh)
24
+
25
+ if auto_refresh:
26
+ number = st.sidebar.number_input("Refresh rate in seconds", value=st.session_state.sleep_time)
27
+ st.session_state.sleep_time = number
28
+
29
+ #Logo
30
+ st.markdown(
31
+ """
32
+ <div style="text-align: center;">
33
+ <img src="https://storage.googleapis.com/storage-annarabic/Nt3awnou(1).png" width="200" height="200">
34
+ </div>
35
+ """,
36
+ unsafe_allow_html=True,
37
+ )
38
+
39
+ st.title("Nt3awnou نتعاونو ")
40
+
41
+ rtl_text = """
42
+
43
+ """
44
+ st.markdown(
45
+ """
46
+ <div style="text-align: right;">
47
+ نتعاونو هي منصة تعاونية لمساعدة الأفراد المتضررين من الزلزال الأخير في المغرب. مهمتنا هي تسهيل تقديم المساعدة في الوقت المناسب و بفاعلية و تنظيم لجميع المتضررين.
48
+ كيفاش؟ كنعاونو الناس لي محتاجين للمساعدة إعلمونا بمكانهم و نوع المساعدة لي محتاجين ليها سواء عن طريق ملأ الاستمارة أو عن طريق إرسال تسجيل صوتي عبر واتساب إلى رقم مخصص. بعد معالجة هاد المعلومات، كنجمعوهم فخريطة كتمكن الجمعيات و المتطوعين من تنظيم و استهداف تدخلاتهم بدقة باش توصل المساعدة للناس لي محتاجين في وقت أسرع. و كل جمعية قامت باللازم في منطقة معينة تقدر تعلمنا عن طريق ملأ استمارة مخصصة لهاد الأمر. هاد المعلومات كذلك كتضاف للخريطة باش باقي المتطوعين يتاجهو لمناطق أخرى.
49
+
50
+ ✉️ nt3awnou@annarabic.com المتطوعين ليبغاو يعاونوا يقدرو يتصلوا معنا عبر البريد
51
+ 📝 https://forms.gle/nZNCUVog9ka2Vdqu6 : ساعدونا نبلغو الناس ليمحتاجين فهاد الاستمارة
52
+ </div>
53
+ """,
54
+ unsafe_allow_html=True,
55
+ )
56
+
57
+
58
+ st.markdown("""
59
+ <div style="text-align: left;">
60
+ 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.
61
+ 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 and volunteers 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.
62
+
63
+ ✉️ You can contact us via email at nt3awnou@annarabic.com <br>
64
+ 📝 Help us report more people in need by filling this form: https://forms.gle/nZNCUVog9ka2Vdqu6
65
+ </div>
66
+ """,
67
+ unsafe_allow_html=True,
68
+ )
69
+
70
+ session = requests.Session()
71
+ @st.cache_data(persist=True)
72
+ def parse_latlng_from_link(url):
73
+ try:
74
+ # extract latitude and longitude from gmaps link
75
+ if "@" not in url:
76
+ # We first need to get the redirect URL
77
+ resp = session.head(url, allow_redirects=True)
78
+ url = resp.url
79
+ latlng = url.split('@')[1].split(',')[0:2]
80
+ return [float(latlng[0]), float(latlng[1])]
81
+ except Exception as e:
82
+ print(f"Error parsing latlng from link: {e}")
83
+ return None
84
+
85
+ def parse_gg_sheet_interventions(url):
86
+ df = pd.read_csv(url)
87
+ return df.assign(latlng=df.iloc[:, 3].apply(parse_latlng_from_link))
88
+
89
+ def parse_gg_sheet(url):
90
+ url = url.replace("edit#gid=", "export?format=csv&gid=")
91
+ df = pd.read_csv(url)
92
+
93
+ # parse latlng (column 4) to [lat, lng]
94
+ def parse_latlng(latlng):
95
+ try:
96
+ lat, lng = latlng.split(",")
97
+ return [float(lat), float(lng)]
98
+ except Exception as e:
99
+ print(f"Error parsing latlng: {e}")
100
+ return None
101
+
102
+ return df.assign(latlng=df.iloc[:, 4].apply(parse_latlng))
103
+
104
+ df = parse_gg_sheet(
105
+ "https://docs.google.com/spreadsheets/d/1gYoBBiBo1L18IVakHkf3t1fOGvHWb23loadyFZUeHJs/edit#gid=966953708"
106
+ )
107
+ interventions_df = parse_gg_sheet_interventions(
108
+ "https://docs.google.com/spreadsheets/d/1eXOTqunOWWP8FRdENPs4cU9ulISm4XZWYJJNR1-SrwY/gviz/tq?tqx=out:csv"
109
+ )
110
+
111
+ # select requests
112
+ headers_mapping = {
113
+ "إغاثة": "Rescue/إغاثة",
114
+ "مساعدة طبية": "Medical Assistance/مساعدة طبية",
115
+ "مأوى": "Shelter/مأوى",
116
+ "طعام وماء": "Food & Water/طعام وماء",
117
+ "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Danger/مخاطر (تسرب الغاز، تلف في الخدمات العامة...)",
118
+ }
119
+ colors_mapping = {
120
+ "إغاثة": "red",
121
+ "مساعدة طبية": "orange",
122
+ "مأوى": "beige",
123
+ "طعام وماء": "blue",
124
+ "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "gray",
125
+ }
126
+ icon_mapping = {
127
+ "إغاثة": "bell", # life ring icon for rescue
128
+ "مساعدة طبية": "heart", # medical kit for medical assistance
129
+ "مأوى": "home", # home icon for shelter
130
+ "طعام وماء": "cutlery", # cutlery (fork and knife) for food & water
131
+ "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Warning" # warning triangle for dangers
132
+ }
133
+
134
+ options = ["إغاثة", "مساعدة طبية", "مأوى", "طعام وماء", "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)"]
135
+ selected_options = []
136
+
137
+ st.markdown("👉 **Choose request type / اختر نوع الطلب**")
138
+ col1, col2, col3, col4, col5 = st.columns([2, 3, 2, 3, 4])
139
+ cols = [col1, col2, col3, col4, col5]
140
+
141
+ for i, option in enumerate(options):
142
+ checked = cols[i].checkbox(headers_mapping[option], value=True)
143
+ if checked:
144
+ selected_options.append(headers_mapping[option])
145
+
146
+ arabic_options = [e.split("/")[1] for e in selected_options]
147
+ df['id'] = df.index
148
+ filtered_df = df[df['ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)'].isin(arabic_options)]
149
+ selected_headers = [headers_mapping[request] for request in arabic_options]
150
+
151
+ # select interventions
152
+ st.markdown("👇 **View past or planned interventions / عرض عمليات المساعدة السابقة أو المخطط لها**")
153
+ show_interventions = st.checkbox("Display Interventions عرض التدخلات", value=True)
154
+
155
+ m = folium.Map(
156
+ location=[31.228674, -7.992047],
157
+ zoom_start=8.5,
158
+ min_zoom=8.5,
159
+ max_lat=35.628674,
160
+ min_lat=29.628674,
161
+ max_lon=-4.992047,
162
+ min_lon=-10.992047,
163
+ max_bounds=True,
164
+ )
165
+
166
+ if show_interventions:
167
+ for index, row in interventions_df.iterrows():
168
+ status = "Done ✅" if row[interventions_df.columns[5]]!="Intervention prévue dans le futur / Planned future intervention" else "Planned ⌛"
169
+ color_mk = "green" if row[interventions_df.columns[5]]!="Intervention prévue dans le futur / Planned future intervention" else "pink"
170
+ intervention_type = row[interventions_df.columns[6]].split("/")[0].strip()
171
+ org = row[interventions_df.columns[1]]
172
+ city = row[interventions_df.columns[9]]
173
+ date = row[interventions_df.columns[4]]
174
+ intervention_info = f"<b>Status:</b> {status}<br><b>Org:</b> {org}<br><b>Intervention:</b> {intervention_type}<br><b>📅 Date:</b> {date}"
175
+ if row["latlng"] is None:
176
+ continue
177
+ folium.Marker(
178
+ location=row["latlng"],
179
+ tooltip=city,
180
+ popup=folium.Popup(intervention_info, max_width=300),
181
+ icon=folium.Icon(color=color_mk)
182
+ ).add_to(m)
183
+
184
+ for index, row in filtered_df.iterrows():
185
+ request_type = row['ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)']
186
+ display_text = f"<b>Request Type:</b> {request_type}<br><b>Id:</b> {row['id']}"
187
+ icon_name = icon_mapping.get(request_type, 'info-sign')
188
+ if row["latlng"] is None:
189
+ continue
190
+
191
+ folium.Marker(
192
+ location=row["latlng"],
193
+ tooltip=row[' لأي جماعة / قيادة / دوار تنتمون ؟'] if not pd.isna(row[' لأي جماعة / قيادة / دوار تنتمون ؟']) else None,
194
+ popup=folium.Popup(display_text, max_width=300),
195
+ icon=folium.Icon(color=colors_mapping.get(request_type, "blue"), icon=icon_name)
196
+ ).add_to(m)
197
+
198
+
199
+ # Macro to add legend
200
+ m.get_root().add_child(legend_macro)
201
+
202
+ st_data = st_folium(m, use_container_width=True)
203
+
204
+ # Google Sheet Table
205
+ st.subheader("📝 **Table of requests / جدول الطلبات**")
206
+ st.markdown(
207
+ """
208
+ <iframe src="https://docs.google.com/spreadsheets/d/1gYoBBiBo1L18IVakHkf3t1fOGvHWb23loadyFZUeHJs/" width="100%" height="600px"></iframe>
209
+ """,
210
+ unsafe_allow_html=True,
211
+ )
212
+
213
+ # Google Sheet Table
214
+ st.subheader("📝 **Table of interventions / جدول التدخلات**")
215
+ st.markdown(
216
+ """
217
+ <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>
218
+ """,
219
+ unsafe_allow_html=True,
220
+ )
221
+
222
+ # Submit an id for review
223
+ st.subheader("🔍 Review of requests")
224
+ st.markdown("**If a request should be reviewed or dropped submit its id here/ إذا كان يجب مراجعة أو حذف طلب، أدخل رقمه هنا:**")
225
+ 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)")
226
+ st.markdown("[form](https://docs.google.com/forms/d/e/1FAIpQLSe8D6T__DJDTVGMrIWMT-H-hQ0qDUWVOncKnrSXgv4NbwHCrQ/viewform) إذا تدخلت لحل الطلب، يرجى ملء هذا النموذج الرابط")
227
+
228
+ id_to_review = st.number_input("Enter id / أدخل الرقم", min_value=0, max_value=len(df), value=0, step=1)
229
+ reason_for_review = st.text_area("Explain why / أدخل سبب المراجعة")
230
+ if st.button("Submit / أرسل"):
231
+ if reason_for_review == "":
232
+ st.error("Please enter a reason / الرجاء إدخال سبب")
233
+ else:
234
+ filename = f"review_id_{id_to_review}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt"
235
+ with open(filename, "w") as f:
236
+ f.write(f"id: {id_to_review}, explanation: {reason_for_review}\n")
237
+ api.upload_file(
238
+ path_or_fileobj=filename,
239
+ path_in_repo=filename,
240
+ repo_id="nt3awnou/review_requests",
241
+ repo_type="dataset",
242
+ )
243
+ st.success("Submitted at https://huggingface.co/datasets/nt3awnou/review_requests/ تم الإرسال")
244
+
245
+
246
+
247
+ # Credits
248
+ st.markdown(
249
+ """
250
+ <hr>
251
+ <div style="text-align: center;">
252
+ <p>By <b>Moroccans</b> for <b>Moroccans</b> 🤝</p>
253
+ <p>Bot powered by <a href="https://www.annarabic.com/">Annarabic</a></p>
254
+ <p>Collaboration made possible thanks to <a href="https://summerschool.morocco.ai/">AI Summer School</a></p>
255
+ """,
256
+ unsafe_allow_html=True,
257
+ )
258
+ if auto_refresh:
259
+ time.sleep(number)
260
+ st.experimental_rerun()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ folium
2
+ streamlit_folium
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"]