diff --git a/src/aki_prj23_transparenzregister/ui/app.py b/src/aki_prj23_transparenzregister/ui/app.py index af85d97..f2b6c26 100644 --- a/src/aki_prj23_transparenzregister/ui/app.py +++ b/src/aki_prj23_transparenzregister/ui/app.py @@ -48,6 +48,7 @@ app.layout = html.Div( Output("url", "href", allow_duplicate=True), Input("home-button", "n_clicks"), prevent_initial_call=True, + allow_duplicate=True, ) def go_to_home(click: int) -> str: """Updates pages after using the home button. @@ -58,6 +59,7 @@ def go_to_home(click: int) -> str: Returns: Returns the href of the home page. """ + _ = click if home_path := os.getenv("DASH_URL_BASE_PATHNAME"): return f"{home_path}" return "/" diff --git a/src/aki_prj23_transparenzregister/ui/data_elements.py b/src/aki_prj23_transparenzregister/ui/data_elements.py index b28cc13..632ec97 100644 --- a/src/aki_prj23_transparenzregister/ui/data_elements.py +++ b/src/aki_prj23_transparenzregister/ui/data_elements.py @@ -1,11 +1,14 @@ """Data elements for Dash.""" +import os +import dash import pandas as pd import sqlalchemy as sa from cachetools import TTLCache, cached from loguru import logger from sqlalchemy.orm import Session +from aki_prj23_transparenzregister.ui.session_handler import SessionHandler from aki_prj23_transparenzregister.utils.sql import entities @@ -38,7 +41,6 @@ def get_person_data(session: Session) -> pd.DataFrame: A dataframe containing all available company data including the corresponding district court. """ query_person = session.query(entities.Person) - engine = session.bind if not isinstance(engine, sa.engine.Engine): raise TypeError @@ -223,3 +225,35 @@ def get_last_article_of_one_company( if not data: return pd.DataFrame() return pd.DataFrame(dict(data), index=[0]) + + +def redirect_via_plot_click(click_data: dict) -> str: + """Redirects on clicking on a plotted entity to that entity.""" + if not click_data: + return dash.no_update + logger.debug(click_data) + db = SessionHandler.session + if not db: + raise ValueError("No SQL session defined.") + home_path = os.getenv("DASH_URL_BASE_PATHNAME", "") + id = ( + db.query(entities.Company.id) + .filter(entities.Company.name == click_data["points"][0]["text"]) + .first() + ) + if id: + url = f"{home_path}/unternehmensdetails/{id[0]}" + logger.debug(f"Redirecting to detail page: {url}") + return url + concatenated_name = entities.Person.firstname + " " + entities.Person.lastname + id = ( + db.query(entities.Person.id) + .filter(concatenated_name == click_data["points"][0]["text"]) + .first() + ) + if id: + url = f"{home_path}/personendetails/{id[0]}" + logger.debug(f"Redirecting to detail page: {url}") + return url + return dash.no_update + # Prevent update if no data point was clicked diff --git a/src/aki_prj23_transparenzregister/ui/pages/company.py b/src/aki_prj23_transparenzregister/ui/pages/company.py index 37d6e13..9e12a9b 100644 --- a/src/aki_prj23_transparenzregister/ui/pages/company.py +++ b/src/aki_prj23_transparenzregister/ui/pages/company.py @@ -1,13 +1,14 @@ """Company detail page.""" import dash from cachetools import TTLCache, cached -from dash import html +from dash import Input, Output, callback, html from aki_prj23_transparenzregister.ui import ( company_elements, data_elements, header_elements, ) +from aki_prj23_transparenzregister.ui.data_elements import redirect_via_plot_click from aki_prj23_transparenzregister.ui.session_handler import SessionHandler dash.register_page( @@ -44,3 +45,13 @@ def layout(value: str = "1") -> html: ), company_elements.create_tabs(session, company_id), ) + + +@callback( + Output("url", "href", allow_duplicate=True), + [Input("company-graph", "clickData")], + prevent_initial_call=True, +) +def redirect_from_company_graph(click_data: dict) -> str: + """Redirects on clicking on a plotted entity to that entity.""" + return redirect_via_plot_click(click_data) diff --git a/src/aki_prj23_transparenzregister/ui/pages/home.py b/src/aki_prj23_transparenzregister/ui/pages/home.py index 5b2c2c4..702b342 100644 --- a/src/aki_prj23_transparenzregister/ui/pages/home.py +++ b/src/aki_prj23_transparenzregister/ui/pages/home.py @@ -1,5 +1,4 @@ """Content of home page.""" -import os from functools import lru_cache from typing import Final @@ -12,7 +11,9 @@ from cachetools import TTLCache, cached from dash import Input, Output, callback, dash_table, dcc, html from loguru import logger -from aki_prj23_transparenzregister.ui.session_handler import SessionHandler +from aki_prj23_transparenzregister.ui.data_elements import ( + redirect_via_plot_click, +) from aki_prj23_transparenzregister.utils.networkx.network_2d import ( create_2d_graph, ) @@ -28,7 +29,6 @@ from aki_prj23_transparenzregister.utils.networkx.networkx_data import ( get_all_company_relations, get_all_person_relations, ) -from aki_prj23_transparenzregister.utils.sql import entities COLORS: Final[dict[str, str]] = { "light": "#edefef", @@ -416,37 +416,15 @@ def update_graph_data( return graph, metrics, nodes_tmp, edges_tmp -@callback(Output("url", "href"), [Input("my-graph", "clickData")]) -def redirect(click_data: dict) -> str: - """Redirects on clicking on an entity to that entity.""" - if not click_data: - return dash.no_update - logger.debug(click_data) - db = SessionHandler.session - if not db: - raise ValueError("No SQL session defined.") - home_path = os.getenv("DASH_URL_BASE_PATHNAME", "") - id = ( - db.query(entities.Company.id) - .filter(entities.Company.name == click_data["points"][0]["text"]) - .first() - ) - if id: - url = f"{home_path}/unternehmensdetails/{id[0]}" - logger.info("Redirecting to detail page: url") - return url - concatenated_name = entities.Person.firstname + " " + entities.Person.lastname - id = ( - db.query(entities.Person.id) - .filter(concatenated_name == click_data["points"][0]["text"]) - .first() - ) - if id: - url = f"{home_path}/personendetails/{id[0]}" - logger.info("Redirecting to detail page: url") - return url - return dash.no_update - # Prevent update if no data point was clicked +@callback( + Output("url", "href"), + [Input("my-graph", "clickData")], + prevent_initial_call=True, + allow_duplicate=True, +) +def redirect_from_home_graph(click_data: dict) -> str: + """Redirects on clicking on a plotted entity to that entity.""" + return redirect_via_plot_click(click_data) @callback( diff --git a/src/aki_prj23_transparenzregister/ui/pages/person.py b/src/aki_prj23_transparenzregister/ui/pages/person.py index 1981c4a..d5b88e6 100644 --- a/src/aki_prj23_transparenzregister/ui/pages/person.py +++ b/src/aki_prj23_transparenzregister/ui/pages/person.py @@ -1,9 +1,10 @@ """Person detail page.""" import dash from cachetools import TTLCache, cached -from dash import html +from dash import Input, Output, callback, html from aki_prj23_transparenzregister.ui import data_elements, header_elements +from aki_prj23_transparenzregister.ui.data_elements import redirect_via_plot_click from aki_prj23_transparenzregister.ui.session_handler import SessionHandler dash.register_page( @@ -31,3 +32,13 @@ def layout(value: str = "1") -> html.Div: selected_person_stats = data_elements.get_person_data(session).loc[person_id] selected_person_name = f"{selected_person_stats['person_firstname']} {selected_person_stats['person_lastname']}" return header_elements.create_selection_header(selected_person_name) + + +@callback( + Output("url", "href", allow_duplicate=True), + [Input("person-graph", "clickData")], + prevent_initial_call=True, +) +def redirect_from_person_graph(click_data: dict) -> str: + """Redirects on clicking on a plotted entity to that entity.""" + return redirect_via_plot_click(click_data) diff --git a/tests/ui/home_page_test.py b/tests/ui/home_page_test.py index 0a28bb0..128aa28 100644 --- a/tests/ui/home_page_test.py +++ b/tests/ui/home_page_test.py @@ -213,13 +213,13 @@ def test_layout() -> None: @pytest.mark.parametrize("empty", ["", None, {}]) def test_redirect_empty(empty: Any) -> None: """Tests the redirection on clicking on the plot with an empty argument.""" - assert home.redirect(empty) == dash.no_update # type: ignore + assert home.redirect_from_home_graph(empty) == dash.no_update # type: ignore def test_redirect_content_without_db() -> None: """Tests the error when no SQL session is defined.""" with pytest.raises(ValueError, match="No SQL session defined."): - assert home.redirect({"empty": ""}) + assert home.redirect_from_home_graph({"empty": ""}) @pytest.mark.usefixtures("_set_session") @@ -236,4 +236,4 @@ def test_redirect_content_without_db() -> None: ) def test_redirect(click_on: dict, redirect_to: Any) -> None: """Tests the redirection when clicking on a plot.""" - assert home.redirect({"points": [click_on]}) == redirect_to + assert home.redirect_from_home_graph({"points": [click_on]}) == redirect_to