diff --git a/pyproject.toml b/pyproject.toml index 5f52b1a..ba2a73c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,8 @@ dependencies = [ "dash", "polars", "gunicorn", - "dash-bootstrap-components" + "dash-bootstrap-components", + "python-dotenv" ] [project.optional-dependencies] diff --git a/src/app.py b/src/app.py index f832877..f97ff3f 100644 --- a/src/app.py +++ b/src/app.py @@ -1,101 +1,35 @@ -from dash import Dash, html, dcc, callback, Output, Input, dash_table -import polars as pl +from dash import Dash, html, page_container, page_registry, dcc import dash_bootstrap_components as dbc -df = pl.read_parquet("/home/colin/git/decp-processing/dist/2025-04-21/decp.parquet") - -app = Dash(external_stylesheets=[dbc.themes.UNITED], title="decp.info") +app = Dash(external_stylesheets=[dbc.themes.UNITED], title="decp.info", use_pages=True) server = app.server -datatable = dash_table.DataTable( - cell_selectable=False, - id="table", - data=df.to_dicts(), - page_size=20, - page_current=0, - page_action="native", - filter_action="native", - filter_options={"case": "insensitive", "placeholder_text": "Filtrer..."}, - columns=[ - {"name": i, "id": i, "deletable": True, "selectable": False} for i in df.columns - ], - selected_columns=[], - selected_rows=[], - sort_action="native", - sort_mode="multi", - export_format="xlsx", - export_columns="visible", - export_headers="ids", - style_cell_conditional=[ - { - "if": {"column_id": "objet"}, - "minWidth": "300px", - "textAlign": "left", - "overflow": "hidden", - "lineHeight": "14px", - "whiteSpace": "normal", - }, - ], -) - app.layout = [ - html.H1(children="decp.info"), - html.Details( - children=[ - html.Summary("Utilisation"), - dcc.Markdown( - """ - - **Filtres** - - Vous pouvez appliquer un filtre pour chaque colonne en entrant du texte sous le nom de la colonne, puis en tapant sur `Entrée`. - - - Champs textuels : la recherche est insensible à la casse (majuscules/minuscules). - - Champs numériques : possibilité d'ajouter < ou > devant le chiffre recherché pour chercher des valeurs inférieures ou supérieur. - - **Télécharger le résultat** - - Vous pouvez télécharger le résultat de vos filtres et tris en cliquant sur Télécharger au format Excel. - - Les colonnes supprimées seront absentes du fichier téléchargé. - - Si vous téléchargez un volume important de données, il se peut que vous attendiez quelques minutes avant le début du téléchargement. - """ - ), - ], - id="instructions", - ), html.Div( [ - "Recherche dans objet : ", - dcc.Input(id="search", value="", type="text"), - ] - ), - dcc.Loading( - overlay_style={"visibility": "visible", "filter": "blur(2px)"}, - id="loading-1", - type="default", - children=datatable, + html.H1("decp.info"), + html.Div( + [ + dcc.Link(page["name"], href=page["relative_path"], className="nav") + for page in page_registry.values() + ] + ), + ], + className="navbar", ), + page_container, ] -@callback( - Output(component_id="table", component_property="data", allow_duplicate=True), - Input(component_id="search", component_property="value"), - prevent_initial_call=True, -) -def global_search(text): - new_df = df - new_df = new_df.filter(pl.col("objet").str.contains("(?i)" + text)) - return new_df.to_dicts() - - # @callback( -# Output("table", "data"), Input("table", "page_current"), Input("table", "page_size") +# Output(component_id="table", component_property="data", allow_duplicate=True), +# Input(component_id="search", component_property="value"), +# prevent_initial_call=True, # ) -# def update_table(page_current, page_size): -# return df[page_current * page_size : (page_current + 1) * page_size].to_dicts() +# def global_search(text): +# new_df = df +# new_df = new_df.filter(pl.col("objet").str.contains("(?i)" + text)) +# return new_df.to_dicts() if __name__ == "__main__": diff --git a/src/assets/style.css b/src/assets/style.css index dcd8cf0..aed2e2e 100644 --- a/src/assets/style.css +++ b/src/assets/style.css @@ -17,3 +17,44 @@ input.dash-filter--case { td[data-dash-column="objet"] { font-size: 85%; } + +.dash-table-container + .dash-spreadsheet-container + .dash-spreadsheet-inner + th.dash-header { + background-color: #666986; + color: white; +} + +.dash-table-container + .dash-spreadsheet-container + .dash-spreadsheet-inner + th.dash-filter { + background-color: #d0d2e6; +} + +#header > *, +.dash-spreadsheet-menu button.export { + margin: 0 0 20px 20px; +} + +.navbar { + width: 100%; + height: 100px; + padding: 10px; +} + +a.nav { + float: left; + margin-right: 40px; + font-size: 120%; +} + +h1 { + float: left; + width: 50%; +} + +h3 { + display: inline; +} diff --git a/src/pages/about.py b/src/pages/about.py new file mode 100644 index 0000000..6118c1c --- /dev/null +++ b/src/pages/about.py @@ -0,0 +1,9 @@ +from dash import register_page, html, page_registry + +title = "À propos" + +register_page( + __name__, path="/a-propos", title=f"decp.info - {title}", name=title, order=3 +) + +layout = [html.H2(title)] diff --git a/src/pages/home.py b/src/pages/home.py new file mode 100644 index 0000000..8e54855 --- /dev/null +++ b/src/pages/home.py @@ -0,0 +1,95 @@ +from dash import ( + Dash, + html, + dcc, + callback, + Output, + Input, + dash_table, + page_container, + register_page, +) +from dotenv import load_dotenv +import os +import polars as pl + +load_dotenv() + +df = pl.read_parquet(os.getenv("DATA_FILE_PARQUET_PATH")) + +title = "Tableau" +register_page(__name__, path="/", title=f"decp.info - {title}", name=title, order=1) + +datatable = dash_table.DataTable( + cell_selectable=False, + id="table", + data=df.to_dicts(), + page_size=20, + page_current=0, + page_action="native", + filter_action="native", + filter_options={"case": "insensitive", "placeholder_text": "Filtrer..."}, + columns=[ + {"name": i, "id": i, "deletable": True, "selectable": False} for i in df.columns + ], + selected_columns=[], + selected_rows=[], + sort_action="native", + sort_mode="multi", + export_format="xlsx", + export_columns="visible", + export_headers="ids", + style_cell_conditional=[ + { + "if": {"column_id": "objet"}, + "minWidth": "300px", + "textAlign": "left", + "overflow": "hidden", + "lineHeight": "14px", + "whiteSpace": "normal", + }, + ], +) + +layout = [ + html.Div( + html.Details( + children=[ + html.Summary(html.H3("Utilisation")), + dcc.Markdown( + """ + + **Filtres** + + Vous pouvez appliquer un filtre pour chaque colonne en entrant du texte sous le nom de la colonne, puis en tapant sur `Entrée`. + + - Champs textuels : la recherche est insensible à la casse (majuscules/minuscules). + - Champs numériques : possibilité d'ajouter < ou > devant le chiffre recherché pour chercher des valeurs inférieures ou supérieur. + + **Télécharger le résultat** + + Vous pouvez télécharger le résultat de vos filtres et tris en cliquant sur Télécharger au format Excel. + + Les colonnes supprimées seront absentes du fichier téléchargé. + + Si vous téléchargez un volume important de données, il se peut que vous attendiez quelques minutes avant le début du téléchargement. + """ + ), + ], + id="instructions", + ), + id="header", + ), + # html.Div( + # [ + # "Recherche dans objet : ", + # dcc.Input(id="search", value="", type="text"), + # ] + # )]), + dcc.Loading( + overlay_style={"visibility": "visible", "filter": "blur(2px)"}, + id="loading-1", + type="default", + children=datatable, + ), +] diff --git a/src/pages/statistics.py b/src/pages/statistics.py new file mode 100644 index 0000000..ad297d6 --- /dev/null +++ b/src/pages/statistics.py @@ -0,0 +1,9 @@ +from dash import register_page, html + +title = "Statistiques" + +register_page( + __name__, path="/statistiques", title=f"decp.info - {title}", name=title, order=2 +) + +layout = [html.H2(title)]