mirror of
https://github.com/fhswf/aki_prj23_transparenzregister.git
synced 2025-04-22 11:32:53 +02:00
update data based on selected company (#122)
Added UI elements to select a company and update shown data depending on chosen company --------- Co-authored-by: Philipp Horstenkamp <philipp@horstenkamp.de>
This commit is contained in:
parent
80f077ee7a
commit
487b2f42d1
2
.github/workflows/lint-actions.yaml
vendored
2
.github/workflows/lint-actions.yaml
vendored
@ -47,7 +47,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: chartboost/ruff-action@v1
|
- uses: chartboost/ruff-action@v1
|
||||||
with:
|
with:
|
||||||
version: 0.0.289
|
version: 0.0.290
|
||||||
|
|
||||||
python-requirements:
|
python-requirements:
|
||||||
name: Check Python Requirements
|
name: Check Python Requirements
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,6 +3,9 @@
|
|||||||
*secrets.json
|
*secrets.json
|
||||||
*secrets_prod.json
|
*secrets_prod.json
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
.vscode/
|
||||||
|
|
||||||
# Snyk
|
# Snyk
|
||||||
.dccache
|
.dccache
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ repos:
|
|||||||
|
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.0.289
|
rev: v0.0.290
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args: [--fix, --exit-non-zero-on-fix]
|
args: [--fix, --exit-non-zero-on-fix]
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"files.eol": "\n"
|
|
||||||
}
|
|
465
poetry.lock
generated
465
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ requires = ["poetry-core"]
|
|||||||
target-version = ["py311"]
|
target-version = ["py311"]
|
||||||
|
|
||||||
[tool.coverage.report]
|
[tool.coverage.report]
|
||||||
exclude_also = ["if __name__ == .__main__.:"]
|
exclude_also = ["if __name__ == .__main__.:", "if not isinstance(engine, Engine):"]
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
branch = true
|
branch = true
|
||||||
@ -81,7 +81,7 @@ mypy = "^1.5.1"
|
|||||||
pandas-stubs = "^2.0.3.230814"
|
pandas-stubs = "^2.0.3.230814"
|
||||||
pip-audit = "^2.6.1"
|
pip-audit = "^2.6.1"
|
||||||
pip-licenses = "^4.3.2"
|
pip-licenses = "^4.3.2"
|
||||||
ruff = "^0.0.289"
|
ruff = "^0.0.290"
|
||||||
types-cachetools = "^5.3.0.6"
|
types-cachetools = "^5.3.0.6"
|
||||||
types-pyOpenSSL = "*"
|
types-pyOpenSSL = "*"
|
||||||
types-requests = "^2.31.0.2"
|
types-requests = "^2.31.0.2"
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
.company-header {
|
||||||
|
float: left;
|
||||||
|
background-color: var(--paynes-gray);
|
||||||
|
border: 1px solid;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-header .company-header-title {
|
||||||
|
color: white;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-wrapper {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
background-color: white;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-right: 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-wrapper .widget-large {
|
||||||
|
background-color: var(--ash-gray);
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 45%;
|
||||||
|
min-width: 600px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-left: 2%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-wrapper .widget-large .widget-title {
|
||||||
|
color: var(--raisin-black);
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-wrapper .widget-small {
|
||||||
|
background-color: var(--ash-gray);
|
||||||
|
border: 1px solid;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 15%;
|
||||||
|
min-width: 200px;
|
||||||
|
height: 150px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-left: 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-wrapper .widget-small .widget-title {
|
||||||
|
color: var(--raisin-black);
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-wrapper .widget-small .widget-content {
|
||||||
|
color: var(--raisin-black);
|
||||||
|
text-align: center;
|
||||||
|
}
|
58
src/aki_prj23_transparenzregister/ui/assets/header.css
Normal file
58
src/aki_prj23_transparenzregister/ui/assets/header.css
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
:root {
|
||||||
|
--light: #edefef;
|
||||||
|
--lavender-blush: #f3e8ee;
|
||||||
|
--ash-gray: #bacdb0;
|
||||||
|
--cambridge-blue: #729b79;
|
||||||
|
--paynes-gray: #475b63;
|
||||||
|
--raisin-black: #2e2c2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-wrapper {
|
||||||
|
float:left;
|
||||||
|
background-color: var(--raisin-black);
|
||||||
|
border: 1px solid;
|
||||||
|
width: 100%;
|
||||||
|
overflow: visible;
|
||||||
|
min-height: 77px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-wrapper .header-title {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-wrapper .header-title .bi-house-door-fill {
|
||||||
|
color: white;
|
||||||
|
font-size: x-large;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-wrapper .header-title .header-title-text {
|
||||||
|
color: white;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-wrapper .header-search {
|
||||||
|
float: right;
|
||||||
|
width: 400px;
|
||||||
|
margin: 0;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
padding-top: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-wrapper .header-search .header-search-dropdown {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
23
src/aki_prj23_transparenzregister/ui/assets/tabs.css
Normal file
23
src/aki_prj23_transparenzregister/ui/assets/tabs.css
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.tabs {
|
||||||
|
float: left;
|
||||||
|
margin-top: 20px;
|
||||||
|
border: 1px solid;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs .tab-style {
|
||||||
|
border-bottom: 1px solid #d6d6d6 !important;
|
||||||
|
padding: 8px !important;
|
||||||
|
background-color: white !important;
|
||||||
|
color: var(--paynes-gray) !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs .selected-tab-style {
|
||||||
|
border-bottom: 1px solid #d6d6d6 !important;
|
||||||
|
border-top: 1px solid #d6d6d6 !important;
|
||||||
|
padding: 8px !important;
|
||||||
|
color: white !important;
|
||||||
|
background-color: var(--paynes-gray) !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
}
|
@ -1,390 +1,78 @@
|
|||||||
"""Dash."""
|
"""Dash."""
|
||||||
|
|
||||||
import dash_bootstrap_components as dbc
|
import dash_bootstrap_components as dbc
|
||||||
import pandas as pd
|
from dash import Dash, Input, Output, callback, html
|
||||||
import plotly.graph_objs as go
|
|
||||||
from dash import Dash, Input, Output, callback, dash_table, dcc, html
|
|
||||||
from dash.exceptions import PreventUpdate
|
|
||||||
from sqlalchemy.engine import Engine
|
|
||||||
|
|
||||||
from aki_prj23_transparenzregister.config.config_providers import JsonFileConfigProvider
|
from aki_prj23_transparenzregister.config.config_providers import JsonFileConfigProvider
|
||||||
from aki_prj23_transparenzregister.utils.sql import connector, entities
|
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
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
session = connector.get_session(JsonFileConfigProvider("./secrets.json"))
|
session = connector.get_session(JsonFileConfigProvider("./secrets.json"))
|
||||||
query_finance = session.query(
|
company_df = get_company_data(session)
|
||||||
entities.AnnualFinanceStatement, entities.Company.name, entities.Company.id
|
finance_df = get_finance_data(session)
|
||||||
).join(entities.Company)
|
options = company_df["company_name"].to_dict()
|
||||||
|
|
||||||
query_company = session.query(entities.Company, entities.DistrictCourt.name).join(
|
|
||||||
entities.DistrictCourt
|
|
||||||
)
|
|
||||||
engine = session.bind
|
|
||||||
if not isinstance(engine, Engine):
|
|
||||||
raise TypeError
|
|
||||||
|
|
||||||
finance_df: pd.DataFrame = pd.read_sql(str(query_finance), engine)
|
|
||||||
company_df: pd.DataFrame = pd.read_sql(str(query_company), engine)
|
|
||||||
|
|
||||||
select_company_df = company_df[["company_id", "company_name"]]
|
|
||||||
select_company_dropdown = select_company_df.to_dict("records")
|
|
||||||
options = [
|
|
||||||
{"label": i["company_name"], "value": i["company_id"]}
|
|
||||||
for i in select_company_dropdown
|
|
||||||
]
|
|
||||||
|
|
||||||
colors = {
|
|
||||||
"light": "#edefef",
|
|
||||||
"lavender-blush": "#f3e8ee",
|
|
||||||
"ash-gray": "#bacdb0",
|
|
||||||
"cambridge-blue": "#729b79",
|
|
||||||
"paynes-gray": "#475b63",
|
|
||||||
"raisin-black": "#2e2c2f",
|
|
||||||
}
|
|
||||||
|
|
||||||
def financials_figure(
|
|
||||||
finance_df: pd.DataFrame, company: str, metric: str
|
|
||||||
) -> go.Figure:
|
|
||||||
"""Creates plotly line chart for a specific company and a metric."""
|
|
||||||
finance_df = finance_df.loc[finance_df["company_name"] == company]
|
|
||||||
# create figure
|
|
||||||
fig_line = go.Figure()
|
|
||||||
# add trace for company 1
|
|
||||||
fig_line.add_trace(
|
|
||||||
go.Scatter(
|
|
||||||
x=finance_df["annual_finance_statement_date"],
|
|
||||||
y=finance_df[metric],
|
|
||||||
name=company,
|
|
||||||
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
|
|
||||||
|
|
||||||
tab_style = {
|
|
||||||
"borderBottom": "1px solid #d6d6d6",
|
|
||||||
"padding": "6px",
|
|
||||||
"backgroundColor": "white",
|
|
||||||
"color": colors["paynes-gray"],
|
|
||||||
"fontWeight": "bold",
|
|
||||||
}
|
|
||||||
|
|
||||||
tab_selected_style = {
|
|
||||||
"borderTop": "1px solid #d6d6d6",
|
|
||||||
"borderBottom": "1px solid #d6d6d6",
|
|
||||||
"padding": "6px",
|
|
||||||
"backgroundColor": colors["paynes-gray"],
|
|
||||||
"color": "white",
|
|
||||||
"fontWeight": "bold",
|
|
||||||
}
|
|
||||||
|
|
||||||
# TBD: get data from database instead of mock data
|
|
||||||
company = 1 # selected company id
|
|
||||||
selected_company = company_df.loc[company_df["company_id"] == company]
|
|
||||||
|
|
||||||
turnover = 123456
|
|
||||||
stock = "1,23"
|
|
||||||
company_data = {
|
|
||||||
"col1": ["Unternehmen", "Straße", "Stadt"],
|
|
||||||
"col2": [
|
|
||||||
selected_company["company_name"][0],
|
|
||||||
selected_company["company_street"][0],
|
|
||||||
str(
|
|
||||||
selected_company["company_zip_code"][0]
|
|
||||||
+ " "
|
|
||||||
+ selected_company["company_city"][0]
|
|
||||||
),
|
|
||||||
],
|
|
||||||
"col3": ["Branche", "Amtsgericht", "Gründungsjahr"],
|
|
||||||
"col4": [
|
|
||||||
selected_company["company_sector"][0],
|
|
||||||
selected_company["district_court_name"][0],
|
|
||||||
"xxx",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
df_company_data = pd.DataFrame(data=company_data)
|
|
||||||
|
|
||||||
app = Dash(
|
app = Dash(
|
||||||
__name__, external_stylesheets=[dbc.icons.BOOTSTRAP]
|
__name__, external_stylesheets=[dbc.icons.BOOTSTRAP]
|
||||||
) # use dbc for icons
|
) # use dbc for icons
|
||||||
|
app.title = "Company Finance Data"
|
||||||
kennzahlen_layout = html.Div(
|
|
||||||
[
|
|
||||||
dcc.Graph(
|
|
||||||
figure=financials_figure(
|
|
||||||
finance_df, str(company), "annual_finance_statement_ebit"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
app.layout = html.Div(
|
app.layout = html.Div(
|
||||||
[
|
className="page_content",
|
||||||
# title header of page
|
children=[
|
||||||
html.Div(
|
create_header(options),
|
||||||
style={
|
html.Div(id="id-company-header"),
|
||||||
"backgroundColor": colors["raisin-black"],
|
],
|
||||||
"border": "1px solid",
|
|
||||||
},
|
|
||||||
children=[
|
|
||||||
html.I(
|
|
||||||
className="bi bi-house-door-fill",
|
|
||||||
style={
|
|
||||||
"fontSize": 24,
|
|
||||||
"paddingLeft": "10px",
|
|
||||||
"color": "white",
|
|
||||||
"display": "inline-block",
|
|
||||||
"verticalAlign": "middle",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
html.H1(
|
|
||||||
children="Transparenzregister für Kapitalgesellschaften",
|
|
||||||
style={
|
|
||||||
"color": "white",
|
|
||||||
"textAlign": "left",
|
|
||||||
"margin": "0",
|
|
||||||
"paddingLeft": "10px",
|
|
||||||
"paddingBottom": "20px",
|
|
||||||
"paddingTop": "20px",
|
|
||||||
"display": "inline-block",
|
|
||||||
"verticalAlign": "middle",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
html.Div(
|
|
||||||
dcc.Dropdown(
|
|
||||||
id="select_company",
|
|
||||||
placeholder="Suche nach Unternehmen oder Person",
|
|
||||||
),
|
|
||||||
style={
|
|
||||||
"float": "right",
|
|
||||||
"width": "30%",
|
|
||||||
"margin": "0",
|
|
||||||
"paddingRight": "10px",
|
|
||||||
"paddingBottom": "20px",
|
|
||||||
"paddingTop": "20px",
|
|
||||||
"display": "inline-block",
|
|
||||||
"verticalAlign": "middle",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
# header company name
|
|
||||||
html.Div(
|
|
||||||
style={"backgroundColor": colors["paynes-gray"], "border": "1px solid"},
|
|
||||||
children=[
|
|
||||||
html.H1(
|
|
||||||
children=selected_company["company_name"][0],
|
|
||||||
style={
|
|
||||||
"color": "white",
|
|
||||||
"fontSize": 30,
|
|
||||||
"textAlign": "left",
|
|
||||||
"margin": "0",
|
|
||||||
"paddingLeft": "20px",
|
|
||||||
"paddingBottom": "20px",
|
|
||||||
"paddingTop": "20px",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
html.Div(style={"height": "20px"}),
|
|
||||||
html.Div(style={"width": "2%", "display": "inline-block"}),
|
|
||||||
# table basic company information
|
|
||||||
html.Div(
|
|
||||||
style={
|
|
||||||
"backgroundColor": colors["ash-gray"],
|
|
||||||
"border": "1px solid",
|
|
||||||
"border-radius": 10,
|
|
||||||
"width": "45%",
|
|
||||||
"height": "150px",
|
|
||||||
"display": "inline-block",
|
|
||||||
"vertical-align": "top",
|
|
||||||
},
|
|
||||||
children=[
|
|
||||||
html.H5(
|
|
||||||
children="Stammdaten",
|
|
||||||
style={
|
|
||||||
"color": colors["raisin-black"],
|
|
||||||
"fontSize": 16,
|
|
||||||
"textAlign": "center",
|
|
||||||
"margin": "0",
|
|
||||||
"paddingBottom": "10px",
|
|
||||||
"paddingTop": "10px",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
dash_table.DataTable(
|
|
||||||
df_company_data.to_dict("records"),
|
|
||||||
[{"name": i, "id": i} for i in df_company_data.columns],
|
|
||||||
style_table={
|
|
||||||
"width": "80%",
|
|
||||||
"overflowX": "auto",
|
|
||||||
"marginLeft": "auto",
|
|
||||||
"marginRight": "auto",
|
|
||||||
"paddingBottom": "20px",
|
|
||||||
"color": colors["raisin-black"],
|
|
||||||
},
|
|
||||||
# hide header of table
|
|
||||||
css=[
|
|
||||||
{
|
|
||||||
"selector": "tr:first-child",
|
|
||||||
"rule": "display: none",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
style_cell={"textAlign": "center"},
|
|
||||||
style_cell_conditional=[
|
|
||||||
{"if": {"column_id": c}, "fontWeight": "bold"}
|
|
||||||
for c in ["col1", "col3"]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
html.Div(style={"width": "2%", "display": "inline-block"}),
|
|
||||||
html.Div(
|
|
||||||
style={
|
|
||||||
"backgroundColor": colors["ash-gray"],
|
|
||||||
"border": "1px solid",
|
|
||||||
"border-radius": 10,
|
|
||||||
"width": "15%",
|
|
||||||
"height": "150px",
|
|
||||||
"display": "inline-block",
|
|
||||||
"vertical-align": "top",
|
|
||||||
},
|
|
||||||
children=[
|
|
||||||
html.H5(
|
|
||||||
children="Stimmung",
|
|
||||||
style={
|
|
||||||
"color": colors["raisin-black"],
|
|
||||||
"fontSize": 16,
|
|
||||||
"textAlign": "center",
|
|
||||||
"margin": "0",
|
|
||||||
"paddingBottom": "10px",
|
|
||||||
"paddingTop": "10px",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
html.Div(style={"width": "2%", "display": "inline-block"}),
|
|
||||||
html.Div(
|
|
||||||
style={
|
|
||||||
"backgroundColor": colors["ash-gray"],
|
|
||||||
"border": "1px solid",
|
|
||||||
"border-radius": 10,
|
|
||||||
"width": "15%",
|
|
||||||
"height": "150px",
|
|
||||||
"display": "inline-block",
|
|
||||||
"vertical-align": "top",
|
|
||||||
},
|
|
||||||
children=[
|
|
||||||
html.H5(
|
|
||||||
children="Aktienkurs",
|
|
||||||
style={
|
|
||||||
"color": colors["raisin-black"],
|
|
||||||
"fontSize": 16,
|
|
||||||
"textAlign": "center",
|
|
||||||
"margin": "0",
|
|
||||||
"paddingBottom": "10px",
|
|
||||||
"paddingTop": "10px",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
html.H1(
|
|
||||||
children=stock,
|
|
||||||
style={
|
|
||||||
"color": colors["raisin-black"],
|
|
||||||
"textAlign": "center",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
html.Div(style={"width": "2%", "display": "inline-block"}),
|
|
||||||
html.Div(
|
|
||||||
style={
|
|
||||||
"backgroundColor": colors["ash-gray"],
|
|
||||||
"border": "1px solid",
|
|
||||||
"border-radius": 10,
|
|
||||||
"width": "15%",
|
|
||||||
"height": "150px",
|
|
||||||
"display": "inline-block",
|
|
||||||
"vertical-align": "top",
|
|
||||||
},
|
|
||||||
children=[
|
|
||||||
html.H5(
|
|
||||||
children="Umsatz",
|
|
||||||
style={
|
|
||||||
"color": colors["raisin-black"],
|
|
||||||
"fontSize": 16,
|
|
||||||
"textAlign": "center",
|
|
||||||
"margin": "0",
|
|
||||||
"paddingBottom": "10px",
|
|
||||||
"paddingTop": "10px",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
html.H1(
|
|
||||||
children=turnover,
|
|
||||||
style={
|
|
||||||
"color": colors["raisin-black"],
|
|
||||||
"textAlign": "center",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
html.Div(style={"width": "2%", "display": "inline-block"}),
|
|
||||||
# ]),
|
|
||||||
html.Div(
|
|
||||||
style={
|
|
||||||
"marginTop": "20px",
|
|
||||||
"border": "1px solid",
|
|
||||||
},
|
|
||||||
children=[
|
|
||||||
dcc.Tabs(
|
|
||||||
id="tabs",
|
|
||||||
value="tab-1",
|
|
||||||
children=[
|
|
||||||
dcc.Tab(
|
|
||||||
label="Kennzahlen",
|
|
||||||
value="tab-1",
|
|
||||||
style=tab_style,
|
|
||||||
selected_style=tab_selected_style,
|
|
||||||
children=[kennzahlen_layout],
|
|
||||||
),
|
|
||||||
dcc.Tab(
|
|
||||||
label="Beteiligte Personen",
|
|
||||||
value="tab-2",
|
|
||||||
style=tab_style,
|
|
||||||
selected_style=tab_selected_style,
|
|
||||||
),
|
|
||||||
dcc.Tab(
|
|
||||||
label="Stimmung",
|
|
||||||
value="tab-3",
|
|
||||||
style=tab_style,
|
|
||||||
selected_style=tab_selected_style,
|
|
||||||
),
|
|
||||||
dcc.Tab(
|
|
||||||
label="Verflechtungen",
|
|
||||||
value="tab-4",
|
|
||||||
style=tab_style,
|
|
||||||
selected_style=tab_selected_style,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
html.Div(id="tabs-example-content-1"),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@callback(
|
@callback(
|
||||||
Output("select_company", "options"), Input("select_company", "search_value")
|
Output("select_company", "options"), Input("select_company", "search_value")
|
||||||
)
|
)
|
||||||
def update_options(search_value: str) -> list:
|
def update_options(search_value: str) -> list:
|
||||||
"""Update page based on selected company."""
|
"""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:
|
if not search_value:
|
||||||
raise PreventUpdate
|
return [{"label": o, "value": key} for key, o in options.items()]
|
||||||
return [o for o in options if search_value in o["label"]]
|
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(selected_finance_df),
|
||||||
|
)
|
||||||
|
|
||||||
app.run_server(debug=True)
|
app.run_server(debug=True)
|
||||||
|
331
src/aki_prj23_transparenzregister/ui/ui_elements.py
Normal file
331
src/aki_prj23_transparenzregister/ui/ui_elements.py
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
"""Dash elements."""
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
import plotly.graph_objs as go
|
||||||
|
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
|
||||||
|
|
||||||
|
COLORS = {
|
||||||
|
"light": "#edefef",
|
||||||
|
"lavender-blush": "#f3e8ee",
|
||||||
|
"ash-gray": "#bacdb0",
|
||||||
|
"cambridge-blue": "#729b79",
|
||||||
|
"paynes-gray": "#475b63",
|
||||||
|
"raisin-black": "#2e2c2f",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
"""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 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)
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
||||||
|
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:
|
||||||
|
"""Create company stats.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
selected_company_data: A series containing all company information of the selected company.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The html div to create the company stats table and the three small widgets.
|
||||||
|
"""
|
||||||
|
company_data = {
|
||||||
|
"col1": ["Unternehmen", "Straße", "Stadt"],
|
||||||
|
"col2": [
|
||||||
|
selected_company_data["company_name"],
|
||||||
|
selected_company_data["company_street"],
|
||||||
|
str(
|
||||||
|
selected_company_data["company_zip_code"]
|
||||||
|
+ " "
|
||||||
|
+ selected_company_data["company_city"]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
"col3": ["Branche", "Amtsgericht", "Gründungsjahr"],
|
||||||
|
"col4": [
|
||||||
|
selected_company_data["company_sector"],
|
||||||
|
selected_company_data["district_court_name"],
|
||||||
|
"xxx",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
df_company_data = pd.DataFrame(data=company_data)
|
||||||
|
return html.Div(
|
||||||
|
className="stats-wrapper",
|
||||||
|
children=[
|
||||||
|
html.Div(
|
||||||
|
className="widget-large",
|
||||||
|
children=[
|
||||||
|
html.H3(
|
||||||
|
className="widget-title",
|
||||||
|
children="Stammdaten",
|
||||||
|
),
|
||||||
|
dash_table.DataTable(
|
||||||
|
df_company_data.to_dict("records"),
|
||||||
|
[{"name": i, "id": i} for i in df_company_data.columns],
|
||||||
|
style_table={
|
||||||
|
"width": "90%",
|
||||||
|
"marginLeft": "auto",
|
||||||
|
"marginRight": "auto",
|
||||||
|
"paddingBottom": "20px",
|
||||||
|
"color": COLORS["raisin-black"],
|
||||||
|
},
|
||||||
|
# hide header of table
|
||||||
|
css=[
|
||||||
|
{
|
||||||
|
"selector": "tr:first-child",
|
||||||
|
"rule": "display: none",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
style_cell={"textAlign": "center"},
|
||||||
|
style_cell_conditional=[
|
||||||
|
{"if": {"column_id": c}, "fontWeight": "bold"}
|
||||||
|
for c in ["col1", "col3"]
|
||||||
|
],
|
||||||
|
style_data={
|
||||||
|
"whiteSpace": "normal",
|
||||||
|
"height": "auto",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
html.Div(
|
||||||
|
className="widget-small",
|
||||||
|
children=[
|
||||||
|
html.H3(
|
||||||
|
className="widget-title",
|
||||||
|
children="Stimmung",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
html.Div(
|
||||||
|
className="widget-small",
|
||||||
|
children=[
|
||||||
|
html.H3(
|
||||||
|
className="widget-title",
|
||||||
|
children="Aktienkurs",
|
||||||
|
),
|
||||||
|
html.H1(
|
||||||
|
className="widget-content",
|
||||||
|
children="123",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
html.Div(
|
||||||
|
className="widget-small",
|
||||||
|
children=[
|
||||||
|
html.H3(
|
||||||
|
className="widget-title",
|
||||||
|
children="Umsatz",
|
||||||
|
),
|
||||||
|
html.H1(
|
||||||
|
className="widget-content",
|
||||||
|
children="1234",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_tabs(selected_finance_df: pd.DataFrame) -> html:
|
||||||
|
"""Create tabs for more company information.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
selected_company_id: Id of the chosen company in the dropdown.
|
||||||
|
selected_finance_df: A dataframe containing all available finance information of the companies.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The html div to create the tabs of the company page.
|
||||||
|
"""
|
||||||
|
return html.Div(
|
||||||
|
className="tabs",
|
||||||
|
children=[
|
||||||
|
dcc.Tabs(
|
||||||
|
id="tabs",
|
||||||
|
value="tab-1",
|
||||||
|
children=[
|
||||||
|
dcc.Tab(
|
||||||
|
label="Kennzahlen",
|
||||||
|
value="tab-1",
|
||||||
|
className="tab-style",
|
||||||
|
selected_className="selected-tab-style",
|
||||||
|
children=[kennzahlen_layout(selected_finance_df)],
|
||||||
|
),
|
||||||
|
dcc.Tab(
|
||||||
|
label="Beteiligte Personen",
|
||||||
|
value="tab-2",
|
||||||
|
className="tab-style",
|
||||||
|
selected_className="selected-tab-style",
|
||||||
|
),
|
||||||
|
dcc.Tab(
|
||||||
|
label="Stimmung",
|
||||||
|
value="tab-3",
|
||||||
|
className="tab-style",
|
||||||
|
selected_className="selected-tab-style",
|
||||||
|
),
|
||||||
|
dcc.Tab(
|
||||||
|
label="Verflechtungen",
|
||||||
|
value="tab-4",
|
||||||
|
className="tab-style",
|
||||||
|
selected_className="selected-tab-style",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
html.Div(id="tabs-example-content-1"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def kennzahlen_layout(selected_finance_df: pd.DataFrame) -> html:
|
||||||
|
"""Create metrics tab.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
selected_company_id: Id of the chosen company in the dropdown.
|
||||||
|
selected_finance_df: A dataframe containing all available finance information of the companies.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The html div to create the metrics tab of the company page.
|
||||||
|
"""
|
||||||
|
return html.Div(
|
||||||
|
[
|
||||||
|
dcc.Graph(
|
||||||
|
figure=financials_figure(
|
||||||
|
selected_finance_df, "annual_finance_statement_ebit"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
@ -124,7 +124,7 @@ def get_district_court_id(name: str, city: str | None, db: Session) -> int:
|
|||||||
|
|
||||||
@cached(cache=LRUCache(maxsize=2000), key=lambda name, surname, date_of_birth, db: hash((name, surname, date_of_birth))) # type: ignore
|
@cached(cache=LRUCache(maxsize=2000), key=lambda name, surname, date_of_birth, db: hash((name, surname, date_of_birth))) # type: ignore
|
||||||
def get_person_id(
|
def get_person_id(
|
||||||
name: str, surname: str, date_of_birth: date | str, db: Session
|
name: str, surname: str, date_of_birth: date | str | None, db: Session
|
||||||
) -> int:
|
) -> int:
|
||||||
"""Identifies the id of and court.
|
"""Identifies the id of and court.
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import datetime
|
|||||||
import os
|
import os
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
from inspect import getmembers, isfunction
|
from inspect import getmembers, isfunction
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from sqlalchemy.engine import Engine
|
from sqlalchemy.engine import Engine
|
||||||
@ -49,7 +50,60 @@ def empty_db() -> Generator[Session, None, None]:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def full_db(empty_db: Session) -> Session:
|
def finance_statements() -> list[dict[str, Any]]:
|
||||||
|
"""Creates a list of finance statements."""
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"company_id": 1,
|
||||||
|
"date": datetime.date.fromisoformat("2023-01-01"),
|
||||||
|
"total_volume": 1000.0,
|
||||||
|
"ebit": 1000.0,
|
||||||
|
"ebitda": 1000.0,
|
||||||
|
"ebit_margin": 1000.0,
|
||||||
|
"total_balance": 1000.0,
|
||||||
|
"equity": 1000.0,
|
||||||
|
"debt": 1000.0,
|
||||||
|
"return_on_equity": 1000.0,
|
||||||
|
"capital_turnover_rate": 1000.0,
|
||||||
|
"current_liabilities": 1000.0,
|
||||||
|
"dividends": float("NaN"),
|
||||||
|
"net_income": float("NaN"),
|
||||||
|
"assets": 1000.0,
|
||||||
|
"long_term_debt": 1000.0,
|
||||||
|
"short_term_debt": 1000.0,
|
||||||
|
"revenue": 1000.0,
|
||||||
|
"cash_flow": 1000.0,
|
||||||
|
"current_assets": 1000.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"company_id": 1,
|
||||||
|
"date": datetime.date.fromisoformat("2022-01-01"),
|
||||||
|
"total_volume": 1100.0,
|
||||||
|
"ebit": 1100.0,
|
||||||
|
"ebitda": 1100.0,
|
||||||
|
"ebit_margin": 1100.0,
|
||||||
|
"total_balance": 1100.0,
|
||||||
|
"equity": 1100.0,
|
||||||
|
"debt": 1100.0,
|
||||||
|
"return_on_equity": 1100.0,
|
||||||
|
"capital_turnover_rate": 1100.0,
|
||||||
|
"current_liabilities": 1100.0,
|
||||||
|
"dividends": float("NaN"),
|
||||||
|
"net_income": float("NaN"),
|
||||||
|
"assets": 1100.0,
|
||||||
|
"long_term_debt": 1100.0,
|
||||||
|
"short_term_debt": 1100.0,
|
||||||
|
"revenue": 1100.0,
|
||||||
|
"cash_flow": 1100.0,
|
||||||
|
"current_assets": 1100.0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def full_db(empty_db: Session, finance_statements: list[dict[str, Any]]) -> Session:
|
||||||
"""Fills a db with some test data."""
|
"""Fills a db with some test data."""
|
||||||
empty_db.add_all(
|
empty_db.add_all(
|
||||||
[
|
[
|
||||||
@ -112,5 +166,13 @@ def full_db(empty_db: Session) -> Session:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
empty_db.commit()
|
empty_db.commit()
|
||||||
|
|
||||||
|
empty_db.add_all(
|
||||||
|
[
|
||||||
|
entities.AnnualFinanceStatement(**finance_statement)
|
||||||
|
for finance_statement in finance_statements
|
||||||
|
]
|
||||||
|
)
|
||||||
|
empty_db.commit()
|
||||||
# print(pd.read_sql_table("company", empty_db.bind).to_string())
|
# print(pd.read_sql_table("company", empty_db.bind).to_string())
|
||||||
return empty_db
|
return empty_db
|
||||||
|
118
tests/ui/ui_elements_test.py
Normal file
118
tests/ui/ui_elements_test.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
"""Tests for ui elements."""
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from aki_prj23_transparenzregister.ui import ui_elements
|
||||||
|
|
||||||
|
|
||||||
|
def test_import() -> None:
|
||||||
|
"""Checks if an import co ui_elements can be made."""
|
||||||
|
assert ui_elements is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_company_data(full_db: Session) -> None:
|
||||||
|
"""Checks if data from the company and district court tables can be accessed."""
|
||||||
|
company_df = ui_elements.get_company_data(full_db)
|
||||||
|
|
||||||
|
test_data = pd.DataFrame(
|
||||||
|
{
|
||||||
|
"company_id": {0: 1, 1: 2, 2: 3},
|
||||||
|
"company_hr": {0: "HRB 123", 1: "HRB 123", 2: "HRB 12"},
|
||||||
|
"company_court_id": {0: 2, 1: 1, 2: 2},
|
||||||
|
"company_name": {
|
||||||
|
0: "Some Company GmbH",
|
||||||
|
1: "Other Company GmbH",
|
||||||
|
2: "Third Company GmbH",
|
||||||
|
},
|
||||||
|
"company_street": {0: "Sesamstr.", 1: "Sesamstr.", 2: None},
|
||||||
|
"company_zip_code": {0: "12345", 1: "12345", 2: None},
|
||||||
|
"company_city": {0: "TV City", 1: "TV City", 2: None},
|
||||||
|
"company_last_update": {
|
||||||
|
0: "2023-01-01",
|
||||||
|
1: "2023-01-01",
|
||||||
|
2: "2023-01-01",
|
||||||
|
},
|
||||||
|
"company_sector": {0: None, 1: None, 2: None},
|
||||||
|
"district_court_name": {
|
||||||
|
0: "Amtsgericht Dortmund",
|
||||||
|
1: "Amtsgericht Bochum",
|
||||||
|
2: "Amtsgericht Dortmund",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
test_data = test_data.set_index("company_id")
|
||||||
|
pd.testing.assert_frame_equal(company_df, test_data)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_finance_data(full_db: Session) -> None:
|
||||||
|
"""Checks if data from the company and finance tables can be accessed."""
|
||||||
|
finance_df = ui_elements.get_finance_data(full_db)
|
||||||
|
test_data = pd.DataFrame(
|
||||||
|
{
|
||||||
|
"annual_finance_statement_id": {0: 1, 1: 2},
|
||||||
|
"annual_finance_statement_company_id": {0: 1, 1: 1},
|
||||||
|
"annual_finance_statement_date": {0: "2023-01-01", 1: "2022-01-01"},
|
||||||
|
"annual_finance_statement_total_volume": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_ebit": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_ebitda": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_ebit_margin": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_total_balance": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_equity": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_debt": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_return_on_equity": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_capital_turnover_rate": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_current_liabilities": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_dividends": {0: None, 1: None},
|
||||||
|
"annual_finance_statement_net_income": {0: None, 1: None},
|
||||||
|
"annual_finance_statement_assets": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_long_term_debt": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_short_term_debt": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_revenue": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_cash_flow": {0: 1000.0, 1: 1100.0},
|
||||||
|
"annual_finance_statement_current_assets": {0: 1000.0, 1: 1100.0},
|
||||||
|
"company_name": {0: "Some Company GmbH", 1: "Some Company GmbH"},
|
||||||
|
"company_id": {0: 1, 1: 1},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
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_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)
|
@ -168,7 +168,7 @@ def test_get_person_id_value_check(
|
|||||||
data_transfer.get_person_id(
|
data_transfer.get_person_id(
|
||||||
firstname,
|
firstname,
|
||||||
surname,
|
surname,
|
||||||
date.fromisoformat(date_str) if date_str else None, # type: ignore
|
date.fromisoformat(date_str) if date_str else None,
|
||||||
full_db,
|
full_db,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -941,7 +941,11 @@ def test_add_annual_report_to_unknown_company(
|
|||||||
@pytest.mark.parametrize("year", [2023, 2025, 2020])
|
@pytest.mark.parametrize("year", [2023, 2025, 2020])
|
||||||
@pytest.mark.parametrize("short_term_debt", [2023.2, 2025.5, 2020.5, float("NaN")])
|
@pytest.mark.parametrize("short_term_debt", [2023.2, 2025.5, 2020.5, float("NaN")])
|
||||||
def test_add_annual_report(
|
def test_add_annual_report(
|
||||||
short_term_debt: float, company_id: int, year: int, full_db: Session
|
short_term_debt: float,
|
||||||
|
company_id: int,
|
||||||
|
year: int,
|
||||||
|
finance_statements: list[dict[str, Any]],
|
||||||
|
full_db: Session,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Tests the addition of annual financial records."""
|
"""Tests the addition of annual financial records."""
|
||||||
data_transfer.add_annual_report(
|
data_transfer.add_annual_report(
|
||||||
@ -961,34 +965,38 @@ def test_add_annual_report(
|
|||||||
df_prior = pd.read_sql_table(
|
df_prior = pd.read_sql_table(
|
||||||
entities.AnnualFinanceStatement.__tablename__, full_db.bind # type: ignore
|
entities.AnnualFinanceStatement.__tablename__, full_db.bind # type: ignore
|
||||||
)
|
)
|
||||||
|
expected_results = pd.DataFrame(
|
||||||
|
finance_statements
|
||||||
|
+ [
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"company_id": company_id,
|
||||||
|
"date": pd.to_datetime(date(year, 1, 1)),
|
||||||
|
"total_volume": float("NaN"),
|
||||||
|
"ebit": 123.0,
|
||||||
|
"ebitda": 235.0,
|
||||||
|
"ebit_margin": float("NaN"),
|
||||||
|
"total_balance": float("NaN"),
|
||||||
|
"equity": float("NaN"),
|
||||||
|
"debt": float("NaN"),
|
||||||
|
"return_on_equity": float("NaN"),
|
||||||
|
"capital_turnover_rate": float("NaN"),
|
||||||
|
"current_liabilities": float("NaN"),
|
||||||
|
"dividends": float("NaN"),
|
||||||
|
"net_income": float("NaN"),
|
||||||
|
"assets": float("NaN"),
|
||||||
|
"long_term_debt": float("NaN"),
|
||||||
|
"short_term_debt": short_term_debt,
|
||||||
|
"revenue": float("NaN"),
|
||||||
|
"cash_flow": float("NaN"),
|
||||||
|
"current_assets": float("NaN"),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_results["date"] = pd.to_datetime(expected_results["date"])
|
||||||
pd.testing.assert_frame_equal(
|
pd.testing.assert_frame_equal(
|
||||||
pd.DataFrame(
|
expected_results,
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"company_id": company_id,
|
|
||||||
"date": pd.to_datetime(date(year, 1, 1)),
|
|
||||||
"total_volume": float("NaN"),
|
|
||||||
"ebit": 123.0,
|
|
||||||
"ebitda": 235.0,
|
|
||||||
"ebit_margin": float("NaN"),
|
|
||||||
"total_balance": float("NaN"),
|
|
||||||
"equity": float("NaN"),
|
|
||||||
"debt": float("NaN"),
|
|
||||||
"return_on_equity": float("NaN"),
|
|
||||||
"capital_turnover_rate": float("NaN"),
|
|
||||||
"current_liabilities": float("NaN"),
|
|
||||||
"dividends": float("NaN"),
|
|
||||||
"net_income": float("NaN"),
|
|
||||||
"assets": float("NaN"),
|
|
||||||
"long_term_debt": float("NaN"),
|
|
||||||
"short_term_debt": short_term_debt,
|
|
||||||
"revenue": float("NaN"),
|
|
||||||
"cash_flow": float("NaN"),
|
|
||||||
"current_assets": float("NaN"),
|
|
||||||
}
|
|
||||||
]
|
|
||||||
),
|
|
||||||
df_prior,
|
df_prior,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user