import os import time import requests import folium import pandas as pd from datetime import datetime import streamlit as st from streamlit_folium import st_folium from utils import legend_macro from huggingface_hub import HfApi from folium import plugins TOKEN = os.environ.get("HF_TOKEN", None) api = HfApi(token=TOKEN) st.set_page_config(layout="wide", initial_sidebar_state="collapsed") if "sleep_time" not in st.session_state: st.session_state.sleep_time = 2 if "auto_refresh" not in st.session_state: st.session_state.auto_refresh = False auto_refresh = st.sidebar.checkbox("Auto Refresh?", st.session_state.auto_refresh) if auto_refresh: number = st.sidebar.number_input("Refresh rate in seconds", value=st.session_state.sleep_time) st.session_state.sleep_time = number #Logo st.markdown( """
""", unsafe_allow_html=True, ) st.title("Nt3awnou نتعاونو ") # middle slogan st.markdown( """

وَمَنْ أَحْيَاهَا فَكَأَنَّمَا أَحْيَا النَّاسَ جَمِيعاً

""", unsafe_allow_html=True, ) # Have different streamlit tabs for each language st.sidebar.title("Language / اللغة") tab_ar, tab_en, tab_fr = st.tabs(["العربية", "English", "Français"]) with tab_en: st.markdown("""
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. 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. ✉️ You can contact us via email at nt3awnou@annarabic.com
📝 Help us report more people in need by filling this form: https://forms.gle/nZNCUVog9ka2Vdqu6
📝 NGOs can report their interventions by filling this form: https://forms.gle/PsNSuHHjTTgwQMmVA
""", unsafe_allow_html=True, ) with tab_ar: st.markdown( """
نتعاونو هي منصة تعاونية لمساعدة الأفراد المتضررين من الزلزال الأخير في المغرب. مهمتنا هي تسهيل تقديم المساعدة في الوقت المناسب و بفاعلية و تنظيم لجميع المتضررين. كيفاش؟ كنعاونو الناس لي محتاجين للمساعدة إعلمونا بمكانهم و نوع المساعدة لي محتاجين ليها سواء عن طريق ملأ الاستمارة أو عن طريق إرسال تسجيل صوتي عبر واتساب إلى رقم مخصص. بعد معالجة هاد المعلومات، كنجمعوهم فخريطة كتمكن الجمعيات من تنظيم و استهداف تدخلاتهم بدقة باش توصل المساعدة للناس لي محتاجين في وقت أسرع. و كل جمعية قامت باللازم في منطقة معينة تقدر تعلمنا عن طريق ملأ استمارة مخصصة لهاد الأمر. هاد المعلومات كذلك كتضاف للخريطة باش باقي الجمعيات يتاجهو لمناطق أخرى مازال ماوصلاتهم مساعدة. تحذير : نظرا لخطورة الطرقان بسبب الحجر اللي كيطيح من الجبال، ننصح المتطوعين اللي بغاو يساعدو المناطق المتضررة يتبرعو عن طريق الجمعيات المختصة. nt3awnou@annarabic.com المتطوعين ليبغاو يعاونوا يقدرو يتصلوا معنا عبر البريد ✉️ https://forms.gle/nZNCUVog9ka2Vdqu6 : ساعدونا نبلغو الناس ليمحتاجين فهاد الاستمارة 📝
https://forms.gle/PsNSuHHjTTgwQMmVA : الجمعيات لي عندهم تدخلات يقدرو يبلغونا عبر هاد الاستمار ة📝
""", unsafe_allow_html=True, ) with tab_fr: st.markdown("""
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. 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. ✉️ Vous pouvez nous contacter par courrier électronique à l'adresse suivante : nt3awnou@annarabic.com
📝 Aidez-nous à signaler plus de personnes dans le besoin en remplissant ce formulaire : https://forms.gle/nZNCUVog9ka2Vdqu6
📝 Les associations peuvent signaler leurs interventions en remplissant ce formulaire : https://forms.gle/PsNSuHHjTTgwQMmVA
""" , unsafe_allow_html=True) session = requests.Session() @st.cache_data(persist=True) def parse_latlng_from_link(url): try: # extract latitude and longitude from gmaps link if "@" not in url: # We first need to get the redirect URL resp = session.head(url, allow_redirects=True) url = resp.url latlng = url.split('@')[1].split(',')[0:2] return [float(latlng[0]), float(latlng[1])] except Exception as e: print(f"Error parsing latlng from link: {e}") return None def parse_gg_sheet_interventions(url): url = url.replace("edit#gid=", "export?format=csv&gid=") print(url) df = pd.read_csv(url, on_bad_lines='skip') return df.assign(latlng=df.iloc[:, 3].apply(parse_latlng_from_link)) def parse_gg_sheet(url): url = url.replace("edit#gid=", "export?format=csv&gid=") print(url) df = pd.read_csv(url, on_bad_lines='warn') # parse latlng (column 4) to [lat, lng] def parse_latlng(latlng): try: lat, lng = latlng.split(",") return [float(lat), float(lng)] except Exception as e: print(f"Error parsing latlng: {e}") return None if df.shape[1] > 4: df = df.assign(latlng=df.iloc[:, 4].apply(parse_latlng)) return df df = parse_gg_sheet( "https://docs.google.com/spreadsheets/d/1gYoBBiBo1L18IVakHkf3t1fOGvHWb23loadyFZUeHJs/edit#gid=966953708" ) interventions_df = parse_gg_sheet_interventions( "https://docs.google.com/spreadsheets/d/1eXOTqunOWWP8FRdENPs4cU9ulISm4XZWYJJNR1-SrwY/edit#gid=2089222765" ) # select requests with tab_ar: headers_mapping = { "إغاثة": "Rescue/إغاثة", "مساعدة طبية": "Medical Assistance/مساعدة طبية", "مأوى": "Shelter/مأوى", "طعام وماء": "Food & Water/طعام وماء", "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Danger/مخاطر (تسرب الغاز، تلف في الخدمات العامة...)", } with tab_en: headers_mapping = { "إغاثة" : "Rescue | إغاثة | Secours", "مساعدة طبية": "Medical Assistance | مساعدة طبية | Assistance médicale", "مأوى": "Shelter | مأوى | Abri", "طعام وماء": "Food & Water | طعام وماء | Nourriture et eau", "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Danger | مخاطر (تسرب الغاز، تلف في الخدمات العامة...) | Danger", } colors_mapping = { "إغاثة": "red", "مساعدة طبية": "orange", "مأوى": "beige", "طعام وماء": "blue", "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "gray", } icon_mapping = { "إغاثة": "bell", # life ring icon for rescue "مساعدة طبية": "heart", # medical kit for medical assistance "مأوى": "home", # home icon for shelter "طعام وماء": "cutlery", # cutlery (fork and knife) for food & water "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)": "Warning" # warning triangle for dangers } options = ["إغاثة", "مساعدة طبية", "مأوى", "طعام وماء", "مخاطر (تسرب الغاز، تلف في الخدمات العامة...)"] selected_options = [] with tab_en: st.markdown("👉 **Choose request type**") with tab_ar: st.markdown("👉 **اختر نوع الطلب**") with tab_fr: st.markdown("👉 **Choisissez le type de demande**") col1, col2, col3, col4, col5 = st.columns([2, 3, 2, 3, 4]) cols = [col1, col2, col3, col4, col5] for i, option in enumerate(options): checked = cols[i].checkbox(headers_mapping[option], value=True) if checked: selected_options.append(option) df['id'] = df.index filtered_df = df[df['ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)'].isin(selected_options)] selected_headers = [headers_mapping[request] for request in selected_options] # select interventions show_interventions = st.checkbox("Display Interventions | عرض عمليات المساعدة | Afficher les interventions", value=True) m = folium.Map( location=[31.228674, -7.992047], zoom_start=8.5, min_zoom=8.5, max_lat=35.628674, min_lat=29.628674, max_lon=-4.992047, min_lon=-10.992047, max_bounds=True, ) # Add a search bar to the map plugins.Geocoder( collapsed=False, position="topright", placeholder="Search | البحث", ).add_to(m) # Add Fullscreen button to the map plugins.Fullscreen( position='topright', title='Expand me | تكبير الخريطة', title_cancel='Exit me | تصغير الخريطة', force_separate_button=True ).add_to(m) #Satellite View from Mapbox tileurl = 'https://api.mapbox.com/styles/v1/phd2020/clmer2mra01d001pbgjkictpt/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoicGhkMjAyMCIsImEiOiJja29lZzFwZmUwNHkzMm5wMjZnYjVvcGltIn0.tE0ritrelQOyLdKUH6hgOw' folium.TileLayer( tiles = tileurl, attr = 'Satellite View', name = 'Satellite View | عرض القمر الصناعي', overlay = False, control = True ).add_to(m) # Marker for the epicentre icon_epicentre = folium.plugins.BeautifyIcon( icon='spinner', spin='true', border_color='#b3334f', background_color = '#b3334f', text_color = 'white' ) folium.Marker(location=[31.12210171476489, -8.42945837915193], popup="Epicenter زلزال المغرب 2023 (مركز الزلزال)", icon=icon_epicentre).add_to(m) # Danger Distances Feature Danger_Distances_group = folium.FeatureGroup(name='Danger distances - earthquake magnitude 7 | مسافات الخطر - قوة الزلازل 7').add_to(m) radius = 100000 # in meters folium.Circle( location=[31.12210171476489, -8.42945837915193], radius=radius, color="black", weight=1, fill_opacity=0.1, opacity=0.1, fill_color="yellow", tooltip="50 to 100 km -Moderate risk area | منطقة خطر معتدلة", ).add_to(Danger_Distances_group) radius = 50000 # in meters folium.Circle( location=[31.12210171476489, -8.42945837915193], radius=radius, color="black", weight=1, fill_opacity=0.1, opacity=0.1, fill_color="orange", tooltip="30 to 50 km - High risk zone | منطقة عالية المخاطر", ).add_to(Danger_Distances_group) radius = 30000 # in meters folium.Circle( location=[31.12210171476489, -8.42945837915193], radius=radius, color="black", weight=1, fill_opacity=0.2, opacity=0.2, fill_color="#FF0000", tooltip="10 to 30 km - Very high risk zone | منطقة شديدة الخطورة ", ).add_to(Danger_Distances_group) radius = 10000 # in meters folium.Circle( location=[31.12210171476489, -8.42945837915193], radius=radius, color="black", weight=0.2, fill_opacity=0.2, opacity=0.2, fill_color="#8B0000", tooltip="0 to 10km - direct impact zone | منطقة التأثير المباشر", ).add_to(Danger_Distances_group) # Add a LayerControl to the map to toggle between layers (Satellite View and Default One) folium.LayerControl().add_to(m) if show_interventions: for index, row in interventions_df.iterrows(): status = "Done ✅" if row[interventions_df.columns[5]]!="Intervention prévue dans le futur / Planned future intervention" else "Planned ⌛" color_mk = "green" if row[interventions_df.columns[5]]!="Intervention prévue dans le futur / Planned future intervention" else "pink" intervention_type = row[interventions_df.columns[6]].split("/")[0].strip() org = row[interventions_df.columns[1]] city = row[interventions_df.columns[9]] date = row[interventions_df.columns[4]] intervention_info = f"Status: {status}
Org: {org}
Intervention: {intervention_type}
📅 Date: {date}" if row["latlng"] is None: continue folium.Marker( location=row["latlng"], tooltip=city, popup=folium.Popup(intervention_info, max_width=300), icon=folium.Icon(color=color_mk) ).add_to(m) for index, row in filtered_df.iterrows(): request_type = row['ما هي احتياجاتك؟ (أضفها إذا لم يتم ذكرها)'] long_lat = row["هل يمكنك تقديم الإحداثيات الدقيقة للموقع؟ (ادا كنت لا توجد بعين المكان) متلاً \n31.01837503440344, -6.781405948842175"] maps_url = f"https://maps.google.com/?q={long_lat}" display_text = f'Request Type: {request_type}
Id: {row["id"]}
Google Maps' icon_name = icon_mapping.get(request_type, 'info-sign') if row["latlng"] is None: continue folium.Marker( location=row["latlng"], tooltip=row[' لأي جماعة / قيادة / دوار تنتمون ؟'] if not pd.isna(row[' لأي جماعة / قيادة / دوار تنتمون ؟']) else None, popup=folium.Popup(display_text, max_width=300), icon=folium.Icon(color=colors_mapping.get(request_type, "blue"), icon=icon_name) ).add_to(m) # Macro to add legend m.get_root().add_child(legend_macro) st_data = st_folium(m, use_container_width=True) # Google Sheet Table st.subheader("📝 **Table of requests / جدول الطلبات** https://forms.gle/nZNCUVog9ka2Vdqu6") st.markdown( """
""", unsafe_allow_html=True, ) # Google Sheet Table st.subheader("📝 **Table of interventions / جدول عمليات المساعدة** https://forms.gle/PsNSuHHjTTgwQMmVA") st.markdown( """
""", unsafe_allow_html=True, ) # chat.sendMessage(`https://www.google.com/maps/search/?api=1&query=${location.latitude},${location.longitude}`); # Submit an id for review st.subheader("🔍 Review of requests") st.markdown("If you intervened to solve a request, please fill this [form](https://docs.google.com/forms/d/e/1FAIpQLSe8D6T__DJDTVGMrIWMT-H-hQ0qDUWVOncKnrSXgv4NbwHCrQ/viewform)") st.markdown("إذا تدخلت لحل طلب، يرجى ملء هذا [النموذج](https://docs.google.com/forms/d/e/1FAIpQLSe8D6T__DJDTVGMrIWMT-H-hQ0qDUWVOncKnrSXgv4NbwHCrQ/viewform)") st.markdown("**If a request should be reviewed or dropped submit its id here/ إذا كان يجب مراجعة أو حذف طلب، أدخل رقمه هنا:**") id_to_review = st.number_input("Enter id / أدخل الرقم", min_value=0, max_value=len(df), value=0, step=1) reason_for_review = st.text_area("Explain why / أدخل سبب المراجعة") if st.button("Submit / أرسل"): if reason_for_review == "": st.error("Please enter a reason / الرجاء إدخال سبب") else: filename = f"review_id_{id_to_review}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt" with open(filename, "w") as f: f.write(f"id: {id_to_review}, explanation: {reason_for_review}\n") api.upload_file( path_or_fileobj=filename, path_in_repo=filename, repo_id="nt3awnou/review_requests", repo_type="dataset", ) st.success("Submitted at https://huggingface.co/datasets/nt3awnou/review_requests/ تم الإرسال") # Credits st.markdown( """

By Moroccans for Moroccans 🤝

Bot powered by Annarabic

Collaboration made possible thanks to AI Summer School

""", unsafe_allow_html=True, ) if auto_refresh: time.sleep(number) st.experimental_rerun()