Style inputs, ajout donut type marché #65
This commit is contained in:
@@ -197,6 +197,16 @@ p.version > a {
|
||||
background-color: rgba(255, 240, 240, 0.4);
|
||||
}
|
||||
|
||||
#filters .col > * {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
#filters input[type="text"] {
|
||||
border: 1px #ccc solid;
|
||||
border-radius: 3px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/* --- Tables (Dash & Custom) --- */
|
||||
|
||||
/* Table Menu (Exports etc) */
|
||||
|
||||
+50
-45
@@ -52,19 +52,18 @@ def get_yearly_statistics(statistics, today_str) -> html.Div:
|
||||
return html.Div(children=table, className="marches_table")
|
||||
|
||||
|
||||
def get_barchart_sources(df_source: pl.DataFrame, type_date: str):
|
||||
lf = df_source.lazy()
|
||||
def get_barchart_sources(lff: pl.LazyFrame, type_date: str):
|
||||
labels = {
|
||||
"dateNotification": "notification",
|
||||
"datePublicationDonnees": "publication des données",
|
||||
}
|
||||
|
||||
lf = lf.select("uid", type_date, "sourceDataset")
|
||||
lff = lff.select("uid", type_date, "sourceDataset")
|
||||
|
||||
lf = lf.unique("uid")
|
||||
lff = lff.unique("uid")
|
||||
|
||||
# Rassemblement des datasets Atexo pour ne pas surcharger le graphique
|
||||
lf = lf.with_columns(
|
||||
lff = lff.with_columns(
|
||||
pl.when(pl.col("sourceDataset").str.starts_with("atexo"))
|
||||
.then(pl.lit("plateformes atexo"))
|
||||
.otherwise(pl.col("sourceDataset"))
|
||||
@@ -72,34 +71,34 @@ def get_barchart_sources(df_source: pl.DataFrame, type_date: str):
|
||||
)
|
||||
|
||||
# Rassemblement des datasets AWS pour ne pas surcharger le graphique
|
||||
lf = lf.with_columns(
|
||||
lff = lff.with_columns(
|
||||
pl.when(pl.col("sourceDataset").str.contains(r"aws|marches\-publics.info"))
|
||||
.then(pl.lit("aws"))
|
||||
.otherwise(pl.col("sourceDataset"))
|
||||
.alias("sourceDataset")
|
||||
)
|
||||
|
||||
lf = lf.with_columns(pl.col(type_date).dt.year().alias("annee"))
|
||||
lf = lf.filter(
|
||||
lff = lff.with_columns(pl.col(type_date).dt.year().alias("annee"))
|
||||
lff = lff.filter(
|
||||
pl.col(type_date).is_not_null() & pl.col("annee").is_between(2019, 2025)
|
||||
)
|
||||
lf = lf.with_columns(pl.col(type_date).cast(pl.String).str.head(7))
|
||||
lf = (
|
||||
lf.group_by([type_date, "sourceDataset"])
|
||||
lff = lff.with_columns(pl.col(type_date).cast(pl.String).str.head(7))
|
||||
lff = (
|
||||
lff.group_by([type_date, "sourceDataset"])
|
||||
.len()
|
||||
.sort(by=[type_date, "len"], descending=True)
|
||||
)
|
||||
|
||||
# lf = lf.with_columns(
|
||||
# lff = lff.with_columns(
|
||||
# pl.when(pl.col("sourceDataset").is_null()).then(
|
||||
# pl.lit("Source inconnue")).alias("sourceDataset")
|
||||
# )
|
||||
|
||||
lf = lf.sort(by=["sourceDataset"], descending=False)
|
||||
df: pl.DataFrame = lf.collect(engine="streaming")
|
||||
lff = lff.sort(by=["sourceDataset"], descending=False)
|
||||
dff: pl.DataFrame = lff.collect(engine="streaming")
|
||||
|
||||
fig = px.bar(
|
||||
df,
|
||||
dff,
|
||||
x=type_date,
|
||||
y="len",
|
||||
color="sourceDataset",
|
||||
@@ -111,7 +110,9 @@ def get_barchart_sources(df_source: pl.DataFrame, type_date: str):
|
||||
},
|
||||
)
|
||||
|
||||
return fig
|
||||
graph = dcc.Graph(figure=fig)
|
||||
|
||||
return graph
|
||||
|
||||
|
||||
def get_sources_tables(source_path) -> html.Div:
|
||||
@@ -301,33 +302,24 @@ class DataTable(dash_table.DataTable):
|
||||
)
|
||||
|
||||
|
||||
def get_duplicate_matrix() -> html.Div:
|
||||
def get_duplicate_matrix() -> dcc.Graph:
|
||||
"""
|
||||
Fonction développée avec l'aide de la LLM Euria d'Infomaniak.
|
||||
:return:
|
||||
"""
|
||||
result_df = pl.read_parquet(
|
||||
lff = pl.scan_parquet(
|
||||
"https://www.data.gouv.fr/api/1/datasets/r/a545bf6c-8b24-46ed-b49f-a32bf02eaffa"
|
||||
).sort("sourceDataset")
|
||||
result_df = result_df.select(
|
||||
["sourceDataset", "unique"] + sorted(result_df.columns[2:])
|
||||
lff = lff.select(
|
||||
["sourceDataset", "unique"] + sorted(lff.collect_schema().names()[2:])
|
||||
)
|
||||
|
||||
description = dcc.Markdown("""
|
||||
Ce graphique illustre les doublons de marchés publics entre sources, c'est-à-dire la proportion de marchés publiés par plus d'une source. Il s'appuie sur les identifiants `uid` qui sont pour chaque marché la concaténation du SIRET de l'acheteur et de l'identifiant interne du marché.
|
||||
|
||||
**Comment lire ce graphique ?**
|
||||
|
||||
On part des codes de sources de données en ordonnée. Ces jeux de données sont documentés dans [À propos](/a-propos#sources).
|
||||
|
||||
La première colonne (**unique**) représente le pourcentage de marchés fournis par cette source qui sont uniquement disponibles dans cette source. Plus le rouge est foncé, plus important est le pourcentage. Donc, à l'inverse, plus le rouge est clair dans la première colonne, plus la source en ordonnée a des marchés en commun avec d'autres sources, et donc plus on trouvera sur la même ligne d'autres cases plus ou moins foncées qui indiqueront avec quelles autres sources cette source partage des marchés.
|
||||
|
||||
Passez votre souris sur une case pour avoir les pourcentages exacts. À noter que ces statistiques sont produites avant le dédoublonnement qui a lieu avant la publication en Open Data et sur ce site.""")
|
||||
dff = lff.collect()
|
||||
|
||||
# Extract data
|
||||
z_data = result_df.select(pl.all().exclude("sourceDataset")).fill_null(0).to_numpy()
|
||||
x_labels = result_df.columns[1:] # columns after "sourceDataset"
|
||||
y_labels = result_df["sourceDataset"].to_list()
|
||||
z_data = dff.select(pl.all().exclude("sourceDataset")).fill_null(0).to_numpy()
|
||||
x_labels = dff.columns[1:] # columns after "sourceDataset"
|
||||
y_labels = dff["sourceDataset"].to_list()
|
||||
|
||||
# Create heatmap
|
||||
fig = go.Figure(
|
||||
@@ -345,7 +337,7 @@ def get_duplicate_matrix() -> html.Div:
|
||||
hoverongaps=False,
|
||||
showscale=True,
|
||||
hovertemplate=(
|
||||
"<b>%{z:.0%}</b> des marchés de <b>%{y}</b> sont également présents dans <b>%{x}</b>"
|
||||
"<b>%{z:.0%}</b> des marchés présents dans <b>%{y}</b> sont également présents dans <b>%{x}</b>"
|
||||
),
|
||||
)
|
||||
)
|
||||
@@ -364,13 +356,7 @@ def get_duplicate_matrix() -> html.Div:
|
||||
margin=dict(l=100, r=50, t=80, b=100), # Add margin for labels
|
||||
)
|
||||
|
||||
return html.Div(
|
||||
children=[
|
||||
html.H3("Doublons de marchés entre les sources"),
|
||||
description,
|
||||
dcc.Graph(figure=fig),
|
||||
]
|
||||
)
|
||||
return dcc.Graph(figure=fig)
|
||||
|
||||
|
||||
def get_geographic_maps(dff: pl.DataFrame) -> list | None:
|
||||
@@ -624,20 +610,39 @@ def make_card(
|
||||
return card
|
||||
|
||||
|
||||
def make_donut(lff: pl.LazyFrame, names_col):
|
||||
def make_donut(
|
||||
lff: pl.LazyFrame,
|
||||
names_col,
|
||||
per_uid: bool,
|
||||
nulls="?",
|
||||
):
|
||||
title = data_schema[names_col]["title"]
|
||||
lff = lff.rename({names_col: title})
|
||||
lff = lff.select("uid", title)
|
||||
|
||||
if per_uid:
|
||||
lff = lff.group_by("uid").first()
|
||||
|
||||
lff = lff.group_by(title).len("Nombre")
|
||||
lff = lff.with_columns(pl.col(title).replace(None, pl.lit("?")))
|
||||
lff = lff.with_columns(pl.col(title).replace(None, pl.lit(nulls)))
|
||||
dff = lff.collect(engine="streaming")
|
||||
dff = dff.with_columns(
|
||||
pl.col("Nombre")
|
||||
.map_elements(format_number, return_dtype=pl.String)
|
||||
.alias("Nombre_fmt")
|
||||
)
|
||||
fig = px.pie(
|
||||
lff.collect(engine="streaming"),
|
||||
dff,
|
||||
values="Nombre",
|
||||
names=title,
|
||||
hole=0.4,
|
||||
color_discrete_sequence=px.colors.qualitative.Safe,
|
||||
custom_data=["Nombre_fmt"],
|
||||
)
|
||||
fig = fig.update_traces(
|
||||
texttemplate="<b>%{label}</b><br><b>%{percent}</b>",
|
||||
hovertemplate="<b>%{label}</b><br>%{customdata[0]}<extra></extra>",
|
||||
)
|
||||
fig = fig.update_traces(texttemplate="<b>%{label}</b><br><b>%{percent}</b>")
|
||||
fig = fig.update_layout(showlegend=False, font=dict(size=14))
|
||||
graph = dcc.Graph(figure=fig)
|
||||
return graph
|
||||
|
||||
+211
-63
@@ -3,9 +3,15 @@ from datetime import datetime, timedelta
|
||||
import dash_bootstrap_components as dbc
|
||||
import polars as pl
|
||||
import polars.selectors as cs
|
||||
from dash import Input, Output, callback, dcc, html, register_page
|
||||
from dash import ALL, Input, Output, callback, ctx, dcc, html, register_page
|
||||
|
||||
from src.figures import get_geographic_maps, make_card, make_donut
|
||||
from src.figures import (
|
||||
get_barchart_sources,
|
||||
get_duplicate_matrix,
|
||||
get_geographic_maps,
|
||||
make_card,
|
||||
make_donut,
|
||||
)
|
||||
from src.utils import (
|
||||
departements,
|
||||
df,
|
||||
@@ -38,6 +44,29 @@ for code, obj in departements.items():
|
||||
layout = [
|
||||
dcc.Store(id="dashboard-filters"),
|
||||
dcc.Location(id="dashboard_url"),
|
||||
dbc.Modal(
|
||||
[
|
||||
dbc.ModalHeader(dbc.ModalTitle("Montants")),
|
||||
dbc.ModalBody(
|
||||
[
|
||||
dcc.Markdown(
|
||||
"""
|
||||
Les données saisies et publiées par les acheteurs comportent de nombreux montants farfelus qui sabotent les statistiques, au lieu de montants estimés avec rigueur. On parle de montant atteignant parfois les millions de milliards. Certains réutilisateurs mettent de côté ces marchés ou bien modifient les montants selon des règles fatalement arbitraires. J'ai fait le choix de ne quasiment pas modifier les données* afin de visibiliser le problème.
|
||||
|
||||
Alors, on fait comment ?
|
||||
|
||||
\* Les montants composés de plus de 11 chiffres, sans les décimales, [sont ramenés](https://github.com/ColinMaudry/decp-processing/blob/main/src/tasks/clean.py#L63-L71) à 12 311 111 111, un nombre qui reste très élevé et qui est facilement reconnaissable.
|
||||
"""
|
||||
),
|
||||
]
|
||||
),
|
||||
dbc.ModalFooter(
|
||||
dbc.Button("Fermer", id="montant-modal-close", className="ms-auto")
|
||||
),
|
||||
],
|
||||
id="montant-modal",
|
||||
is_open=False,
|
||||
),
|
||||
html.Div(
|
||||
className="container-fluid",
|
||||
children=[
|
||||
@@ -56,66 +85,109 @@ layout = [
|
||||
children=[
|
||||
html.H5("Période d'attribution"),
|
||||
dbc.Row(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_year",
|
||||
options=options_years,
|
||||
placeholder="12 derniers mois",
|
||||
dbc.Col(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_year",
|
||||
options=options_years,
|
||||
placeholder="12 derniers mois",
|
||||
),
|
||||
),
|
||||
),
|
||||
html.H5("Acheteur"),
|
||||
dbc.Row(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_acheteur_categorie",
|
||||
options=get_enum_values_as_dict(
|
||||
"acheteur_categorie"
|
||||
dbc.Col(
|
||||
dcc.Input(
|
||||
id="dashboard_acheteur_id",
|
||||
placeholder="SIRET",
|
||||
style={"width": "100%"},
|
||||
),
|
||||
placeholder="Catégorie",
|
||||
),
|
||||
),
|
||||
dbc.Row(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_acheteur_departement_code",
|
||||
searchable=True,
|
||||
multi=True,
|
||||
placeholder="Code département acheteur",
|
||||
options=options_departements,
|
||||
dbc.Col(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_acheteur_categorie",
|
||||
options=get_enum_values_as_dict(
|
||||
"acheteur_categorie"
|
||||
),
|
||||
placeholder="Catégorie",
|
||||
)
|
||||
),
|
||||
),
|
||||
dbc.Row(
|
||||
dbc.Col(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_acheteur_departement_code",
|
||||
searchable=True,
|
||||
multi=True,
|
||||
placeholder="Département",
|
||||
options=options_departements,
|
||||
),
|
||||
),
|
||||
),
|
||||
html.H5("Titulaire"),
|
||||
dbc.Row(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_titulaire_categorie",
|
||||
placeholder="Catégorie",
|
||||
options=get_enum_values_as_dict(
|
||||
"titulaire_categorie"
|
||||
dbc.Col(
|
||||
dcc.Input(
|
||||
id="dashboard_titulaire_id",
|
||||
placeholder="SIRET",
|
||||
style={"width": "100%"},
|
||||
),
|
||||
),
|
||||
),
|
||||
dbc.Row(
|
||||
dbc.Col(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_titulaire_categorie",
|
||||
placeholder="Catégorie",
|
||||
options=get_enum_values_as_dict(
|
||||
"titulaire_categorie"
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
dbc.Row(
|
||||
dbc.Col(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_titulaire_departement_code",
|
||||
searchable=True,
|
||||
multi=True,
|
||||
placeholder="Département",
|
||||
options=options_departements,
|
||||
),
|
||||
),
|
||||
),
|
||||
html.H5("Marché"),
|
||||
dbc.Row(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_marche_type",
|
||||
placeholder="Type",
|
||||
options=get_enum_values_as_dict("type"),
|
||||
),
|
||||
),
|
||||
dbc.Row(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_marche_considerationsSociales",
|
||||
placeholder="Considérations sociales",
|
||||
options=get_enum_values_as_dict(
|
||||
"considerationsSociales"
|
||||
dbc.Col(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_marche_type",
|
||||
placeholder="Type",
|
||||
options=get_enum_values_as_dict("type"),
|
||||
),
|
||||
multi=True,
|
||||
),
|
||||
),
|
||||
dbc.Row(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_marche_considerationsEnvironnementales",
|
||||
placeholder="Considérations environnementales",
|
||||
multi=True,
|
||||
options=get_enum_values_as_dict(
|
||||
"considerationsEnvironnementales"
|
||||
dbc.Col(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_marche_considerationsSociales",
|
||||
placeholder="Considérations sociales",
|
||||
options=get_enum_values_as_dict(
|
||||
"considerationsSociales"
|
||||
),
|
||||
multi=True,
|
||||
),
|
||||
),
|
||||
),
|
||||
dbc.Row(
|
||||
dbc.Col(
|
||||
dcc.Dropdown(
|
||||
id="dashboard_marche_considerationsEnvironnementales",
|
||||
placeholder="Considérations environnementales",
|
||||
multi=True,
|
||||
options=get_enum_values_as_dict(
|
||||
"considerationsEnvironnementales"
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -140,21 +212,27 @@ layout = [
|
||||
@callback(
|
||||
Output("cards", "children"),
|
||||
Input("dashboard_year", "value"),
|
||||
Input("dashboard_acheteur_id", "value"),
|
||||
Input("dashboard_acheteur_categorie", "value"),
|
||||
Input("dashboard_acheteur_departement_code", "value"),
|
||||
Input("dashboard_titulaire_id", "value"),
|
||||
Input("dashboard_titulaire_categorie", "value"),
|
||||
Input("dashboard_titulaire_departement_code", "value"),
|
||||
Input("dashboard_marche_type", "value"),
|
||||
Input("dashboard_marche_considerationsSociales", "value"),
|
||||
Input("dashboard_marche_considerationsEnvironnementales", "value"),
|
||||
)
|
||||
def udpate_dashboard_cards(
|
||||
dashboard_year,
|
||||
dashboard_acheteur_id,
|
||||
dashboard_acheteur_categorie,
|
||||
dashboard_acheteur_departement_code,
|
||||
dashboard_titulaire_id,
|
||||
dashboard_titulaire_categorie,
|
||||
dashboard_titulaire_departement_code,
|
||||
dashboard_marche_type,
|
||||
dashboard_marche_considerationsSociales,
|
||||
dashboard_marche_considerationsEnvironnementales,
|
||||
dashboard_marche_considerations_sociales,
|
||||
dashboard_marche_considerations_environnementales,
|
||||
):
|
||||
lff: pl.LazyFrame = df.lazy()
|
||||
lff = lff.select(
|
||||
@@ -165,6 +243,8 @@ def udpate_dashboard_cards(
|
||||
"montant",
|
||||
"considerationsSociales",
|
||||
"considerationsEnvironnementales",
|
||||
"sourceDataset",
|
||||
"type",
|
||||
)
|
||||
|
||||
# Application des filtres
|
||||
@@ -180,40 +260,57 @@ def udpate_dashboard_cards(
|
||||
|
||||
## Acheteur
|
||||
|
||||
if dashboard_acheteur_categorie:
|
||||
lff = lff.filter(pl.col("acheteur_categorie") == dashboard_acheteur_categorie)
|
||||
|
||||
if dashboard_acheteur_departement_code:
|
||||
lff = lff.filter(
|
||||
pl.col("acheteur_departement_code").is_in(
|
||||
dashboard_acheteur_departement_code
|
||||
if dashboard_acheteur_id:
|
||||
lff = lff.filter(pl.col("acheteur_id").str.contains(dashboard_acheteur_id))
|
||||
else:
|
||||
if dashboard_acheteur_categorie:
|
||||
lff = lff.filter(
|
||||
pl.col("acheteur_categorie") == dashboard_acheteur_categorie
|
||||
)
|
||||
|
||||
if dashboard_acheteur_departement_code:
|
||||
lff = lff.filter(
|
||||
pl.col("acheteur_departement_code").is_in(
|
||||
dashboard_acheteur_departement_code
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
## Titulaire
|
||||
|
||||
if dashboard_titulaire_categorie:
|
||||
lff = lff.filter(pl.col("titulaire_categorie") == dashboard_titulaire_categorie)
|
||||
if dashboard_titulaire_id:
|
||||
lff = lff.filter(pl.col("titulaire_id").str.contains(dashboard_titulaire_id))
|
||||
else:
|
||||
if dashboard_titulaire_categorie:
|
||||
lff = lff.filter(
|
||||
pl.col("titulaire_categorie") == dashboard_titulaire_categorie
|
||||
)
|
||||
|
||||
if dashboard_titulaire_departement_code:
|
||||
lff = lff.filter(
|
||||
pl.col("titulaire_departement_code").is_in(
|
||||
dashboard_titulaire_departement_code
|
||||
)
|
||||
)
|
||||
|
||||
## Marché
|
||||
|
||||
if dashboard_marche_type:
|
||||
lff = lff.filter(pl.col("type") == dashboard_marche_type)
|
||||
|
||||
if dashboard_marche_considerationsSociales:
|
||||
if dashboard_marche_considerations_sociales:
|
||||
lff = lff.filter(
|
||||
pl.col("considerationsSociales")
|
||||
.str.split(", ")
|
||||
.list.set_intersection(dashboard_marche_considerationsSociales)
|
||||
.list.set_intersection(dashboard_marche_considerations_sociales)
|
||||
.list.len()
|
||||
> 0
|
||||
)
|
||||
|
||||
if dashboard_marche_considerationsEnvironnementales:
|
||||
if dashboard_marche_considerations_environnementales:
|
||||
lff = lff.filter(
|
||||
pl.col("considerationsEnvironnementales")
|
||||
.str.split(", ")
|
||||
.list.set_intersection(dashboard_marche_considerationsEnvironnementales)
|
||||
.list.set_intersection(dashboard_marche_considerations_environnementales)
|
||||
.list.len()
|
||||
> 0
|
||||
)
|
||||
@@ -228,7 +325,8 @@ def udpate_dashboard_cards(
|
||||
df_per_uid = (
|
||||
dff.select("uid", "montant").group_by("uid").agg(pl.col("montant").first())
|
||||
)
|
||||
total_montant = df_per_uid.select(pl.col("montant").sum()).item()
|
||||
|
||||
total_montant = int(df_per_uid.select(pl.col("montant").sum()).item())
|
||||
nb_marches = df_per_uid.height
|
||||
|
||||
cards = []
|
||||
@@ -241,19 +339,69 @@ def udpate_dashboard_cards(
|
||||
html.P(
|
||||
["Nombre de titulaires : ", html.Strong(str(format_number(nb_titulaires)))]
|
||||
),
|
||||
html.P(["Montant total : ", html.Strong(format_number(total_montant) + " €")]),
|
||||
html.P(
|
||||
[
|
||||
"Montant total (",
|
||||
html.Span(
|
||||
"?",
|
||||
id={"type": "modal-trigger", "index": "montant"},
|
||||
style={"cursor": "pointer", "textDecoration": "underline dotted"},
|
||||
),
|
||||
") : ",
|
||||
html.Strong(format_number(total_montant) + " €"),
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
cards.append(make_card(title="Résumé", paragraphs=card_basic_counts))
|
||||
|
||||
donut_acheteur_categorie = make_donut(lff, "acheteur_categorie")
|
||||
cards.append(make_card(title="Catégorie d'acheteur", fig=donut_acheteur_categorie))
|
||||
donut_acheteur_categorie = make_donut(
|
||||
lff, "acheteur_categorie", nulls="Autres", per_uid=True
|
||||
)
|
||||
cards.append(
|
||||
make_card(
|
||||
title="Catégorie d'acheteur", fig=donut_acheteur_categorie, lg=12, xl=8
|
||||
)
|
||||
)
|
||||
|
||||
donut_titulaire_categorie = make_donut(lff, "titulaire_categorie")
|
||||
donut_titulaire_categorie = make_donut(
|
||||
lff, "titulaire_categorie", per_uid=False, nulls="?"
|
||||
)
|
||||
cards.append(
|
||||
make_card(title="Catégorie d'entreprise", fig=donut_titulaire_categorie)
|
||||
)
|
||||
|
||||
donut_marche_type = make_donut(lff, "type", per_uid=True, nulls="?")
|
||||
cards.append(make_card(title="Type d'achat", fig=donut_marche_type))
|
||||
|
||||
geographic_maps: list[dbc.Col] = get_geographic_maps(dff)
|
||||
|
||||
return dbc.Row(children=cards + geographic_maps)
|
||||
other_cards = []
|
||||
|
||||
sources_barchart = get_barchart_sources(lff, type_date="dateNotification")
|
||||
other_cards.append(
|
||||
make_card(title="Sources de données", fig=sources_barchart, lg=12, xl=8)
|
||||
)
|
||||
|
||||
duplicate_matrix = get_duplicate_matrix()
|
||||
other_cards.append(
|
||||
make_card(
|
||||
title="Matrice de doublons entre sources de données",
|
||||
subtitle="Ce graphique illustre les doublons de marchés publics entre sources, c'est-à-dire la proportion de marchés publiés par plus d'une source.",
|
||||
fig=duplicate_matrix,
|
||||
lg=12,
|
||||
xl=8,
|
||||
)
|
||||
)
|
||||
|
||||
return dbc.Row(children=cards + geographic_maps + other_cards)
|
||||
|
||||
|
||||
@callback(
|
||||
Output("montant-modal", "is_open"),
|
||||
Input({"type": "modal-trigger", "index": ALL}, "n_clicks"),
|
||||
Input("montant-modal-close", "n_clicks"),
|
||||
prevent_initial_call=True,
|
||||
)
|
||||
def toggle_montant_modal(n_triggers, _close):
|
||||
return isinstance(ctx.triggered_id, dict) and any(n_triggers)
|
||||
|
||||
Reference in New Issue
Block a user