diff --git a/src/callbacks.py b/src/callbacks.py deleted file mode 100644 index 050a838..0000000 --- a/src/callbacks.py +++ /dev/null @@ -1,36 +0,0 @@ -import polars as pl -from dash import html - -from src.figures import DataTable -from src.utils import add_links_in_dict, format_values, setup_table_columns - - -def get_top_org_table(data, org_type: str, extra_columns: list): - dff = pl.DataFrame(data, strict=False, infer_schema_length=5000) - if dff.height == 0: - return html.Div() - - extra_columns = [] if extra_columns is None else extra_columns - - dff = dff.select(["uid", f"{org_type}_id", f"{org_type}_nom"] + extra_columns) - dff_nb = dff.group_by( - f"{org_type}_id", f"{org_type}_nom", "titulaire_distance" - ).agg(pl.len().alias("Attributions"), pl.sum("montant").alias("montant")) - dff_nb = dff_nb.sort(by="montant", descending=True, nulls_last=True) - dff_nb = dff_nb.cast(pl.String) - dff_nb = dff_nb.fill_null("") - dff_nb = format_values(dff_nb) - columns, tooltip = setup_table_columns( - dff_nb, hideable=False, exclude=[f"{org_type}_id"], new_columns=["Attributions"] - ) - data = dff_nb.to_dicts() - data = add_links_in_dict(data, f"{org_type}") - - return DataTable( - dtid=f"top10_{org_type}", - data=data, - page_action="native", - page_size=10, - columns=columns, - tooltip_header=tooltip, - ) diff --git a/src/figures.py b/src/figures.py index f94d2c8..687b5c0 100644 --- a/src/figures.py +++ b/src/figures.py @@ -11,7 +11,14 @@ import polars as pl from dash import dash_table, dcc, html from dash_extensions.javascript import Namespace -from src.utils import data_schema, departements_geojson, df, format_number +from src.utils import ( + add_links, + data_schema, + departements_geojson, + df, + format_number, + setup_table_columns, +) def get_yearly_statistics(statistics, today_str) -> html.Div: @@ -356,15 +363,15 @@ def get_duplicate_matrix() -> dcc.Graph: def get_geographic_maps(dff: pl.DataFrame) -> list | None: """ - Génère les cartes géographiques pour la métropole et les DOM-TOM. + Génère les cartes géographiques pour l'hexagone et les DOM-TOM. """ regions: dict = { - "Métropole": { + "Hexagone": { "coordinates": [46.6, 2.2], "zoom_leaflet": 5, "zoom_chloropleth": 1, - "name": "Métropole", + "name": "Hexagone", }, "971": { "coordinates": [16.23, -61.55], @@ -400,7 +407,7 @@ def get_geographic_maps(dff: pl.DataFrame) -> list | None: def make_map_data(region_code: str) -> tuple[list, str or None]: lff: pl.LazyFrame = dff.lazy() - if region_code == "Métropole": + if region_code == "Hexagone": lff = lff.filter( (pl.col("acheteur_departement_code").str.len_chars() == 2) & (pl.col("titulaire_departement_code").str.len_chars() == 2) @@ -418,8 +425,8 @@ def get_geographic_maps(dff: pl.DataFrame) -> list | None: dfs = [] - if (code == "Métropole" and nb_marches > 30000) or ( - code != "Métropole" and nb_marches > 10000 + if (code == "Hexagone" and nb_marches > 30000) or ( + code != "Hexagone" and nb_marches > 10000 ): _map_type: str = "chloropleth" @@ -490,7 +497,7 @@ def get_geographic_maps(dff: pl.DataFrame) -> list | None: else: raise ValueError(f"Map type '{map_type}' not recognised") - lg, xl = (12, 8) if code == "Métropole" else (6, 4) + lg, xl = (12, 8) if code == "Hexagone" else (6, 4) col = make_card(regions[code]["name"], fig=map_graph, lg=lg, xl=xl) cols.append(col) @@ -573,7 +580,7 @@ def make_clusters_map(region: dict) -> dl.Map: zoom=zoom, style={ "width": "100%", - "height": "400px" if name == "Métropole" else "300px", + "height": "400px" if name == "Hexagone" else "300px", }, id=f"map-{region_id}", ) @@ -626,7 +633,7 @@ def get_distance_histogram(lff: pl.LazyFrame) -> dcc.Graph: ) fig.update_layout(bargap=0) - fig.update_layout(margin=dict(r=10)) + fig.update_layout(margin=dict(r=10, t=10)) fig.update_xaxes( tickvals=[0, 1, 2, 3, 4], ticktext=["1", "10", "100", "1 000", "10 000"], @@ -807,3 +814,44 @@ def make_column_picker(page: str): ) return table + + +def get_top_org_table(data, org_type: str, extra_columns: list, filters: bool = True): + if isinstance(data, pl.LazyFrame): + lff = data + else: + lff = pl.LazyFrame(data, strict=False, infer_schema_length=5000) + + if org_type == "titulaire": + extra_columns.append("titulaire_typeIdentifiant") + columns = ["uid", f"{org_type}_id", f"{org_type}_nom"] + extra_columns + + lff = lff.select(columns) + lff = lff.group_by([f"{org_type}_id", f"{org_type}_nom"] + extra_columns).agg( + pl.len().alias("Attributions") + ) + lff = lff.sort(by="Attributions", descending=True, nulls_last=True) + lff = lff.cast(pl.String) + lff = lff.fill_null("") + + dff: pl.DataFrame = lff.collect(engine="streaming") + + if dff.height == 0: + return html.Div() + + columns, tooltip = setup_table_columns( + dff, hideable=False, exclude=[f"{org_type}_id"], new_columns=["Attributions"] + ) + dff = add_links(dff) + data = dff.to_dicts() + # data = add_links_in_dict(data, f"{org_type}") + + return DataTable( + dtid=f"top10_{org_type}", + data=data, + page_action="native", + page_size=10, + columns=columns, + tooltip_header=tooltip, + filter_action="native" if filters else "none", + ) diff --git a/src/pages/acheteur.py b/src/pages/acheteur.py index 1315e2e..623af4c 100644 --- a/src/pages/acheteur.py +++ b/src/pages/acheteur.py @@ -15,10 +15,10 @@ from dash import ( register_page, ) -from src.callbacks import get_top_org_table from src.figures import ( DataTable, get_distance_histogram, + get_top_org_table, make_card, make_column_picker, point_on_map, @@ -372,7 +372,7 @@ def get_last_marches_data( Input(component_id="acheteur_data", component_property="data"), ) def get_top_titulaires(data): - table = get_top_org_table(data, "titulaire", ["titulaire_distance", "montant"]) + table = get_top_org_table(data, "titulaire", ["titulaire_distance"]) return make_card(fig=table, title="Top titulaires", lg=12, xl=12) diff --git a/src/pages/observatoire.py b/src/pages/observatoire.py index 4953732..3b66447 100644 --- a/src/pages/observatoire.py +++ b/src/pages/observatoire.py @@ -23,6 +23,7 @@ from src.figures import ( get_distance_histogram, get_duplicate_matrix, get_geographic_maps, + get_top_org_table, make_card, make_donut, ) @@ -649,6 +650,16 @@ def udpate_dashboard_cards( ) ) + top_acheteurs = get_top_org_table( + lff, org_type="acheteur", filters=False, extra_columns=[] + ) + cards.append(make_card(title="Top acheteurs", fig=top_acheteurs, lg=12, xl=8)) + + top_titulaires = get_top_org_table( + lff, org_type="titulaire", filters=False, extra_columns=[] + ) + cards.append(make_card(title="Top titulaires", fig=top_titulaires, lg=12, xl=8)) + geographic_maps: list[dbc.Col] = get_geographic_maps(dff) other_cards = [] diff --git a/src/pages/titulaire.py b/src/pages/titulaire.py index 158a298..b13438c 100644 --- a/src/pages/titulaire.py +++ b/src/pages/titulaire.py @@ -15,10 +15,10 @@ from dash import ( register_page, ) -from src.callbacks import get_top_org_table from src.figures import ( DataTable, get_distance_histogram, + get_top_org_table, make_column_picker, point_on_map, ) @@ -395,7 +395,7 @@ def get_last_marches_data( Input(component_id="titulaire_data", component_property="data"), ) def get_top_acheteurs(data): - return get_top_org_table(data, "acheteur", ["titulaire_distance", "montant"]) + return get_top_org_table(data, "acheteur", ["titulaire_distance"]) @callback(