mirror of
https://github.com/fhswf/aki_prj23_transparenzregister.git
synced 2025-06-21 23:33:54 +02:00
175 create person page (#178)
Created person page and updated search bar in the header to search for persons
This commit is contained in:
@ -11,7 +11,7 @@ from aki_prj23_transparenzregister.config.config_providers import (
|
|||||||
HELP_TEXT_CONFIG,
|
HELP_TEXT_CONFIG,
|
||||||
get_config_provider,
|
get_config_provider,
|
||||||
)
|
)
|
||||||
from aki_prj23_transparenzregister.ui import ui_elements
|
from aki_prj23_transparenzregister.ui import data_elements, header_elements
|
||||||
from aki_prj23_transparenzregister.ui.session_handler import SessionHandler
|
from aki_prj23_transparenzregister.ui.session_handler import SessionHandler
|
||||||
from aki_prj23_transparenzregister.utils.logger_config import (
|
from aki_prj23_transparenzregister.utils.logger_config import (
|
||||||
add_logger_options_to_argparse,
|
add_logger_options_to_argparse,
|
||||||
@ -26,7 +26,9 @@ app = Dash(
|
|||||||
app.layout = html.Div(
|
app.layout = html.Div(
|
||||||
className="page_content",
|
className="page_content",
|
||||||
children=[
|
children=[
|
||||||
ui_elements.create_header(ui_elements.get_options(SessionHandler.session)),
|
header_elements.create_header(
|
||||||
|
data_elements.get_options(SessionHandler.session)
|
||||||
|
),
|
||||||
dash.page_container,
|
dash.page_container,
|
||||||
dcc.Location(id="url", refresh=True),
|
dcc.Location(id="url", refresh=True),
|
||||||
],
|
],
|
||||||
@ -55,16 +57,21 @@ def go_to_home(click: int) -> str:
|
|||||||
Input("select_company", "value"),
|
Input("select_company", "value"),
|
||||||
prevent_initial_call=True,
|
prevent_initial_call=True,
|
||||||
)
|
)
|
||||||
def go_to_company_page(value: int) -> str:
|
def go_to_detail_page(id: str) -> str:
|
||||||
"""Updates pages after selecting a company or using the home button.
|
"""Updates pages after selecting a company or using the home button.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: Represents the company id of the chosen company in the dropdown
|
id: Represents the company or person id of the chosen company or person in the dropdown menu.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Returns the href of the company page.
|
Returns the href of the company page.
|
||||||
"""
|
"""
|
||||||
return f"/Unternehmensdetails/{value}"
|
if id.startswith("p_"):
|
||||||
|
page = "/Personendetails/"
|
||||||
|
if id.startswith("c_"):
|
||||||
|
page = "/Unternehmensdetails/"
|
||||||
|
chosen_id = id.split("_", 1)[1]
|
||||||
|
return f"{page}{chosen_id}"
|
||||||
|
|
||||||
|
|
||||||
@app.callback(
|
@app.callback(
|
||||||
@ -83,11 +90,11 @@ def update_options(search_value: str) -> list:
|
|||||||
if not search_value:
|
if not search_value:
|
||||||
return [
|
return [
|
||||||
{"label": o, "value": key}
|
{"label": o, "value": key}
|
||||||
for key, o in ui_elements.get_options(SessionHandler.session).items()
|
for key, o in data_elements.get_options(SessionHandler.session).items()
|
||||||
]
|
]
|
||||||
return [
|
return [
|
||||||
{"label": o, "value": key}
|
{"label": o, "value": key}
|
||||||
for key, o in ui_elements.get_options(SessionHandler.session).items()
|
for key, o in data_elements.get_options(SessionHandler.session).items()
|
||||||
if search_value.upper() in o.upper()
|
if search_value.upper() in o.upper()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
.company-header {
|
.selection-header {
|
||||||
float: left;
|
float: left;
|
||||||
background-color: var(--paynes-gray);
|
background-color: var(--paynes-gray);
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.company-header .company-header-title {
|
.selection-header .selection-header-title {
|
||||||
color: white;
|
color: white;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
"""Dash elements."""
|
"""Dash elements."""
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import plotly.graph_objs as go
|
|
||||||
from cachetools import TTLCache, cached
|
|
||||||
from dash import dash_table, dcc, html
|
from dash import dash_table, dcc, html
|
||||||
from sqlalchemy.engine import Engine
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from aki_prj23_transparenzregister.utils.sql import entities
|
from aki_prj23_transparenzregister.ui import finance_elements
|
||||||
|
|
||||||
COLORS = {
|
COLORS = {
|
||||||
"light": "#edefef",
|
"light": "#edefef",
|
||||||
@ -19,132 +15,6 @@ COLORS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_company_data(session: Session) -> pd.DataFrame:
|
|
||||||
"""Creates a session to the database and get's all available company data.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
session: A session connecting to the database.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A dataframe containing all available company data including the corresponding district court.
|
|
||||||
"""
|
|
||||||
query_company = session.query(entities.Company, entities.DistrictCourt.name).join(
|
|
||||||
entities.DistrictCourt
|
|
||||||
)
|
|
||||||
engine = session.bind
|
|
||||||
if not isinstance(engine, Engine):
|
|
||||||
raise TypeError
|
|
||||||
|
|
||||||
return pd.read_sql(str(query_company), engine, index_col="company_id")
|
|
||||||
|
|
||||||
|
|
||||||
def get_finance_data(session: Session) -> pd.DataFrame:
|
|
||||||
"""Collects all available company data.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
session: A session connecting to the database.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A dataframe containing all financial data of all companies.
|
|
||||||
"""
|
|
||||||
query_finance = session.query(
|
|
||||||
entities.AnnualFinanceStatement, entities.Company.name, entities.Company.id
|
|
||||||
).join(entities.Company)
|
|
||||||
|
|
||||||
engine = session.bind
|
|
||||||
if not isinstance(engine, Engine):
|
|
||||||
raise TypeError
|
|
||||||
|
|
||||||
return pd.read_sql(str(query_finance), engine)
|
|
||||||
|
|
||||||
|
|
||||||
@cached( # type: ignore
|
|
||||||
cache=TTLCache(maxsize=1, ttl=300),
|
|
||||||
key=lambda session: 0 if session is None else str(session.bind),
|
|
||||||
)
|
|
||||||
def get_options(session: Session | None) -> dict[int, str]:
|
|
||||||
"""Collects the search options for the companies.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
session: A session connecting to the database.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A dict containing the company id as key and its name.
|
|
||||||
"""
|
|
||||||
if not session:
|
|
||||||
return {}
|
|
||||||
return get_company_data(session)["company_name"].to_dict()
|
|
||||||
|
|
||||||
|
|
||||||
def create_header(options: dict) -> html:
|
|
||||||
"""Creates header for dashboard.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
options: A dictionary with company names and ids for the dropdown.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The html div to create the page's header including the name of the page and the search for companies.
|
|
||||||
"""
|
|
||||||
return html.Div(
|
|
||||||
className="header-wrapper",
|
|
||||||
children=[
|
|
||||||
html.Div(
|
|
||||||
className="header-title",
|
|
||||||
children=[
|
|
||||||
html.I(
|
|
||||||
id="home-button",
|
|
||||||
n_clicks=0,
|
|
||||||
className="bi-house-door-fill",
|
|
||||||
),
|
|
||||||
html.H1(
|
|
||||||
className="header-title-text",
|
|
||||||
children="Transparenzregister für Kapitalgesellschaften",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
html.Div(
|
|
||||||
className="header-search",
|
|
||||||
children=[
|
|
||||||
html.Div(
|
|
||||||
className="header-search-dropdown",
|
|
||||||
children=[
|
|
||||||
dcc.Dropdown(
|
|
||||||
id="select_company",
|
|
||||||
options=[
|
|
||||||
{"label": o, "value": key}
|
|
||||||
for key, o in options.items()
|
|
||||||
],
|
|
||||||
placeholder="Suche nach Unternehmen oder Person",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def create_company_header(selected_company_name: str) -> html:
|
|
||||||
"""Create company header based on selected company.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
selected_company_name: The company name that has been chosen in the dropdown.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The html div to create the company header.
|
|
||||||
"""
|
|
||||||
return html.Div(
|
|
||||||
className="company-header",
|
|
||||||
children=[
|
|
||||||
html.H1(
|
|
||||||
className="company-header-title",
|
|
||||||
id="id-company-header-title",
|
|
||||||
children=selected_company_name,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def create_company_stats(selected_company_data: pd.Series) -> html:
|
def create_company_stats(selected_company_data: pd.Series) -> html:
|
||||||
"""Create company stats.
|
"""Create company stats.
|
||||||
|
|
||||||
@ -314,7 +184,7 @@ def kennzahlen_layout(selected_finance_df: pd.DataFrame) -> html:
|
|||||||
return html.Div(
|
return html.Div(
|
||||||
[
|
[
|
||||||
dcc.Graph(
|
dcc.Graph(
|
||||||
figure=financials_figure(
|
figure=finance_elements.financials_figure(
|
||||||
selected_finance_df, "annual_finance_statement_ebit"
|
selected_finance_df, "annual_finance_statement_ebit"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -322,37 +192,6 @@ def kennzahlen_layout(selected_finance_df: pd.DataFrame) -> html:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def financials_figure(selected_finance_df: pd.DataFrame, metric: str) -> go.Figure:
|
|
||||||
"""Creates plotly line chart for a specific company and a metric.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
selected_finance_df: A dataframe containing all finance information of the selected company.
|
|
||||||
metric: The metric that should be visualized.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A plotly figure showing the available metric data of the company.
|
|
||||||
"""
|
|
||||||
# create figure
|
|
||||||
fig_line = go.Figure()
|
|
||||||
# add trace for company 1
|
|
||||||
fig_line.add_trace(
|
|
||||||
go.Scatter(
|
|
||||||
x=selected_finance_df["annual_finance_statement_date"],
|
|
||||||
y=selected_finance_df[metric],
|
|
||||||
line_color=COLORS["raisin-black"],
|
|
||||||
marker_color=COLORS["raisin-black"],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
# set title and labels
|
|
||||||
fig_line.update_layout(
|
|
||||||
title=metric,
|
|
||||||
xaxis_title="Jahr",
|
|
||||||
yaxis_title="in Mio.€",
|
|
||||||
plot_bgcolor=COLORS["light"],
|
|
||||||
)
|
|
||||||
return fig_line
|
|
||||||
|
|
||||||
|
|
||||||
def network_layout(selected_company_id: int) -> html:
|
def network_layout(selected_company_id: int) -> html:
|
||||||
"""Create network tab.
|
"""Create network tab.
|
||||||
|
|
@ -1,84 +0,0 @@
|
|||||||
"""Dash."""
|
|
||||||
|
|
||||||
import dash_bootstrap_components as dbc
|
|
||||||
from dash import Dash, Input, Output, callback, html
|
|
||||||
|
|
||||||
from aki_prj23_transparenzregister.config.config_providers import (
|
|
||||||
EnvironmentConfigProvider,
|
|
||||||
)
|
|
||||||
from aki_prj23_transparenzregister.ui.protection import add_auth
|
|
||||||
from aki_prj23_transparenzregister.ui.ui_elements import (
|
|
||||||
create_company_header,
|
|
||||||
create_company_stats,
|
|
||||||
create_header,
|
|
||||||
create_tabs,
|
|
||||||
get_company_data,
|
|
||||||
get_finance_data,
|
|
||||||
)
|
|
||||||
from aki_prj23_transparenzregister.utils.sql import connector
|
|
||||||
from aki_prj23_transparenzregister.utils.sql.connector import init_db
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
session = connector.get_session(EnvironmentConfigProvider())
|
|
||||||
init_db(session)
|
|
||||||
company_df = get_company_data(session)
|
|
||||||
finance_df = get_finance_data(session)
|
|
||||||
options = company_df["company_name"].to_dict()
|
|
||||||
|
|
||||||
app = Dash(
|
|
||||||
__name__, external_stylesheets=[dbc.icons.BOOTSTRAP]
|
|
||||||
) # use dbc for icons
|
|
||||||
add_auth(app)
|
|
||||||
app.title = "Company Finance Data"
|
|
||||||
|
|
||||||
app.layout = html.Div(
|
|
||||||
className="page_content",
|
|
||||||
children=[
|
|
||||||
create_header(options),
|
|
||||||
html.Div(id="id-company-header"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
@callback(
|
|
||||||
Output("select_company", "options"), Input("select_company", "search_value")
|
|
||||||
)
|
|
||||||
def update_options(search_value: str) -> list:
|
|
||||||
"""Update dropdown options based on user input.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
search_value: The input string in the dropdown field entered by the user.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The available companies matching the input.
|
|
||||||
"""
|
|
||||||
if not search_value:
|
|
||||||
return [{"label": o, "value": key} for key, o in options.items()]
|
|
||||||
return [
|
|
||||||
{"label": o, "value": key}
|
|
||||||
for key, o in options.items()
|
|
||||||
if search_value.upper() in o.upper()
|
|
||||||
]
|
|
||||||
|
|
||||||
@callback(Output("id-company-header", "children"), Input("select_company", "value"))
|
|
||||||
def update_output(value_chosen: int) -> html:
|
|
||||||
"""Update page based on chosen company.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value_chosen: Id of the selected company.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The html divs of the company page.
|
|
||||||
"""
|
|
||||||
label = options.get(value_chosen)
|
|
||||||
if not label:
|
|
||||||
return ""
|
|
||||||
selected_company = str(label)
|
|
||||||
selected_company_stats = company_df.loc[value_chosen]
|
|
||||||
selected_finance_df = finance_df.loc[finance_df["company_id"] == value_chosen]
|
|
||||||
return (
|
|
||||||
create_company_header(selected_company),
|
|
||||||
create_company_stats(selected_company_stats),
|
|
||||||
create_tabs(value_chosen, selected_finance_df),
|
|
||||||
)
|
|
||||||
|
|
||||||
app.run_server(debug=False)
|
|
92
src/aki_prj23_transparenzregister/ui/data_elements.py
Normal file
92
src/aki_prj23_transparenzregister/ui/data_elements.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
"""Data elements for Dash."""
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
from cachetools import TTLCache, cached
|
||||||
|
from sqlalchemy.engine import Engine
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from aki_prj23_transparenzregister.utils.sql import entities
|
||||||
|
|
||||||
|
|
||||||
|
def get_company_data(session: Session) -> pd.DataFrame:
|
||||||
|
"""Creates a session to the database and get's all available company data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session: A session connecting to the database.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A dataframe containing all available company data including the corresponding district court.
|
||||||
|
"""
|
||||||
|
query_company = session.query(entities.Company, entities.DistrictCourt.name).join(
|
||||||
|
entities.DistrictCourt
|
||||||
|
)
|
||||||
|
engine = session.bind
|
||||||
|
if not isinstance(engine, Engine):
|
||||||
|
raise TypeError
|
||||||
|
|
||||||
|
return pd.read_sql(str(query_company), engine, index_col="company_id")
|
||||||
|
|
||||||
|
|
||||||
|
def get_person_data(session: Session) -> pd.DataFrame:
|
||||||
|
"""Creates a session to the database and get's all available company data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session: A session connecting to the database.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
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, Engine):
|
||||||
|
raise TypeError
|
||||||
|
|
||||||
|
return pd.read_sql(str(query_person), engine, index_col="person_id")
|
||||||
|
|
||||||
|
|
||||||
|
def get_finance_data(session: Session) -> pd.DataFrame:
|
||||||
|
"""Collects all available company data.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session: A session connecting to the database.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A dataframe containing all financial data of all companies.
|
||||||
|
"""
|
||||||
|
query_finance = session.query(
|
||||||
|
entities.AnnualFinanceStatement, entities.Company.name, entities.Company.id
|
||||||
|
).join(entities.Company)
|
||||||
|
|
||||||
|
engine = session.bind
|
||||||
|
if not isinstance(engine, Engine):
|
||||||
|
raise TypeError
|
||||||
|
|
||||||
|
return pd.read_sql(str(query_finance), engine)
|
||||||
|
|
||||||
|
|
||||||
|
@cached( # type: ignore
|
||||||
|
cache=TTLCache(maxsize=1, ttl=300),
|
||||||
|
key=lambda session: 0 if session is None else str(session.bind),
|
||||||
|
)
|
||||||
|
def get_options(session: Session | None) -> dict[int, str]:
|
||||||
|
"""Collects the search options for the companies and persons.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session: A session connecting to the database.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A dict containing person and company id's as key and their names.
|
||||||
|
"""
|
||||||
|
if not session:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
persons = get_person_data(session).rename("p_{}".format)
|
||||||
|
persons["person_lastname_firstname"] = (
|
||||||
|
persons["person_surname"] + ", " + persons["person_name"]
|
||||||
|
)
|
||||||
|
persons_options = persons["person_lastname_firstname"]
|
||||||
|
companies = get_company_data(session).rename("c_{}".format)
|
||||||
|
companies_options = companies["company_name"]
|
||||||
|
all_options = pd.concat([persons_options, companies_options])
|
||||||
|
|
||||||
|
return all_options.sort_values(ascending=True).to_dict()
|
44
src/aki_prj23_transparenzregister/ui/finance_elements.py
Normal file
44
src/aki_prj23_transparenzregister/ui/finance_elements.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
"""Finance elements for Dash."""
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import plotly.graph_objs as go
|
||||||
|
|
||||||
|
COLORS = {
|
||||||
|
"light": "#edefef",
|
||||||
|
"lavender-blush": "#f3e8ee",
|
||||||
|
"ash-gray": "#bacdb0",
|
||||||
|
"cambridge-blue": "#729b79",
|
||||||
|
"paynes-gray": "#475b63",
|
||||||
|
"raisin-black": "#2e2c2f",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def financials_figure(selected_finance_df: pd.DataFrame, metric: str) -> go.Figure:
|
||||||
|
"""Creates plotly line chart for a specific company and a metric.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
selected_finance_df: A dataframe containing all finance information of the selected company.
|
||||||
|
metric: The metric that should be visualized.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A plotly figure showing the available metric data of the company.
|
||||||
|
"""
|
||||||
|
# create figure
|
||||||
|
fig_line = go.Figure()
|
||||||
|
# add trace for company 1
|
||||||
|
fig_line.add_trace(
|
||||||
|
go.Scatter(
|
||||||
|
x=selected_finance_df["annual_finance_statement_date"],
|
||||||
|
y=selected_finance_df[metric],
|
||||||
|
line_color=COLORS["raisin-black"],
|
||||||
|
marker_color=COLORS["raisin-black"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# set title and labels
|
||||||
|
fig_line.update_layout(
|
||||||
|
title=metric,
|
||||||
|
xaxis_title="Jahr",
|
||||||
|
yaxis_title="in Mio.€",
|
||||||
|
plot_bgcolor=COLORS["light"],
|
||||||
|
)
|
||||||
|
return fig_line
|
72
src/aki_prj23_transparenzregister/ui/header_elements.py
Normal file
72
src/aki_prj23_transparenzregister/ui/header_elements.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
"""Header elements for Dash."""
|
||||||
|
|
||||||
|
from dash import dcc, html
|
||||||
|
|
||||||
|
|
||||||
|
def create_header(options: dict) -> html:
|
||||||
|
"""Creates header for dashboard.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
options: A dictionary with company names and ids for the dropdown.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The html div to create the page's header including the name of the page and the search for companies.
|
||||||
|
"""
|
||||||
|
return html.Div(
|
||||||
|
className="header-wrapper",
|
||||||
|
children=[
|
||||||
|
html.Div(
|
||||||
|
className="header-title",
|
||||||
|
children=[
|
||||||
|
html.I(
|
||||||
|
id="home-button",
|
||||||
|
n_clicks=0,
|
||||||
|
className="bi-house-door-fill",
|
||||||
|
),
|
||||||
|
html.H1(
|
||||||
|
className="header-title-text",
|
||||||
|
children="Transparenzregister für Kapitalgesellschaften",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
html.Div(
|
||||||
|
className="header-search",
|
||||||
|
children=[
|
||||||
|
html.Div(
|
||||||
|
className="header-search-dropdown",
|
||||||
|
children=[
|
||||||
|
dcc.Dropdown(
|
||||||
|
id="select_company",
|
||||||
|
options=[
|
||||||
|
{"label": o, "value": key}
|
||||||
|
for key, o in options.items()
|
||||||
|
],
|
||||||
|
placeholder="Suche nach Unternehmen oder Person",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_selection_header(selected_name: str) -> html:
|
||||||
|
"""Create company header based on selected company.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
selected_name: The company or person name that has been chosen in the dropdown.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The html div to create the company or person header.
|
||||||
|
"""
|
||||||
|
return html.Div(
|
||||||
|
className="selection-header",
|
||||||
|
children=[
|
||||||
|
html.H1(
|
||||||
|
className="selection-header-title",
|
||||||
|
id="id-selection-header-title",
|
||||||
|
children=selected_name,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
@ -2,7 +2,11 @@
|
|||||||
import dash
|
import dash
|
||||||
from dash import html
|
from dash import html
|
||||||
|
|
||||||
from aki_prj23_transparenzregister.ui import ui_elements
|
from aki_prj23_transparenzregister.ui import (
|
||||||
|
company_elements,
|
||||||
|
data_elements,
|
||||||
|
header_elements,
|
||||||
|
)
|
||||||
from aki_prj23_transparenzregister.ui.session_handler import SessionHandler
|
from aki_prj23_transparenzregister.ui.session_handler import SessionHandler
|
||||||
|
|
||||||
dash.register_page(
|
dash.register_page(
|
||||||
@ -26,13 +30,13 @@ def layout(value: str = "1") -> html:
|
|||||||
raise ValueError("Initialise the session first.")
|
raise ValueError("Initialise the session first.")
|
||||||
company_id = int(value)
|
company_id = int(value)
|
||||||
# get all necessary data of the selected company
|
# get all necessary data of the selected company
|
||||||
selected_company_stats = ui_elements.get_company_data(session).loc[company_id]
|
selected_company_stats = data_elements.get_company_data(session).loc[company_id]
|
||||||
selected_company_name = selected_company_stats.loc["company_name"]
|
selected_company_name = selected_company_stats.loc["company_name"]
|
||||||
finance_df = ui_elements.get_finance_data(session)
|
finance_df = data_elements.get_finance_data(session)
|
||||||
selected_finance_df = finance_df.loc[finance_df["company_id"] == value]
|
selected_finance_df = finance_df.loc[finance_df["company_id"] == value]
|
||||||
# create all company page divs
|
# create all company page divs
|
||||||
return (
|
return (
|
||||||
ui_elements.create_company_header(selected_company_name),
|
header_elements.create_selection_header(selected_company_name),
|
||||||
ui_elements.create_company_stats(selected_company_stats),
|
company_elements.create_company_stats(selected_company_stats),
|
||||||
ui_elements.create_tabs(company_id, selected_finance_df),
|
company_elements.create_tabs(company_id, selected_finance_df),
|
||||||
)
|
)
|
||||||
|
35
src/aki_prj23_transparenzregister/ui/pages/person.py
Normal file
35
src/aki_prj23_transparenzregister/ui/pages/person.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
"""Person detail page."""
|
||||||
|
import dash
|
||||||
|
from dash import html
|
||||||
|
|
||||||
|
from aki_prj23_transparenzregister.ui import data_elements, header_elements
|
||||||
|
from aki_prj23_transparenzregister.ui.session_handler import SessionHandler
|
||||||
|
|
||||||
|
dash.register_page(
|
||||||
|
__name__, path_template="/Personendetails/<value>", title="Personendetails"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def layout(value: str = "1") -> html:
|
||||||
|
"""Defines the layout of the person detail page.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Person id of the chosen person on the home page dropdown.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The html divs for the person page.
|
||||||
|
"""
|
||||||
|
if not value:
|
||||||
|
return html.Div("Diese Seite kann nicht angezeigt werden.")
|
||||||
|
session = SessionHandler.session
|
||||||
|
if not session:
|
||||||
|
raise ValueError("Initialise the session first.")
|
||||||
|
person_id = int(value)
|
||||||
|
# get all necessary data of the selected person
|
||||||
|
selected_person_stats = data_elements.get_person_data(session).loc[person_id]
|
||||||
|
selected_person_name = (
|
||||||
|
selected_person_stats["person_name"]
|
||||||
|
+ " "
|
||||||
|
+ selected_person_stats["person_surname"]
|
||||||
|
)
|
||||||
|
return header_elements.create_selection_header(selected_person_name)
|
1
src/aki_prj23_transparenzregister/ui/person_elements.py
Normal file
1
src/aki_prj23_transparenzregister/ui/person_elements.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Dash elements for person page."""
|
@ -31,9 +31,9 @@ def test_go_to_home() -> None:
|
|||||||
assert output == "/"
|
assert output == "/"
|
||||||
|
|
||||||
|
|
||||||
def test_go_to_company_page() -> None:
|
def test_go_to_detail_page() -> None:
|
||||||
"""Checks if the go_to_company_page callback yields a result."""
|
"""Checks if the go_to_detail_page callback yields a result."""
|
||||||
output = app.go_to_company_page(1)
|
output = app.go_to_detail_page("c_1")
|
||||||
assert output == "/Unternehmensdetails/1"
|
assert output == "/Unternehmensdetails/1"
|
||||||
|
|
||||||
|
|
||||||
|
38
tests/ui/company_elements_test.py
Normal file
38
tests/ui/company_elements_test.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
"""Tests for company elements."""
|
||||||
|
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from aki_prj23_transparenzregister.ui import company_elements, data_elements
|
||||||
|
|
||||||
|
|
||||||
|
def test_import() -> None:
|
||||||
|
"""Checks if an import co ui_elements can be made."""
|
||||||
|
assert company_elements is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_company_stats(full_db: Session) -> None:
|
||||||
|
"""Checks if the company widgets can be created."""
|
||||||
|
company_df = data_elements.get_company_data(full_db)
|
||||||
|
value_chosen = 1
|
||||||
|
selected_company_stats = company_df.loc[value_chosen]
|
||||||
|
company_elements.create_company_stats(selected_company_stats)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_tabs(full_db: Session) -> None:
|
||||||
|
"""Checks if the tabs of the company page can be created."""
|
||||||
|
selected_company_id = 1
|
||||||
|
finance_df = data_elements.get_finance_data(full_db)
|
||||||
|
selected_finance_df = finance_df.loc[
|
||||||
|
finance_df["company_id"] == selected_company_id
|
||||||
|
]
|
||||||
|
company_elements.create_tabs(selected_company_id, selected_finance_df)
|
||||||
|
|
||||||
|
|
||||||
|
def test_kennzahlen_layout(full_db: Session) -> None:
|
||||||
|
"""Checks if the financial metric layout of the company page can be created."""
|
||||||
|
selected_company_id = 1
|
||||||
|
finance_df = data_elements.get_finance_data(full_db)
|
||||||
|
selected_finance_df = finance_df.loc[
|
||||||
|
finance_df["company_id"] == selected_company_id
|
||||||
|
]
|
||||||
|
company_elements.kennzahlen_layout(selected_finance_df)
|
@ -1,7 +0,0 @@
|
|||||||
"""Test for the company stats dashboard."""
|
|
||||||
from aki_prj23_transparenzregister.ui import company_finance_dash
|
|
||||||
|
|
||||||
|
|
||||||
def test_import() -> None:
|
|
||||||
"""Checks if an import co company_stats_dash can be made."""
|
|
||||||
assert company_finance_dash is not None
|
|
@ -1,19 +1,19 @@
|
|||||||
"""Tests for ui elements."""
|
"""Tests for data elements."""
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from aki_prj23_transparenzregister.ui import ui_elements
|
from aki_prj23_transparenzregister.ui import data_elements
|
||||||
|
|
||||||
|
|
||||||
def test_import() -> None:
|
def test_import() -> None:
|
||||||
"""Checks if an import co ui_elements can be made."""
|
"""Checks if an import co ui_elements can be made."""
|
||||||
assert ui_elements is not None
|
assert data_elements is not None
|
||||||
|
|
||||||
|
|
||||||
def test_get_company_data(full_db: Session) -> None:
|
def test_get_company_data(full_db: Session) -> None:
|
||||||
"""Checks if data from the company and district court tables can be accessed."""
|
"""Checks if data from the company and district court tables can be accessed."""
|
||||||
company_df = ui_elements.get_company_data(full_db)
|
company_df = data_elements.get_company_data(full_db)
|
||||||
|
|
||||||
test_data = pd.DataFrame(
|
test_data = pd.DataFrame(
|
||||||
{
|
{
|
||||||
@ -50,7 +50,7 @@ def test_get_company_data(full_db: Session) -> None:
|
|||||||
|
|
||||||
def test_get_finance_data(full_db: Session) -> None:
|
def test_get_finance_data(full_db: Session) -> None:
|
||||||
"""Checks if data from the company and finance tables can be accessed."""
|
"""Checks if data from the company and finance tables can be accessed."""
|
||||||
finance_df = ui_elements.get_finance_data(full_db)
|
finance_df = data_elements.get_finance_data(full_db)
|
||||||
test_data = pd.DataFrame(
|
test_data = pd.DataFrame(
|
||||||
{
|
{
|
||||||
"annual_finance_statement_id": {0: 1, 1: 2},
|
"annual_finance_statement_id": {0: 1, 1: 2},
|
||||||
@ -79,43 +79,3 @@ def test_get_finance_data(full_db: Session) -> None:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
pd.testing.assert_frame_equal(finance_df, test_data)
|
pd.testing.assert_frame_equal(finance_df, test_data)
|
||||||
|
|
||||||
|
|
||||||
def test_create_header() -> None:
|
|
||||||
"""Checks if the header can be created."""
|
|
||||||
options = {1: "a", 2: "b"}
|
|
||||||
ui_elements.create_header(options)
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_company_header() -> None:
|
|
||||||
"""Checks if the company header can be created."""
|
|
||||||
selected_company = "Test GmbH"
|
|
||||||
ui_elements.create_company_header(selected_company)
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_company_stats(full_db: Session) -> None:
|
|
||||||
"""Checks if the company widgets can be created."""
|
|
||||||
company_df = ui_elements.get_company_data(full_db)
|
|
||||||
value_chosen = 1
|
|
||||||
selected_company_stats = company_df.loc[value_chosen]
|
|
||||||
ui_elements.create_company_stats(selected_company_stats)
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_tabs(full_db: Session) -> None:
|
|
||||||
"""Checks if the tabs of the company page can be created."""
|
|
||||||
selected_company_id = 1
|
|
||||||
finance_df = ui_elements.get_finance_data(full_db)
|
|
||||||
selected_finance_df = finance_df.loc[
|
|
||||||
finance_df["company_id"] == selected_company_id
|
|
||||||
]
|
|
||||||
ui_elements.create_tabs(selected_company_id, selected_finance_df)
|
|
||||||
|
|
||||||
|
|
||||||
def test_kennzahlen_layout(full_db: Session) -> None:
|
|
||||||
"""Checks if the financial metric layout of the company page can be created."""
|
|
||||||
selected_company_id = 1
|
|
||||||
finance_df = ui_elements.get_finance_data(full_db)
|
|
||||||
selected_finance_df = finance_df.loc[
|
|
||||||
finance_df["company_id"] == selected_company_id
|
|
||||||
]
|
|
||||||
ui_elements.kennzahlen_layout(selected_finance_df)
|
|
20
tests/ui/header_elements_test.py
Normal file
20
tests/ui/header_elements_test.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
"""Tests for header elements."""
|
||||||
|
|
||||||
|
from aki_prj23_transparenzregister.ui import header_elements
|
||||||
|
|
||||||
|
|
||||||
|
def test_import() -> None:
|
||||||
|
"""Checks if an import co ui_elements can be made."""
|
||||||
|
assert header_elements is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_header() -> None:
|
||||||
|
"""Checks if the header can be created."""
|
||||||
|
options = {1: "a", 2: "b"}
|
||||||
|
header_elements.create_header(options)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_selection_header() -> None:
|
||||||
|
"""Checks if the company header can be created."""
|
||||||
|
selected_company = "Test GmbH"
|
||||||
|
header_elements.create_selection_header(selected_company)
|
27
tests/ui/person_page_test.py
Normal file
27
tests/ui/person_page_test.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
"""Tests for the person page dashboard."""
|
||||||
|
from collections.abc import Generator
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from aki_prj23_transparenzregister.ui.pages import person
|
||||||
|
from aki_prj23_transparenzregister.ui.session_handler import SessionHandler
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _set_session(full_db: Session) -> Generator[None, None, None]:
|
||||||
|
"""Sets a session for the dash application to be used."""
|
||||||
|
SessionHandler.session = full_db
|
||||||
|
yield
|
||||||
|
SessionHandler.session = None
|
||||||
|
|
||||||
|
|
||||||
|
def test_import() -> None:
|
||||||
|
"""Checks if an import of the company page can be made."""
|
||||||
|
assert person is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_layout() -> None:
|
||||||
|
"""Checks if the company page can be created."""
|
||||||
|
selected_company_id = "2"
|
||||||
|
person.layout(selected_company_id)
|
Reference in New Issue
Block a user