Spaces:
Running
Running
| import streamlit as st | |
| import warnings | |
| warnings.filterwarnings("ignore") | |
| st.set_page_config(layout="wide") | |
| import folium | |
| from streamlit_folium import st_folium | |
| import pandas as pd | |
| #import pymongo | |
| from pymongo.errors import PyMongoError | |
| import uuid | |
| import datetime | |
| import numpy as np | |
| from st_aggrid import GridOptionsBuilder, AgGrid, GridUpdateMode, ColumnsAutoSizeMode | |
| from FSM import view | |
| from utils import construct_filters, construct_filters_geo,store_data, save_report | |
| # from utils import display_sidebar | |
| from modal import show_modal | |
| import json | |
| from preload import ( | |
| mongo_client, | |
| mappatura_data, | |
| color_data_regioni, | |
| color_data_province, | |
| color_data_comuni, | |
| presence_regioni, | |
| all_regions, | |
| all_provinces, | |
| all_munis, | |
| presence_province, | |
| presence_comuni, | |
| ) | |
| from const import ( | |
| COLUMNS_SHOW, | |
| HELP, | |
| OPTIONS_DEPRIVATION, | |
| OPTIONS_OBJECTIVE, | |
| OPTIONS_ACTIVITIES, | |
| OPTIONS_TARGET, | |
| CRPL_SETTINGS, | |
| ) | |
| #### INITIALIZATION | |
| # Gets run only once when each user opens the app | |
| if "init" not in st.session_state: | |
| st.session_state["init"] = True | |
| st.session_state["view"] = view() | |
| st.session_state["id"] = uuid.uuid4() | |
| st.session_state["last_row"] = None | |
| if "logging_level" in st.secrets: | |
| st.session_state["logging_level"] = st.secrets["logging_level"] | |
| else: | |
| st.session_state["logging_level"] = "basic" | |
| store_data( | |
| { | |
| "UniqueID": st.session_state["id"], | |
| "Timestamp": str(datetime.datetime.now()), | |
| "Start": True, | |
| }, | |
| mongo_client=mongo_client, | |
| ) | |
| ## Show the sidebar | |
| #display_sidebar() | |
| #### BUTTONS | |
| # These functions deal with button clicks, in this case we really only need one I think... | |
| def clicked_go_up(): | |
| st.session_state["view"].go_up() | |
| def info(): | |
| # st.write("**Come usare questa app?**") | |
| for item in HELP: | |
| st.write(item) | |
| #### LAYOUT of the main page is in two columns, feel free to change this... | |
| col1, col2 = st.columns([2, 1]) | |
| #### The second column are the filters | |
| with col2: | |
| st.button( | |
| ":grey_question: Come usare questa app", | |
| key="info", | |
| type="primary", | |
| on_click=info, | |
| ) | |
| with st.container(border=True): | |
| st.write("**Filtri**") | |
| st.multiselect( | |
| label="Apprendimento/privazione educativa *", | |
| options=OPTIONS_DEPRIVATION, | |
| key="filter_deprivation", | |
| ) | |
| st.multiselect( | |
| label="Obiettivo dell'iniziativa", | |
| options=OPTIONS_OBJECTIVE, | |
| key="filter_objective", | |
| ) | |
| st.multiselect( | |
| label="Attività realizzate", | |
| options=OPTIONS_ACTIVITIES, | |
| key="filter_activities", | |
| ) | |
| st.multiselect( | |
| label="Target", | |
| options=OPTIONS_TARGET, | |
| key="filter_target", | |
| ) | |
| # st.multiselect(label="ambito territoriale",options=["Internazionale","Multi-regionale","Regionale","Locale"], key="filter_level") | |
| for k in range(1): | |
| st.write("") | |
| st.caption( | |
| "*Per la definizione delle categorie riportate è stata utilizzata la classificazione messa a punto da Save the Children" | |
| ) | |
| #### dynamic color | |
| #### DATA | |
| color_masks = construct_filters(st.session_state, mappatura_data) | |
| df_color = mappatura_data[color_masks] | |
| chosen_ids = list(df_color["_id"]) | |
| color_reg_dyn = np.zeros(len(all_regions)) | |
| color_pro_dyn = np.zeros(len(all_provinces)) | |
| color_mun_dyn = np.zeros(len(all_munis)) | |
| for id in chosen_ids: | |
| color_reg_dyn += np.array(presence_regioni[id]) | |
| color_pro_dyn += np.array(presence_province[id]) | |
| color_mun_dyn += np.array(presence_comuni[id]) | |
| color_data_regioni2 = pd.DataFrame({"reg_name": all_regions, "color": color_reg_dyn}) | |
| color_data_province2 = pd.DataFrame({"prov_name": all_provinces, "color": color_pro_dyn}) | |
| color_data_comuni2 = pd.DataFrame({"name": all_munis, "color": color_mun_dyn}) | |
| #### MAP | |
| # This part of the code deals with the map | |
| # Use the Finite State Machine class to figure out what to show. | |
| map_name, cur_center, cur_zoom = st.session_state["view"].render() | |
| base_map = folium.Map(location=cur_center, zoom_start=cur_zoom, scrollWheelZoom=False) | |
| # This is the tooltip section, depending on the level we show different tooltips | |
| # it could be made fancier, but this is just an example | |
| CRPL_SETTINGS[0]["data"] = color_data_regioni2 | |
| CRPL_SETTINGS[1]["data"] = color_data_province2 | |
| CRPL_SETTINGS[2]["data"] = color_data_comuni2 | |
| cp_level = st.session_state["view"].level | |
| with open(map_name, encoding="utf-8") as f: # aggiunta per codifica UTF8 su WIN | |
| geo_data = json.load(f) # aggiunta per codifica UTF8 su WIN | |
| choropleth = folium.Choropleth( | |
| geo_data=geo_data, # ← ora è un dict Python, non un path (geo_data al posto di map_name) | |
| key_on=CRPL_SETTINGS[cp_level]["key_on"], | |
| data=CRPL_SETTINGS[cp_level]["data"], | |
| columns=CRPL_SETTINGS[cp_level]["columns"], | |
| line_opacity=0.75, | |
| highlight=True, | |
| bins=CRPL_SETTINGS[cp_level]["bins"], | |
| ) | |
| tooltip = folium.GeoJsonTooltip(CRPL_SETTINGS[cp_level]["tooltip"], aliases=[""]) | |
| tooltip.add_to(choropleth.geojson) | |
| choropleth.geojson.add_to(base_map) | |
| #placeholder2 = st.empty() | |
| #### The first column is the map | |
| with col1: | |
| ### This button is for going up: when clicked it calls the "go_up function" | |
| # st.button(label=":arrow_up: Vai al livello territoriale superiore",type='primary', on_click=clicked_go_up) | |
| ### This is the map, here we call it, which means it will be displayed here. | |
| st.button( | |
| label=":arrow_up: Vai al livello territoriale superiore", | |
| type="primary", | |
| on_click=clicked_go_up, | |
| ) | |
| st_data = st_folium(base_map, height=600, width=600) | |
| if st_data is not None: | |
| ## When the map is clicked the 'last_active_drawing' variable is filled with the properties of the clicked object | |
| ## I check that it exists and it is not empty, that means the map was clicked. | |
| if ( | |
| "last_active_drawing" in st_data | |
| and st_data["last_active_drawing"] is not None | |
| ): | |
| last_level = st.session_state["view"].level | |
| ## Update the Finite State Machine | |
| st.session_state["view"].go_down( | |
| st_data["last_active_drawing"]["properties"] | |
| ) | |
| ## I use the "last_level" to see if I just transitioned from province to comune level, in that case I rerun the script. | |
| if last_level < 2: | |
| ## This means we are drilling down so we need to rerun the script. | |
| st.rerun() | |
| else: | |
| ## This means we are already at the comune level, so we just changed the comune view... | |
| pass | |
| # st.write(st_data["last_active_drawing"]['properties']['name']) | |
| else: | |
| pass | |
| if st_data is None: | |
| st.write("None") | |
| #### DATA | |
| full_masks = construct_filters_geo(st.session_state, mappatura_data, st_data) | |
| df_display = mappatura_data[full_masks] | |
| # This part of the code deals with the data table | |
| gb = GridOptionsBuilder.from_dataframe(df_display[COLUMNS_SHOW]) | |
| col_width = 600 | |
| gb.configure_column( | |
| "Titolo", | |
| header_name="Titolo", | |
| editable=False, | |
| width=col_width, | |
| initialWidth=col_width, | |
| maxWidth=col_width, | |
| minWidth=col_width, | |
| suppressSizeToFit=True, | |
| ) | |
| gb.configure_column( | |
| "Apprendimento/privazione educativa", | |
| header_name="Apprendimento", | |
| editable=False, | |
| width=col_width, | |
| initialWidth=col_width, | |
| maxWidth=col_width, | |
| minWidth=col_width, | |
| suppressSizeToFit=True, | |
| ) | |
| gb.configure_column( | |
| "Obiettivo sintetico", | |
| header_name="Obiettivo", | |
| editable=False, | |
| width=col_width, | |
| initialWidth=col_width, | |
| maxWidth=col_width, | |
| minWidth=col_width, | |
| suppressSizeToFit=True, | |
| ) | |
| gb.configure_column( | |
| "Attività sintetico", | |
| header_name="Attività", | |
| editable=False, | |
| width=col_width, | |
| initialWidth=col_width, | |
| maxWidth=col_width, | |
| minWidth=col_width, | |
| suppressSizeToFit=True, | |
| ) | |
| gb.configure_column( | |
| "Target", | |
| header_name="Target", | |
| editable=False, | |
| width=col_width, | |
| initialWidth=col_width, | |
| maxWidth=col_width, | |
| minWidth=col_width, | |
| suppressSizeToFit=True, | |
| ) | |
| gb.configure_column( | |
| "Link", | |
| header_name="Link", | |
| editable=False, | |
| width=col_width, | |
| initialWidth=col_width, | |
| maxWidth=col_width, | |
| minWidth=col_width, | |
| suppressSizeToFit=True, | |
| ) | |
| gb.configure_selection(selection_mode="single") | |
| gb.configure_side_bar() | |
| gridOptions = gb.build() | |
| # cols = gridOptions['columnDefs'] | |
| # for col in cols: | |
| # if col['field'] == 'Titolo': | |
| # col["suppressSizeToFit"] = True | |
| #with placeholder2: | |
| data = AgGrid( | |
| df_display, | |
| gridOptions=gridOptions, | |
| enable_enterprise_modules=False, | |
| allow_unsafe_jscode=True, | |
| update_mode=GridUpdateMode.SELECTION_CHANGED, | |
| columns_auto_size_mode=ColumnsAutoSizeMode.FIT_ALL_COLUMNS_TO_VIEW, | |
| ) | |
| save_report(st.session_state, modal=None, error=None) | |
| if "selected_rows" in data and data["selected_rows"] is not None: | |
| selected_row = data["selected_rows"].iloc[0].to_dict() | |
| if "last_displayed_popup" in st.session_state: | |
| if st.session_state["last_displayed_popup"] != selected_row["Titolo"]: | |
| st.session_state["last_displayed_popup"] = selected_row["Titolo"] | |
| show_modal(selected_row) | |
| else: | |
| st.session_state["last_displayed_popup"] = selected_row["Titolo"] | |
| show_modal(selected_row) | |