From e8eba9ba66d4e6a6b2d198e5daff909a3db4f56e Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Fri, 19 Sep 2025 08:24:20 +0200 Subject: [PATCH 01/24] Suppression des champs siren, noms des champs source --- src/pages/home.py | 6 ++---- src/utils.py | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pages/home.py b/src/pages/home.py index c7b4095..cdf2c7f 100644 --- a/src/pages/home.py +++ b/src/pages/home.py @@ -28,8 +28,6 @@ lf = lf.filter(pl.col("donneesActuelles")) # Suppression des colonnes inutiles lf = lf.drop( [ - "titulaire_siren", - "acheteur_siren", "donneesActuelles", ] ) @@ -231,8 +229,8 @@ def download_data(n_clicks, hidden_columns: list = None): # Rétablissement des colonnes source et sourceOpenData (voir add_resource_link) df_to_download = df_to_download.with_columns( - pl.col("source").str.extract(r'href="(.*?)"').alias("sourceOpenData"), - pl.col("source").str.extract(r'">(.*?)<').alias("source"), + pl.col("source").str.extract(r'href="(.*?)"').alias("sourceFile"), + pl.col("source").str.extract(r'">(.*?)<').alias("sourceDataset"), ) # Les colonnes masquées sont supprimées diff --git a/src/utils.py b/src/utils.py index ace70c8..5efe954 100644 --- a/src/utils.py +++ b/src/utils.py @@ -43,10 +43,10 @@ def split_filter_part(filter_part): def add_resource_link(lff: pl.LazyFrame) -> pl.LazyFrame: lff = lff.with_columns( ( - '' + pl.col("source") + "" + '' + pl.col("sourceDataset") + "" ).alias("source") ) - lff = lff.drop("sourceOpenData") + lff = lff.drop(["sourceFile", "sourceDataset"]) return lff From b93b290f97481fa0c3696c6198a418f78be26128 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Fri, 19 Sep 2025 08:26:03 +0200 Subject: [PATCH 02/24] =?UTF-8?q?Lazyframe=20d=C3=A8s=20le=20d=C3=A9part?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/utils.py b/src/utils.py index 5efe954..3c6856e 100644 --- a/src/utils.py +++ b/src/utils.py @@ -110,14 +110,12 @@ def get_decp_data() -> pl.LazyFrame: logger.info( f"Lecture du fichier parquet ({os.getenv('DATA_FILE_PARQUET_PATH')})..." ) - df: pl.DataFrame = pl.read_parquet(os.getenv("DATA_FILE_PARQUET_PATH")) + lff: pl.LazyFrame = pl.scan_parquet(os.getenv("DATA_FILE_PARQUET_PATH")) except ComputeError: # Le fichier est probablement en cours de mise à jour logger.info("Échec, nouvelle tentative dans 10s...") sleep(10) - df: pl.DataFrame = pl.read_parquet(os.getenv("DATA_FILE_PARQUET_PATH")) - - lff: pl.LazyFrame = df.lazy() + lff: pl.LazyFrame = pl.scan_parquet(os.getenv("DATA_FILE_PARQUET_PATH")) # Remplacement des valeurs numériques par des chaînes de caractères # lff = numbers_to_strings(lff) From 5ca5fc300fe5980356f418e8e2f56dab05e6bad5 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Fri, 19 Sep 2025 09:30:00 +0200 Subject: [PATCH 03/24] Adaptation du graphique aux nouvelles sources #32 --- src/figures.py | 52 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/figures.py b/src/figures.py index 54e9140..9d93c0c 100644 --- a/src/figures.py +++ b/src/figures.py @@ -4,13 +4,13 @@ import plotly.express as px import polars as pl -def get_map_count_marches(lf): +def get_map_count_marches(lf: pl.LazyFrame): lf = lf.with_columns( pl.col("lieuExecution_code").str.head(2).str.zfill(2).alias("Département") ) lf = ( - lf.unique(subset="uid") - .select(["uid", "Département"]) + lf.select(["uid", "Département"]) + .drop_nulls() .unique(subset="uid") .group_by("Département") .len("uid") @@ -59,34 +59,56 @@ def get_barchart_sources(lf: pl.LazyFrame, type_date: str): "datePublicationDonnees": "publication des données", } - lf = lf.select("uid", type_date, "source") + lf = lf.select("uid", type_date, "sourceDataset") + lf = lf.unique("uid") + + # Rassemblement des datasets Atexo pour ne pas surcharger le graphique + lf = lf.with_columns( + pl.when(pl.col("sourceDataset").str.starts_with("atexo")) + .then(pl.lit("plateformes atexo")) + .otherwise(pl.col("sourceDataset")) + .alias("sourceDataset") + ) + + # Rassemblement des datasets AWS pour ne pas surcharger le graphique + lf = lf.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( - pl.col(type_date).is_not_null() & pl.col("annee").is_between(2018, 2025) + pl.col(type_date).is_not_null() & pl.col("annee").is_between(2019, 2025) ) - lf = lf.sort(by=[type_date, "source"], descending=False) lf = lf.with_columns(pl.col(type_date).cast(pl.String).str.head(7)) - - lf = lf.group_by([type_date, "source"]).len() - lf = lf.with_columns( - pl.when(pl.col("source").is_null()).then( - pl.lit("Source inconnue").alias("source") - ) + lf = ( + lf.group_by([type_date, "sourceDataset"]) + .len() + .sort(by=[type_date, "len"], descending=True) ) - lf = lf.sort(by=[type_date, "source"], descending=False) + + # lf = lf.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() fig = px.bar( df, x=type_date, y="len", - color="source", + color="sourceDataset", title=f"Nombre de marchés attribués par date de {labels[type_date]} et source de données", labels={ "len": "Nombre de marchés", type_date: f"Mois de {labels[type_date]}", - "source": "Source de données", + "sourceDataset": "Source de données", }, ) + return fig From 5bd519f9e59449ea2feaaca4a2dc61977204d5b4 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sat, 20 Sep 2025 07:59:47 +0200 Subject: [PATCH 04/24] app.compress = True #31 --- src/app.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app.py b/src/app.py index c30949f..07583ab 100644 --- a/src/app.py +++ b/src/app.py @@ -3,7 +3,12 @@ import logging import dash_bootstrap_components as dbc from dash import Dash, dcc, html, page_container, page_registry -app = Dash(external_stylesheets=[dbc.themes.SIMPLEX], title="decp.info", use_pages=True) +app = Dash( + external_stylesheets=[dbc.themes.SIMPLEX], + title="decp.info", + use_pages=True, + compress=True, +) # COSMO (belle font, blue), # UNITED (rouge, ubuntu font), # LUMEN (gros séparateur, blue clair), From 56b7a8a0a1d2422ba5d59d6acb975b545a2435a5 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sat, 20 Sep 2025 08:13:45 +0200 Subject: [PATCH 05/24] robots.txt #31 --- src/app.py | 8 ++++++++ src/assets/robots.txt | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/assets/robots.txt diff --git a/src/app.py b/src/app.py index 07583ab..e45d9ef 100644 --- a/src/app.py +++ b/src/app.py @@ -2,6 +2,7 @@ import logging import dash_bootstrap_components as dbc from dash import Dash, dcc, html, page_container, page_registry +from flask import send_from_directory app = Dash( external_stylesheets=[dbc.themes.SIMPLEX], @@ -14,6 +15,13 @@ app = Dash( # LUMEN (gros séparateur, blue clair), # SIMPLEX (rouge, séparateur) + +# robots.txt +@app.server.route("/robots.txt") +def robots(): + return send_from_directory("./assets", "robots.txt", mimetype="text/plain") + + logger = logging.getLogger("decp.info") logging.basicConfig( format="%(asctime)s %(levelname)-8s %(message)s", diff --git a/src/assets/robots.txt b/src/assets/robots.txt new file mode 100644 index 0000000..bbb5aa1 --- /dev/null +++ b/src/assets/robots.txt @@ -0,0 +1,43 @@ +# START YOAST BLOCK +# Copié depuis https://next.ink/robots.txt +# --------------------------- +User-agent: * + +Sitemap: https://next.ink/sitemap_index.xml +Allow: / + +# --------------------------- +# END YOAST BLOCK + +User-agent: GPTBot +Disallow: / +User-agent: ChatGPT-User +Disallow: / +User-agent: Google-Extended +Disallow: / +User-agent: PerplexityBot +Disallow: / +User-agent: Amazonbot +Disallow: / +User-agent: ClaudeBot +Disallow: / +User-Agent: FacebookBot +Disallow: / +User-Agent: Applebot +Disallow: / +User-agent: anthropic-ai +Disallow: / +User-agent: Bytespider +Disallow: / +User-agent: Claude-Web +Disallow: / +User-agent: Diffbot +Disallow: / +User-agent: ImagesiftBot +Disallow: / +User-agent: Omgilibot +Disallow: / +User-agent: Omgili +Disallow: / +User-agent: YouBot +Disallow: / From 6a5b4aa2aac447f31ccfe76144e09cab83b813f1 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sat, 20 Sep 2025 08:23:05 +0200 Subject: [PATCH 06/24] Text compression ok (zstd) #31 --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 8f49bae..021fa6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ authors = [ ] dependencies = [ "dash==3.2.0", + "dash[compress]", "polars", "gunicorn", "dash-bootstrap-components", From e9db143f454a418aebbbfe625e3f117a745f9c74 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sat, 20 Sep 2025 08:25:51 +0200 Subject: [PATCH 07/24] Coquille robots #31 --- src/assets/robots.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/assets/robots.txt b/src/assets/robots.txt index bbb5aa1..9381e66 100644 --- a/src/assets/robots.txt +++ b/src/assets/robots.txt @@ -2,8 +2,6 @@ # Copié depuis https://next.ink/robots.txt # --------------------------- User-agent: * - -Sitemap: https://next.ink/sitemap_index.xml Allow: / # --------------------------- From 90af1f6eb4fc6bde12e01ade2b4b2f150ce7fa12 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sun, 21 Sep 2025 11:13:48 +0200 Subject: [PATCH 08/24] GPL v3 --- LICENSE | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..285710b --- /dev/null +++ b/LICENSE @@ -0,0 +1,16 @@ +decp.info is a web application that enable analysis and download of +French public procurement data. +Copyright (C) 2025 Colin Maudry + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . From 89a419a8d2daf89e3d42b59a4a6af3525df12fa2 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sun, 21 Sep 2025 11:42:29 +0200 Subject: [PATCH 09/24] =?UTF-8?q?Ajout=20des=20d=C3=A9tails=20sur=20les=20?= =?UTF-8?q?sources=20de=20donn=C3=A9es=20#32?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .template.env | 1 + src/figures.py | 45 +++++++++++++++++++++++++++++++++++++++ src/pages/about.py | 14 ++++++++++++ src/pages/statistiques.py | 7 ------ 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/.template.env b/.template.env index 5cbcdfa..c47e456 100644 --- a/.template.env +++ b/.template.env @@ -1,3 +1,4 @@ DATA_FILE_PARQUET_PATH=https://www.data.gouv.fr/fr/datasets/r/11cea8e8-df3e-4ed1-932b-781e2635e432 PORT=8050 DEVELOPMENT=True +SOURCE_STATS_CSV_PATH="https://www.data.gouv.fr/api/1/datasets/r/8ded94de-3b80-4840-a5bb-7faad1c9c234" diff --git a/src/figures.py b/src/figures.py index 9d93c0c..8ca0c5d 100644 --- a/src/figures.py +++ b/src/figures.py @@ -2,6 +2,7 @@ import json import plotly.express as px import polars as pl +from dash import dash_table, html def get_map_count_marches(lf: pl.LazyFrame): @@ -112,3 +113,47 @@ def get_barchart_sources(lf: pl.LazyFrame, type_date: str): ) return fig + + +def get_sources_tables(source_path) -> html.Div: + df = pl.read_csv(source_path) + df = df.with_columns( + ( + pl.lit('') + + pl.col("nom") + + pl.lit("") + ).alias("nom") + ) + df = df.drop("url") + df = df.sort(by=["nb_marchés"], descending=True) + + datatable = dash_table.DataTable( + id="source_table", + columns=[ + { + "name": i, + "id": i, + "presentation": "markdown", + "type": "text", + "format": {"nully": "N/A"}, + } + for i in df.schema.names() + ], + style_cell_conditional=[ + { + "if": {"column_id": ["nom", "organisation"]}, + "minWidth": "350px", + "textAlign": "left", + "overflow": "hidden", + "lineHeight": "14px", + "whiteSpace": "normal", + }, + ], + sort_action="native", + markdown_options={"html": True}, + ) + datatable.data = df.to_dicts() + + return html.Div(children=datatable) diff --git a/src/pages/about.py b/src/pages/about.py index 1aeb1cb..db8065f 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -1,5 +1,9 @@ +import os + from dash import dcc, html, register_page +from src.figures import get_sources_tables + title = "À propos" register_page( @@ -43,6 +47,16 @@ les fonctionnalités actuelles de decp.info. Il est ainsi possible de rajouter - LinkedIn : [colinmaudry](https://www.linkedin.com/in/colinmaudry/) """ ), + html.H4("Sources de données", id="sources"), + get_sources_tables(os.getenv("SOURCE_STATS_CSV_PATH")), + html.H4("Mentions légales", id="mentions-legales"), + dcc.Markdown(""" + Site Web développé et édité par [SAS Colmo](https://annuaire-entreprises.data.gouv.fr/entreprise/colmo-989393350), 989 393 350 RCS Rennes au capital de 3 000 euros. + + Siège social : 1 carrefour Jouaust, 35000 Rennes + + Hébergement : serveur situé en France et administré par Scaleway, 8 rue de la Ville l’Evêque, 75008 Paris + """), ], ) ] diff --git a/src/pages/statistiques.py b/src/pages/statistiques.py index d692ad6..3196b0f 100644 --- a/src/pages/statistiques.py +++ b/src/pages/statistiques.py @@ -15,13 +15,6 @@ layout = [ className="container", children=[ html.H2(title), - dcc.Markdown(""" - À savoir, les données suivantes existent mais sont en cours d'intégration dans ce projet : - - - les données publiées dans le [format DECP 2019 (période 2018-2022)](https://www.data.gouv.fr/fr/datasets/donnees-essentielles-de-la-commande-publique-fichiers-consolides/#/resources/16962018-5c31-4296-9454-5998585496d2) - - les données [collectées par l'AIFE](https://github.com/ColinMaudry/decp-processing/issues/68) (API DUME, notamment achatpublic.info) - - les données [des plateformes Atexo](https://github.com/ColinMaudry/decp-processing/issues/57) - """), dcc.Loading( overlay_style={"visibility": "visible", "filter": "blur(2px)"}, id="loading-1", From 5a9b585b44ca2669d9b6afc2278e745844a1cc7a Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sun, 21 Sep 2025 11:43:07 +0200 Subject: [PATCH 10/24] =?UTF-8?q?Ajout=20des=20d=C3=A9tails=20sur=20les=20?= =?UTF-8?q?sources=20de=20donn=C3=A9es=20#32?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/about.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/about.py b/src/pages/about.py index db8065f..a0dfd88 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -1,11 +1,14 @@ import os from dash import dcc, html, register_page +from dotenv import load_dotenv from src.figures import get_sources_tables title = "À propos" +load_dotenv() + register_page( __name__, path="/a-propos", title=f"decp.info - {title}", name=title, order=5 ) From f56f5bfc94a383551d8b162bff98f094b249d18f Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sun, 21 Sep 2025 11:43:21 +0200 Subject: [PATCH 11/24] =?UTF-8?q?Ajout=20des=20d=C3=A9tails=20sur=20les=20?= =?UTF-8?q?sources=20de=20donn=C3=A9es=20#32?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/style.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/assets/style.css b/src/assets/style.css index a77995f..2532b92 100644 --- a/src/assets/style.css +++ b/src/assets/style.css @@ -10,6 +10,14 @@ float: left; } +#source_table p { + line-height: 1.5; +} + +#source_table { + margin-bottom: 25px; +} + /* Réduire la taille du texte de la colonne Objet */ td[data-dash-column="objet"] { From f3305d42cd6d1d72518e4ecb667aa4b150847b96 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sun, 21 Sep 2025 11:43:55 +0200 Subject: [PATCH 12/24] =?UTF-8?q?acheteur=5Fnom=20plus=20=C3=A9troit,=20co?= =?UTF-8?q?mment=20contribuer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/about.py | 22 ++++++++++++++-------- src/pages/home.py | 12 +++++++++++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/pages/about.py b/src/pages/about.py index a0dfd88..4b19822 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -19,7 +19,7 @@ layout = [ children=[ html.H2(title), dcc.Markdown( - """Outil d'exploration des [Données Essentielles de la Commande Publique](), développé par Colin Maudry, sous licence GPL v3 (libre et gratuit). + """Outil d'exploration libre et gratuit des [Données Essentielles de la Commande Publique](), développé par Colin Maudry. Ce projet vise à démocratiser l'accès aux données des marchés publics et à un outil performant et gratuit. Si vous le trouvez utile j'aimerais beaucoup échanger avec vous pour comprendre vos cas d'usages et vos besoins. Cet outil ne peut rester performant que si je comprends les problèmes qu'il peut aider à résoudre. Ce projet ne peut rester gratuit que grâce au financement du développement de nouvelles fonctionnalités. @@ -33,23 +33,29 @@ les fonctionnalités actuelles de decp.info. Il est ainsi possible de rajouter - le développement d'une API pour alimenter d'autres logiciels - ...et toutes les fonctionnalités auxquelles vous pourrez penser :) -#### Pour aller plus loin +#### Pour contribuer + +- via l'achat d'un prestation de service (devis, prestation, facture), vous pouvez financer le développement de [fonctionnalités prévues](https://github.com/ColinMaudry/decp.info/issues), ou d'autres ! +- ma société accepte aussi les dons (pas de réduction d'impôt possible) +- envoyez un mail et on discute ! + +#### Pour explorer le projet - ✉️ [inscription à la liste de diffusion](https://6254d9a3.sibforms.com/serve/MUIFAEonUVkoSVrdgey18CTgLyI16xw4yeu-M-YOUzhWE_AgfQfbgkyT7GvA_RYLro9MfuRqkzQxSvu7-uzbMSv2a2ZQPsliM7wtiiqIL8kR2zOvl6m11fb5qjcOxMAYsLiY_YBi3P7NY95CTJ8vRY4CpsDclF2iLooOElKkTgIgi5nePe7zAIrgiYM5v2EuALlGJZMEG9vBP-Cu) (annonces des mises à jour et évènements, maximum une fois par mois) - 📔 [wiki du projet](https://github.com/ColinMaudry/decp-processing/wiki) - 🚰 code source - [de decp.info](https://github.com/ColinMaudry/decp.info) - [du traitement des données](https://github.com/ColinMaudry/decp-processing) - -#### Contact - -- venez discuter de la transparence de la commande publique [sur le forum teamopendata.org](https://teamopendata.org/c/commande-publique/101) + """ + ), + html.H4("Contact", id="contact"), + dcc.Markdown(""" - Email : [colin+decp@maudry.com](mailto:colin+decp@maudry.com) +- venez discuter de la transparence de la commande publique [sur le forum teamopendata.org](https://teamopendata.org/c/commande-publique/101) - Bluesky : [@col1m.bsky.social](https://bsky.app/profile/col1m.bsky.social) - Mastodon : [col1m@mamot.fr](https://mamot.fr/@col1m) - LinkedIn : [colinmaudry](https://www.linkedin.com/in/colinmaudry/) -""" - ), +"""), html.H4("Sources de données", id="sources"), get_sources_tables(os.getenv("SOURCE_STATS_CSV_PATH")), html.H4("Mentions légales", id="mentions-legales"), diff --git a/src/pages/home.py b/src/pages/home.py index cdf2c7f..d7c3e44 100644 --- a/src/pages/home.py +++ b/src/pages/home.py @@ -83,6 +83,14 @@ datatable = dash_table.DataTable( "lineHeight": "14px", "whiteSpace": "normal", }, + { + "if": {"column_id": "acheteur_nom"}, + "minWidth": "250px", + "textAlign": "left", + "overflow": "hidden", + "lineHeight": "14px", + "whiteSpace": "normal", + }, ], data_timestamp=0, markdown_options={"html": True}, @@ -92,7 +100,9 @@ layout = [ html.Div( html.Details( children=[ - html.Summary(html.H3("Mode d'emploi")), + html.Summary( + html.H3("Mode d'emploi", style={"text-decoration": "underline"}), + ), dcc.Markdown( """ From 3396986b2dce455a167730e6762e217f914b7060 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Sun, 21 Sep 2025 12:16:06 +0200 Subject: [PATCH 13/24] =?UTF-8?q?Explication=20sur=20les=20sources=20de=20?= =?UTF-8?q?donn=C3=A9es=20et=20l'exhaustivit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/about.py | 9 ++++---- src/pages/statistiques.py | 45 ++++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/pages/about.py b/src/pages/about.py index 4b19822..2cd1761 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -33,8 +33,10 @@ les fonctionnalités actuelles de decp.info. Il est ainsi possible de rajouter - le développement d'une API pour alimenter d'autres logiciels - ...et toutes les fonctionnalités auxquelles vous pourrez penser :) -#### Pour contribuer - + """ + ), + html.H4("Pour contribuer", id="contribuer"), + dcc.Markdown(""" - via l'achat d'un prestation de service (devis, prestation, facture), vous pouvez financer le développement de [fonctionnalités prévues](https://github.com/ColinMaudry/decp.info/issues), ou d'autres ! - ma société accepte aussi les dons (pas de réduction d'impôt possible) - envoyez un mail et on discute ! @@ -46,8 +48,7 @@ les fonctionnalités actuelles de decp.info. Il est ainsi possible de rajouter - 🚰 code source - [de decp.info](https://github.com/ColinMaudry/decp.info) - [du traitement des données](https://github.com/ColinMaudry/decp-processing) - """ - ), + """), html.H4("Contact", id="contact"), dcc.Markdown(""" - Email : [colin+decp@maudry.com](mailto:colin+decp@maudry.com) diff --git a/src/pages/statistiques.py b/src/pages/statistiques.py index 3196b0f..877457d 100644 --- a/src/pages/statistiques.py +++ b/src/pages/statistiques.py @@ -17,34 +17,31 @@ layout = [ html.H2(title), dcc.Loading( overlay_style={"visibility": "visible", "filter": "blur(2px)"}, - id="loading-1", + id="loading-statistques", type="default", children=[ html.Div( children=[ - dcc.Loading( - overlay_style={ - "visibility": "visible", - "filter": "blur(2px)", - }, - id="loading-stats", - type="default", - children=[ - dcc.Graph(figure=get_map_count_marches(lf)), - dcc.Graph( - figure=get_barchart_sources( - lf, "dateNotification" - ) - ), - dcc.Graph( - figure=get_barchart_sources( - lf, "datePublicationDonnees" - ) - ), - ], - ) - ] - ), + dcc.Markdown(""" + La publication de données essentielles de marchés publics (DECP) est souvent effectuée par + les plateformes de marchés publics (profils d'acheteurs). Cependant, certaines plateformes ne publient pas, + ou publient d'une manière qui rend la récupération des données compliquée. Les données présentées sur ce site + ne représentent donc pas tous les marchés attribués en France, seulement une partie significative. + + L'ajout de nouvelles plateformes [est en cours](https://github.com/ColinMaudry/decp-processing/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22source%20de%20donn%C3%A9es%22), + toutes les [contributions](/a-propos#contribuer) sont les bienvenues pour atteindre l'exhaustivité. + """), + dcc.Graph(figure=get_map_count_marches(lf)), + dcc.Graph( + figure=get_barchart_sources(lf, "dateNotification") + ), + dcc.Graph( + figure=get_barchart_sources( + lf, "datePublicationDonnees" + ) + ), + ], + ) ], ), ], From 000e4e89db05307772f17dd45039c8945f342386 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Sep 2025 08:13:46 +0200 Subject: [PATCH 14/24] =?UTF-8?q?Liens=20vers=20les=20donn=C3=A9es=20et=20?= =?UTF-8?q?le=20blog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/about.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/pages/about.py b/src/pages/about.py index 2cd1761..2ce3a51 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -28,11 +28,10 @@ En effet, le potentiel des données d'attribution de marchés et des données qu les fonctionnalités actuelles de decp.info. Il est ainsi possible de rajouter - de nombreuses visualisations de données (cartes, graphiques, tableaux) sur des thématiques variées (vivacité de la concurrence, secteurs d'activité, insertion par l'activité économique (IAE), distance acheteur-fournisseur...) -- la sauvegarde de filtre pour les retrouver plus tard et les partager +- la sauvegarde de filtres pour les retrouver plus tard et les partager - des alertes par email si des marchés correspondant à certains critères - le développement d'une API pour alimenter d'autres logiciels -- ...et toutes les fonctionnalités auxquelles vous pourrez penser :) - +- ...et toutes les fonctionnalités auxquelles vous pourrez penser """ ), html.H4("Pour contribuer", id="contribuer"), @@ -44,6 +43,8 @@ les fonctionnalités actuelles de decp.info. Il est ainsi possible de rajouter #### Pour explorer le projet - ✉️ [inscription à la liste de diffusion](https://6254d9a3.sibforms.com/serve/MUIFAEonUVkoSVrdgey18CTgLyI16xw4yeu-M-YOUzhWE_AgfQfbgkyT7GvA_RYLro9MfuRqkzQxSvu7-uzbMSv2a2ZQPsliM7wtiiqIL8kR2zOvl6m11fb5qjcOxMAYsLiY_YBi3P7NY95CTJ8vRY4CpsDclF2iLooOElKkTgIgi5nePe7zAIrgiYM5v2EuALlGJZMEG9vBP-Cu) (annonces des mises à jour et évènements, maximum une fois par mois) +- 💾 [données consolidées en Open Data](https://www.data.gouv.fr/datasets/donnees-essentielles-de-la-commande-publique-consolidees-format-tabulaire/) +- 🗞️ [mon blog](https://colin.maudry.com), qui parle beaucoup de transparence des marchés publics - 📔 [wiki du projet](https://github.com/ColinMaudry/decp-processing/wiki) - 🚰 code source - [de decp.info](https://github.com/ColinMaudry/decp.info) @@ -52,20 +53,20 @@ les fonctionnalités actuelles de decp.info. Il est ainsi possible de rajouter html.H4("Contact", id="contact"), dcc.Markdown(""" - Email : [colin+decp@maudry.com](mailto:colin+decp@maudry.com) -- venez discuter de la transparence de la commande publique [sur le forum teamopendata.org](https://teamopendata.org/c/commande-publique/101) - Bluesky : [@col1m.bsky.social](https://bsky.app/profile/col1m.bsky.social) - Mastodon : [col1m@mamot.fr](https://mamot.fr/@col1m) - LinkedIn : [colinmaudry](https://www.linkedin.com/in/colinmaudry/) +- venez discuter de la transparence de la commande publique [sur le forum teamopendata.org](https://teamopendata.org/c/commande-publique/101) """), html.H4("Sources de données", id="sources"), get_sources_tables(os.getenv("SOURCE_STATS_CSV_PATH")), html.H4("Mentions légales", id="mentions-legales"), dcc.Markdown(""" - Site Web développé et édité par [SAS Colmo](https://annuaire-entreprises.data.gouv.fr/entreprise/colmo-989393350), 989 393 350 RCS Rennes au capital de 3 000 euros. +Site Web développé et édité par [SAS Colmo](https://annuaire-entreprises.data.gouv.fr/entreprise/colmo-989393350), 989 393 350 RCS Rennes au capital de 3 000 euros. - Siège social : 1 carrefour Jouaust, 35000 Rennes +Siège social : 1 carrefour Jouaust, 35000 Rennes - Hébergement : serveur situé en France et administré par Scaleway, 8 rue de la Ville l’Evêque, 75008 Paris +Hébergement : serveur situé en France et administré par Scaleway, 8 rue de la Ville l’Evêque, 75008 Paris """), ], ) From 36ea24278f1b72498f90e069cb1745b15fa4429f Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Sep 2025 08:49:08 +0200 Subject: [PATCH 15/24] Suivi d'audience --- src/pages/about.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/pages/about.py b/src/pages/about.py index 2ce3a51..c23422e 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -62,12 +62,40 @@ les fonctionnalités actuelles de decp.info. Il est ainsi possible de rajouter get_sources_tables(os.getenv("SOURCE_STATS_CSV_PATH")), html.H4("Mentions légales", id="mentions-legales"), dcc.Markdown(""" +##### Publication + Site Web développé et édité par [SAS Colmo](https://annuaire-entreprises.data.gouv.fr/entreprise/colmo-989393350), 989 393 350 RCS Rennes au capital de 3 000 euros. Siège social : 1 carrefour Jouaust, 35000 Rennes Hébergement : serveur situé en France et administré par Scaleway, 8 rue de la Ville l’Evêque, 75008 Paris - """), + +##### Suivi d'audience + +Ce site dépose un petit fichier texte (un « cookie ») sur votre ordinateur lorsque vous le consultez ([Wikipédia](https://fr.wikipedia.org/wiki/Cookie_(informatique))). Cela me permet de mesurer le nombre de visites, de distinguer les nouveaux visiteurs des utilisateurs réguliers et ainsi de communiquer sur l'impact de decp.info. + +**Ce site n’affiche pas de bannière de consentement aux cookies, pourquoi ?** + +C’est vrai, vous n’avez pas eu à cliquer sur un bloc qui recouvre la moitié de la page pour dire que vous êtes d’accord avec le dépôt de cookies. + +Rien d’exceptionnel, je respecte simplement la loi, qui dit que certains outils de suivi d’audience, correctement configurés pour respecter la vie privée, sont exemptés d’autorisation préalable. + +J’utilise pour cela [Matomo](https://matomo.org/), un outil [libre](https://matomo.org/free-software/), paramétré pour être en conformité avec [la recommandation « Cookies »](https://www.cnil.fr/fr/solutions-pour-les-cookies-de-mesure-daudience) de la CNIL. Cela signifie que votre adresse IP, par exemple, est anonymisée avant d’être enregistrée. Il m’est donc impossible d’associer vos visites sur ce site à votre personne. + +Matomo propose cependant ce formulaire si vous souhaitez totalement désactiver le suivi de vos sessions sur ce site :"""), + html.Div( + id="matomo-opt-out", + style={ + "border": "1pt solid lightgrey", + "padding": "12px", + "margin": "auto 0 auto 12px", + "width": "80%", + }, + children=["Vous utilisez un bloqueur de suivi de trafic."], + ), + html.Script( + src="https://analytics.maudry.com/index.php?module=CoreAdminHome&action=optOutJS&divId=matomo-opt-out&language=auto&showIntro=1" + ), ], ) ] From 48da8cde5cd39e3dd474558295a9a33011f6ff27 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Sep 2025 08:57:49 +0200 Subject: [PATCH 16/24] Comment out le formulaire pour l'instant, script ne s'execute pas --- src/pages/about.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/pages/about.py b/src/pages/about.py index c23422e..ff564f9 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -80,22 +80,21 @@ C’est vrai, vous n’avez pas eu à cliquer sur un bloc qui recouvre la moiti Rien d’exceptionnel, je respecte simplement la loi, qui dit que certains outils de suivi d’audience, correctement configurés pour respecter la vie privée, sont exemptés d’autorisation préalable. -J’utilise pour cela [Matomo](https://matomo.org/), un outil [libre](https://matomo.org/free-software/), paramétré pour être en conformité avec [la recommandation « Cookies »](https://www.cnil.fr/fr/solutions-pour-les-cookies-de-mesure-daudience) de la CNIL. Cela signifie que votre adresse IP, par exemple, est anonymisée avant d’être enregistrée. Il m’est donc impossible d’associer vos visites sur ce site à votre personne. - -Matomo propose cependant ce formulaire si vous souhaitez totalement désactiver le suivi de vos sessions sur ce site :"""), - html.Div( - id="matomo-opt-out", - style={ - "border": "1pt solid lightgrey", - "padding": "12px", - "margin": "auto 0 auto 12px", - "width": "80%", - }, - children=["Vous utilisez un bloqueur de suivi de trafic."], - ), - html.Script( - src="https://analytics.maudry.com/index.php?module=CoreAdminHome&action=optOutJS&divId=matomo-opt-out&language=auto&showIntro=1" - ), +J’utilise pour cela [Matomo](https://matomo.org/), un outil [libre](https://matomo.org/free-software/), paramétré pour être en conformité avec [la recommandation « Cookies »](https://www.cnil.fr/fr/solutions-pour-les-cookies-de-mesure-daudience) de la CNIL. Cela signifie que votre adresse IP, par exemple, est anonymisée avant d’être enregistrée. Il m’est donc impossible d’associer vos visites sur ce site à votre personne."""), + # Matomo propose cependant ce formulaire si vous souhaitez totalement désactiver le suivi de vos sessions sur ce site :"""), + # html.Div( + # id="matomo-opt-out", + # style={ + # "border": "1pt solid lightgrey", + # "padding": "12px", + # "margin": "auto 0 auto 12px", + # "width": "80%", + # }, + # children=["Vous utilisez un bloqueur de suivi de trafic."], + # ), + # html.Script( + # src="https://analytics.maudry.com/index.php?module=CoreAdminHome&action=optOutJS&divId=matomo-opt-out&language=auto&showIntro=1" + # ), ], ) ] From 73a10e20a6a79f131f926eb688e21add004e6e2e Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Sep 2025 09:10:01 +0200 Subject: [PATCH 17/24] README --- README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ba10c99..801a712 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # decp.info +> v2.0 + Outil d'exploration et de téléchargement des données essentielles de la commande publique. => [decp.info](https://decp.info) @@ -10,15 +12,27 @@ Outil d'exploration et de téléchargement des données essentielles de la comma python -m venv .venv source .venv/bin/activate pip install . + +# Pour la production gunicorn app:server + +# Pour avoir le debuggage et le hot reload +python run.py ``` -## Dépôts de code connexes + +## Liens connexes - [decp-processing](https://github.com/ColinMaudry/decp-processing) (traitement et publication des données) -- [decp-table-schema](https://github.com/ColinMaudry/decp-table-schema) (schéma de données tabulaire) +- [colin.maudry.com](https://colin.maudry.com) (blog) ## Notes de version +### 2.0.0 (23 septembre 2025) + +- détails des sources de données +- section "À propos" plus développée +- correction de bugs dans les filtres de la data table + ### 2.0.0-alpha - Data table fonctionnelle From 8c3642e5d61bc9119457a5e4b694e70e7d52b946 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Sep 2025 10:28:31 +0200 Subject: [PATCH 18/24] Typo --- README.md | 8 +++++++- src/pages/about.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 801a712..69733e6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # decp.info -> v2.0 +> v2.0.1 Outil d'exploration et de téléchargement des données essentielles de la commande publique. @@ -13,6 +13,10 @@ python -m venv .venv source .venv/bin/activate pip install . +# Copie et personnalisation du .env +cp template.env .env +nano .env + # Pour la production gunicorn app:server @@ -27,6 +31,8 @@ python run.py ## Notes de version +### 2.0.1 (23 septembre 2025) + ### 2.0.0 (23 septembre 2025) - détails des sources de données diff --git a/src/pages/about.py b/src/pages/about.py index ff564f9..72fe357 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -36,7 +36,7 @@ les fonctionnalités actuelles de decp.info. Il est ainsi possible de rajouter ), html.H4("Pour contribuer", id="contribuer"), dcc.Markdown(""" -- via l'achat d'un prestation de service (devis, prestation, facture), vous pouvez financer le développement de [fonctionnalités prévues](https://github.com/ColinMaudry/decp.info/issues), ou d'autres ! +- via l'achat d'une prestation de service (devis, prestation, facture), vous pouvez financer le développement de [fonctionnalités prévues](https://github.com/ColinMaudry/decp.info/issues), ou d'autres ! - ma société accepte aussi les dons (pas de réduction d'impôt possible) - envoyez un mail et on discute ! From 159d409237c800ae219b396a4e206dfcff08f714 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Sep 2025 12:27:57 +0200 Subject: [PATCH 19/24] =?UTF-8?q?Bloquage=20du=20bouton=20de=20t=C3=A9l?= =?UTF-8?q?=C3=A9chargement=20si=20trop=20de=20lignes=20#38?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++-- src/pages/home.py | 31 ++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 69733e6..41f2c26 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,11 @@ python run.py ## Notes de version -### 2.0.1 (23 septembre 2025) +#### 2.0.1 (23 septembre 2025) + +- Bloquage du bouton de téléchargement si trop de lignes (+ 65000) [#38](https//github.com/ColinMaudry/decp.info/issues/38) +- Meilleures instructions d'installation et lancement +- Coquilles 🐚 ### 2.0.0 (23 septembre 2025) @@ -39,7 +43,7 @@ python run.py - section "À propos" plus développée - correction de bugs dans les filtres de la data table -### 2.0.0-alpha +#### 2.0.0-alpha - Data table fonctionnelle diff --git a/src/pages/home.py b/src/pages/home.py index d7c3e44..6ab1aa0 100644 --- a/src/pages/home.py +++ b/src/pages/home.py @@ -141,7 +141,11 @@ layout = [ html.Div( [ html.P("lignes", id="nb_rows"), - html.Button("Télécharger au format Excel", id="btn-download-data"), + html.Button( + "Téléchargement désactivé au-delà de 65 000 lignes", + id="btn-download-data", + disabled=True, + ), dcc.Download(id="download-data"), html.P("Données mises à jour le " + str(update_date)), ], @@ -157,6 +161,9 @@ layout = [ Output("table", "data"), Output("table", "data_timestamp"), Output("nb_rows", "children"), + Output("btn-download-data", "disabled"), + Output("btn-download-data", "children"), + Output("btn-download-data", "title"), Input("table", "page_current"), Input("table", "page_size"), Input("table", "filter_query"), @@ -215,7 +222,9 @@ def update_table(page_current, page_size, filter_query, sort_by, data_timestamp) df_filtered = dff.clone() - nb_rows = f"{format_number(dff.height)} lignes" + height = dff.height + + nb_rows = f"{format_number(height)} lignes" # Pagination des données start_row = page_current * page_size @@ -223,7 +232,23 @@ def update_table(page_current, page_size, filter_query, sort_by, data_timestamp) dff = dff.slice(start_row, page_size) dicts = dff.to_dicts() - return dicts, data_timestamp + 1, nb_rows + if height > 65000: + download_disabled = True + download_text = "Téléchargement désactivé au-delà de 65 000 lignes" + download_title = "Excel ne supporte pas d'avoir plus de 65 000 URLs dans une même feuille de calcul. Contactez-moi pour me présenter votre besoin en téléchargement afin je puisse adapter la solution." + else: + download_disabled = False + download_text = "Télécharger au format Excel" + download_title = "" + + return ( + dicts, + data_timestamp + 1, + nb_rows, + download_disabled, + download_text, + download_title, + ) @callback( From 0fea49109aed9e67858c0ce2d952036f29e8f525 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Sep 2025 12:27:57 +0200 Subject: [PATCH 20/24] =?UTF-8?q?Bloquage=20du=20bouton=20de=20t=C3=A9l?= =?UTF-8?q?=C3=A9chargement=20si=20trop=20de=20lignes=20#38?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++-- src/pages/about.py | 2 +- src/pages/home.py | 31 ++++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 69733e6..41f2c26 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,11 @@ python run.py ## Notes de version -### 2.0.1 (23 septembre 2025) +#### 2.0.1 (23 septembre 2025) + +- Bloquage du bouton de téléchargement si trop de lignes (+ 65000) [#38](https//github.com/ColinMaudry/decp.info/issues/38) +- Meilleures instructions d'installation et lancement +- Coquilles 🐚 ### 2.0.0 (23 septembre 2025) @@ -39,7 +43,7 @@ python run.py - section "À propos" plus développée - correction de bugs dans les filtres de la data table -### 2.0.0-alpha +#### 2.0.0-alpha - Data table fonctionnelle diff --git a/src/pages/about.py b/src/pages/about.py index 72fe357..bdae8df 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -19,7 +19,7 @@ layout = [ children=[ html.H2(title), dcc.Markdown( - """Outil d'exploration libre et gratuit des [Données Essentielles de la Commande Publique](), développé par Colin Maudry. + """Outil d'exploration libre et gratuit des données de marchés publics, développé par Colin Maudry. Ce projet vise à démocratiser l'accès aux données des marchés publics et à un outil performant et gratuit. Si vous le trouvez utile j'aimerais beaucoup échanger avec vous pour comprendre vos cas d'usages et vos besoins. Cet outil ne peut rester performant que si je comprends les problèmes qu'il peut aider à résoudre. Ce projet ne peut rester gratuit que grâce au financement du développement de nouvelles fonctionnalités. diff --git a/src/pages/home.py b/src/pages/home.py index d7c3e44..6ab1aa0 100644 --- a/src/pages/home.py +++ b/src/pages/home.py @@ -141,7 +141,11 @@ layout = [ html.Div( [ html.P("lignes", id="nb_rows"), - html.Button("Télécharger au format Excel", id="btn-download-data"), + html.Button( + "Téléchargement désactivé au-delà de 65 000 lignes", + id="btn-download-data", + disabled=True, + ), dcc.Download(id="download-data"), html.P("Données mises à jour le " + str(update_date)), ], @@ -157,6 +161,9 @@ layout = [ Output("table", "data"), Output("table", "data_timestamp"), Output("nb_rows", "children"), + Output("btn-download-data", "disabled"), + Output("btn-download-data", "children"), + Output("btn-download-data", "title"), Input("table", "page_current"), Input("table", "page_size"), Input("table", "filter_query"), @@ -215,7 +222,9 @@ def update_table(page_current, page_size, filter_query, sort_by, data_timestamp) df_filtered = dff.clone() - nb_rows = f"{format_number(dff.height)} lignes" + height = dff.height + + nb_rows = f"{format_number(height)} lignes" # Pagination des données start_row = page_current * page_size @@ -223,7 +232,23 @@ def update_table(page_current, page_size, filter_query, sort_by, data_timestamp) dff = dff.slice(start_row, page_size) dicts = dff.to_dicts() - return dicts, data_timestamp + 1, nb_rows + if height > 65000: + download_disabled = True + download_text = "Téléchargement désactivé au-delà de 65 000 lignes" + download_title = "Excel ne supporte pas d'avoir plus de 65 000 URLs dans une même feuille de calcul. Contactez-moi pour me présenter votre besoin en téléchargement afin je puisse adapter la solution." + else: + download_disabled = False + download_text = "Télécharger au format Excel" + download_title = "" + + return ( + dicts, + data_timestamp + 1, + nb_rows, + download_disabled, + download_text, + download_title, + ) @callback( From dd229a212fa96b2590c0fe6883b7aedd14c33961 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Sep 2025 12:27:57 +0200 Subject: [PATCH 21/24] =?UTF-8?q?Bloquage=20du=20bouton=20de=20t=C3=A9l?= =?UTF-8?q?=C3=A9chargement=20si=20trop=20de=20lignes=20#38?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++++-- src/pages/about.py | 2 +- src/pages/home.py | 31 ++++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 69733e6..5c35fdc 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,11 @@ python run.py ## Notes de version -### 2.0.1 (23 septembre 2025) +#### 2.0.1 (23 septembre 2025) + +- Bloquage du bouton de téléchargement si trop de lignes (+ 65000) [#38](https://github.com/ColinMaudry/decp.info/issues/38) +- Meilleures instructions d'installation et lancement +- Coquilles 🐚 ### 2.0.0 (23 septembre 2025) @@ -39,7 +43,7 @@ python run.py - section "À propos" plus développée - correction de bugs dans les filtres de la data table -### 2.0.0-alpha +#### 2.0.0-alpha - Data table fonctionnelle diff --git a/src/pages/about.py b/src/pages/about.py index 72fe357..bdae8df 100644 --- a/src/pages/about.py +++ b/src/pages/about.py @@ -19,7 +19,7 @@ layout = [ children=[ html.H2(title), dcc.Markdown( - """Outil d'exploration libre et gratuit des [Données Essentielles de la Commande Publique](), développé par Colin Maudry. + """Outil d'exploration libre et gratuit des données de marchés publics, développé par Colin Maudry. Ce projet vise à démocratiser l'accès aux données des marchés publics et à un outil performant et gratuit. Si vous le trouvez utile j'aimerais beaucoup échanger avec vous pour comprendre vos cas d'usages et vos besoins. Cet outil ne peut rester performant que si je comprends les problèmes qu'il peut aider à résoudre. Ce projet ne peut rester gratuit que grâce au financement du développement de nouvelles fonctionnalités. diff --git a/src/pages/home.py b/src/pages/home.py index d7c3e44..a33fae3 100644 --- a/src/pages/home.py +++ b/src/pages/home.py @@ -141,7 +141,11 @@ layout = [ html.Div( [ html.P("lignes", id="nb_rows"), - html.Button("Télécharger au format Excel", id="btn-download-data"), + html.Button( + "Téléchargement désactivé au-delà de 65 000 lignes", + id="btn-download-data", + disabled=True, + ), dcc.Download(id="download-data"), html.P("Données mises à jour le " + str(update_date)), ], @@ -157,6 +161,9 @@ layout = [ Output("table", "data"), Output("table", "data_timestamp"), Output("nb_rows", "children"), + Output("btn-download-data", "disabled"), + Output("btn-download-data", "children"), + Output("btn-download-data", "title"), Input("table", "page_current"), Input("table", "page_size"), Input("table", "filter_query"), @@ -215,7 +222,9 @@ def update_table(page_current, page_size, filter_query, sort_by, data_timestamp) df_filtered = dff.clone() - nb_rows = f"{format_number(dff.height)} lignes" + height = dff.height + + nb_rows = f"{format_number(height)} lignes" # Pagination des données start_row = page_current * page_size @@ -223,7 +232,23 @@ def update_table(page_current, page_size, filter_query, sort_by, data_timestamp) dff = dff.slice(start_row, page_size) dicts = dff.to_dicts() - return dicts, data_timestamp + 1, nb_rows + if height > 65000: + download_disabled = True + download_text = "Téléchargement désactivé au-delà de 65 000 lignes" + download_title = "Excel ne supporte pas d'avoir plus de 65 000 URLs dans une même feuille de calcul. Contactez-moi pour me présenter votre besoin en téléchargement afin que je puisse adapter la solution." + else: + download_disabled = False + download_text = "Télécharger au format Excel" + download_title = "" + + return ( + dicts, + data_timestamp + 1, + nb_rows, + download_disabled, + download_text, + download_title, + ) @callback( From 5c90b5f350820af6b17a113851e0c14b3d834936 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Tue, 23 Sep 2025 17:40:05 +0200 Subject: [PATCH 22/24] bump version number --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 021fa6e..5d086ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "decp.info" description = "" -version = "2.0.0" +version = "2.0.1" requires-python = ">= 3.10" authors = [ { name = "Colin Maudry", email = "colin+decp@maudry.com" } From a41b70d5afd66db38fd0f78c1b678cfb42766d18 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Wed, 24 Sep 2025 09:50:36 +0200 Subject: [PATCH 23/24] =?UTF-8?q?Script=20pour=20le=20d=C3=A9ploiement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 4 ++-- deploy.sh | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100755 deploy.sh diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 9eb467b..c2365fb 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest environment: ${{ github.ref_name }} steps: - - name: Checkout repositorypu + - name: Checkout repository uses: actions/checkout@v3 - name: Set up SSH key @@ -40,4 +40,4 @@ jobs: key: ${{ secrets.ARTIFACT_SSH_KEY }} passphrase: ${{ secrets.SSH_PSWD }} command_timeout: 5m - script: ${{ secrets.APP_PATH }}/deploy.sh + script: ${{ secrets.APP_PATH }}/deploy.sh ${{ env.APP_NAME }} diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..16f3723 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,15 @@ +#!bin/bash + +# Utilisé principalement avec les Github Actions +# cf. .github/workflows/deploy.yaml + +appname = "$1" + +systemctl stop $appname +cd /var/www/$appname +git pull +source .venv/bin/activate +pip install . +deactivate +chown -R $appname:www-data * +systemctl start $appname From ec877669d4dea8f648fb55c918c0eb482dfa2076 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Wed, 24 Sep 2025 10:21:40 +0200 Subject: [PATCH 24/24] =?UTF-8?q?D=C3=A9tails=20sur=20le=20d=C3=A9ploiemen?= =?UTF-8?q?t=20dans=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 5c35fdc..7092317 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,11 @@ gunicorn app:server python run.py ``` +## Déploiement + +- **Production** (branche `main`, [decp.info](https://decp.info)) : déploiement manuel via un déclenchement de la Github Action [Déploiement](https://github.com/ColinMaudry/decp.info/actions/workflows/deploy.yaml). +- **Test** (branche `dev`, [test.decp.info](https://test.decp.info)) : déploiement automatique à chaque push sur la branche `dev`, via la même Github Action. + ## Liens connexes - [decp-processing](https://github.com/ColinMaudry/decp-processing) (traitement et publication des données) @@ -34,6 +39,7 @@ python run.py #### 2.0.1 (23 septembre 2025) - Bloquage du bouton de téléchargement si trop de lignes (+ 65000) [#38](https://github.com/ColinMaudry/decp.info/issues/38) +- Amélioration du script de déploiement (deploy.sh) - Meilleures instructions d'installation et lancement - Coquilles 🐚