Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0db800fdab | |||
| 4619dd2708 | |||
| 367e5f64ff | |||
| adda58bada | |||
| 2b0b048520 | |||
| 5c43bfea78 |
@@ -1,3 +1,11 @@
|
||||
##### 2.6.2 (22 février 2026)
|
||||
|
||||
- Correction du téléchargemnent buggé dans /tableau
|
||||
|
||||
##### 2.6.1 (17 février 2026)
|
||||
|
||||
- Corrections la création des liens canoniques (SEO)
|
||||
|
||||
#### 2.6.0 (5 février 2026)
|
||||
|
||||
- Suite de la refonte graphique
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# decp.info
|
||||
|
||||
> v2.6.0
|
||||
> v2.6.2
|
||||
> Outil d'exploration et de téléchargement des données essentielles de la commande publique.
|
||||
|
||||
=> [decp.info](https://decp.info)
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "decp.info"
|
||||
description = "Interface d'exploration et d'analyse des marchés publics français."
|
||||
version = "2.6.0"
|
||||
version = "2.6.2"
|
||||
requires-python = ">= 3.10"
|
||||
authors = [
|
||||
{ name = "Colin Maudry", email = "colin@colmo.tech" }
|
||||
@@ -28,6 +28,7 @@ dev = [
|
||||
"selenium",
|
||||
"webdriver-manager",
|
||||
"dash[testing]",
|
||||
"fastexcel"
|
||||
]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
@@ -38,6 +39,7 @@ testpaths = [
|
||||
"tests"
|
||||
]
|
||||
env = [
|
||||
"DATA_FILE_PARQUET_PATH=tests/test.parquet"
|
||||
"DATA_FILE_PARQUET_PATH=tests/test.parquet",
|
||||
"DEVELOPMENT=true"
|
||||
]
|
||||
addopts = "-p no:warnings"
|
||||
|
||||
@@ -87,6 +87,7 @@ app.index_string = """
|
||||
<title>{%title%}</title>
|
||||
{%favicon%}
|
||||
{%css%}
|
||||
<!-- canonical link -->
|
||||
</head>
|
||||
<body>
|
||||
{%app_entry%}
|
||||
|
||||
+6
-11
@@ -1,4 +1,5 @@
|
||||
import datetime
|
||||
from typing import Any
|
||||
|
||||
import dash_bootstrap_components as dbc
|
||||
import polars as pl
|
||||
@@ -17,7 +18,6 @@ from dash import (
|
||||
from src.callbacks import get_top_org_table
|
||||
from src.figures import DataTable, make_column_picker, point_on_map
|
||||
from src.utils import (
|
||||
add_canonical_link,
|
||||
columns,
|
||||
df,
|
||||
df_acheteurs,
|
||||
@@ -34,12 +34,12 @@ from src.utils import (
|
||||
|
||||
|
||||
def get_title(acheteur_id: str = None) -> str:
|
||||
df_acheteur = df_acheteurs.filter(pl.col("acheteur_id") == acheteur_id).select(
|
||||
acheteur_nom = df_acheteurs.filter(pl.col("acheteur_id") == acheteur_id).select(
|
||||
"acheteur_nom"
|
||||
)
|
||||
acheteur_nom = df_acheteur.item(0, 0)
|
||||
|
||||
return f"Marchés publics attribués par {acheteur_nom} | decp.info"
|
||||
if acheteur_nom.height > 0:
|
||||
return f"Marchés publics attribués par {acheteur_nom.item(0, 0)} | decp.info"
|
||||
return "Marchés publics attribués | decp.info"
|
||||
|
||||
|
||||
register_page(
|
||||
@@ -353,7 +353,7 @@ def get_top_titulaires(data):
|
||||
)
|
||||
def download_acheteur_data(
|
||||
n_clicks,
|
||||
data: [dict],
|
||||
data: list[dict[str, Any]],
|
||||
acheteur_nom: str,
|
||||
annee: str,
|
||||
):
|
||||
@@ -476,8 +476,3 @@ def toggle_acheteur_columns(click_open, click_close, is_open):
|
||||
)
|
||||
def reset_view(n_clicks):
|
||||
return "", []
|
||||
|
||||
|
||||
@callback(Input("acheteur_url", "pathname"))
|
||||
def cb_add_canonical_link(pathname):
|
||||
add_canonical_link(pathname)
|
||||
|
||||
+1
-13
@@ -22,7 +22,6 @@ from dash import (
|
||||
from figures import make_column_picker
|
||||
from src.figures import DataTable
|
||||
from src.utils import (
|
||||
add_canonical_link,
|
||||
columns,
|
||||
df,
|
||||
filter_table_data,
|
||||
@@ -327,7 +326,7 @@ def download_data(n_clicks, filter_query, sort_by, hidden_columns: list = None):
|
||||
if filter_query:
|
||||
lff = filter_table_data(lff, filter_query, "tab download")
|
||||
|
||||
if len(sort_by) > 0:
|
||||
if sort_by and len(sort_by) > 0:
|
||||
lff = sort_table_data(lff, sort_by)
|
||||
|
||||
def to_bytes(buffer):
|
||||
@@ -534,14 +533,3 @@ def toggle_tableau_columns(click_open, click_close, is_open):
|
||||
)
|
||||
def reset_view(n_clicks):
|
||||
return "", []
|
||||
|
||||
|
||||
@callback(Input("tableau_url", "pathname"))
|
||||
def cb_add_canonical_link(pathname):
|
||||
add_canonical_link(pathname)
|
||||
|
||||
|
||||
# @callback(Input("tableau_url", "pathname"), Output("btn-copy-url", "children"))
|
||||
# def cb_add_canonical_link(pathname):
|
||||
# add_canonical_link(pathname)
|
||||
#
|
||||
|
||||
+7
-12
@@ -1,4 +1,5 @@
|
||||
import datetime
|
||||
from typing import Any
|
||||
|
||||
import dash_bootstrap_components as dbc
|
||||
import polars as pl
|
||||
@@ -17,7 +18,6 @@ from dash import (
|
||||
from src.callbacks import get_top_org_table
|
||||
from src.figures import DataTable, make_column_picker, point_on_map
|
||||
from src.utils import (
|
||||
add_canonical_link,
|
||||
columns,
|
||||
df,
|
||||
df_titulaires,
|
||||
@@ -34,12 +34,12 @@ from src.utils import (
|
||||
|
||||
|
||||
def get_title(titulaire_id: str = None) -> str:
|
||||
titulaire_nom = (
|
||||
df_titulaires.filter(pl.col("titulaire_id") == titulaire_id)
|
||||
.select("titulaire_nom")
|
||||
.item(0, 0)
|
||||
titulaire_nom = df_titulaires.filter(pl.col("titulaire_id") == titulaire_id).select(
|
||||
"titulaire_nom"
|
||||
)
|
||||
return f"Marchés publics remportés par {titulaire_nom} | decp.info"
|
||||
if titulaire_nom.height > 0:
|
||||
return f"Marchés publics remportés par {titulaire_nom.item(0, 0)} | decp.info"
|
||||
return "Marchés publics remportés | decp.info"
|
||||
|
||||
|
||||
register_page(
|
||||
@@ -367,7 +367,7 @@ def get_top_acheteurs(data):
|
||||
)
|
||||
def download_titulaire_data(
|
||||
n_clicks,
|
||||
data: [dict],
|
||||
data: list[dict[str, Any]],
|
||||
titulaire_nom: str,
|
||||
annee: str,
|
||||
):
|
||||
@@ -490,8 +490,3 @@ def toggle_titulaire_columns(click_open, click_close, is_open):
|
||||
)
|
||||
def reset_view(n_clicks):
|
||||
return "", []
|
||||
|
||||
|
||||
@callback(Input("titulaire_url", "pathname"))
|
||||
def cb_add_canonical_link(pathname):
|
||||
add_canonical_link(pathname)
|
||||
|
||||
+1
-10
@@ -5,7 +5,6 @@ import uuid
|
||||
from collections import OrderedDict
|
||||
from time import localtime, sleep
|
||||
|
||||
import dash
|
||||
import polars as pl
|
||||
import polars.selectors as cs
|
||||
from dash import no_update
|
||||
@@ -443,7 +442,7 @@ def get_default_hidden_columns(page):
|
||||
"codeCPV",
|
||||
"dureeRestanteMois",
|
||||
]
|
||||
elif page == "titulaire":
|
||||
elif page == "tableau":
|
||||
displayed_columns = os.getenv("DISPLAYED_COLUMNS")
|
||||
else:
|
||||
displayed_columns = os.getenv("DISPLAYED_COLUMNS")
|
||||
@@ -740,14 +739,6 @@ def make_org_jsonld(org_id, org_type, org_name=None, type_org_id="SIRET") -> dic
|
||||
return jsonld
|
||||
|
||||
|
||||
def add_canonical_link(pathname):
|
||||
@dash.hooks.index()
|
||||
def update_index(html_string):
|
||||
url = f"https://{domain_name}{pathname}"
|
||||
canonical_tag = f'<link rel="canonical" href="{url}" />'
|
||||
return html_string.replace("</head>", f"{canonical_tag}\n </head>")
|
||||
|
||||
|
||||
df: pl.DataFrame = get_decp_data()
|
||||
schema = df.collect_schema()
|
||||
|
||||
|
||||
+14
-1
@@ -43,8 +43,21 @@ def test_data():
|
||||
pl.DataFrame(data).write_parquet("tests/test.parquet")
|
||||
yield path
|
||||
|
||||
if os.path.exists(path):
|
||||
os.unlink(path)
|
||||
print(path, "deleted")
|
||||
|
||||
|
||||
def pytest_setup_options():
|
||||
options = Options()
|
||||
options.add_argument("--window-size=1200,800")
|
||||
options.add_argument("--window-size=1200,1200 ")
|
||||
options.add_experimental_option(
|
||||
"prefs",
|
||||
{
|
||||
"download.default_directory": "/home/colin/git/decp.info",
|
||||
"download.prompt_for_download": False,
|
||||
"download.directory_upgrade": True,
|
||||
"safebrowsing.enabled": True,
|
||||
},
|
||||
)
|
||||
return options
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import polars as pl
|
||||
from dash.testing.composite import DashComposite
|
||||
from selenium.webdriver import Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
@@ -58,3 +59,31 @@ def test_002_filter_persistence(dash_duo: DashComposite):
|
||||
filter_input.send_keys(Keys.ENTER)
|
||||
filter_input = open_page_and_check_filter_input()
|
||||
assert filter_input.get_attribute("value") == "11"
|
||||
|
||||
|
||||
def test_003_tableau_download(dash_duo: DashComposite):
|
||||
from pages.acheteur import download_acheteur_data
|
||||
from pages.tableau import download_data
|
||||
from pages.titulaire import download_titulaire_data
|
||||
from src.app import app
|
||||
|
||||
# Juste pour instancier l'app
|
||||
print(app.server.name)
|
||||
|
||||
dicts = pl.read_parquet("tests/test.parquet").to_dicts()
|
||||
|
||||
outputs = [
|
||||
download_data(1, "", [], None),
|
||||
download_acheteur_data(1, dicts, "a1", "2025"),
|
||||
download_titulaire_data(1, dicts, "t1", "2025"),
|
||||
]
|
||||
for output in outputs:
|
||||
assert isinstance(output, dict)
|
||||
for f in ["content", "filename", "type", "base64"]:
|
||||
assert f in output
|
||||
assert isinstance(output["content"], str) and len(output["content"]) > 100
|
||||
assert isinstance(output["filename"], str) and output["filename"].startswith(
|
||||
"decp_"
|
||||
)
|
||||
assert output["type"] is None
|
||||
assert output["base64"] is True
|
||||
|
||||
Reference in New Issue
Block a user