toolsos 0.1.4__py3-none-any.whl → 0.2.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toolsos/cbs_tools.py +36 -17
- toolsos/{database_connection.py → database/database_connection.py} +54 -13
- toolsos/geo.py +1 -1
- toolsos/huisstijl/graphs/bargraph.py +11 -1
- toolsos/huisstijl/graphs/graph_styles.py +15 -7
- toolsos/huisstijl/graphs/linegraph.py +18 -3
- toolsos/huisstijl/graphs/piegraph.py +5 -1
- toolsos/huisstijl/graphs/styler.py +132 -46
- toolsos/huisstijl/tables/table_helpers.py +76 -0
- toolsos/huisstijl/tables/tables.py +265 -116
- {toolsos-0.1.4.dist-info → toolsos-0.2.6.dist-info}/METADATA +28 -21
- toolsos-0.2.6.dist-info/RECORD +25 -0
- {toolsos-0.1.4.dist-info → toolsos-0.2.6.dist-info}/WHEEL +1 -1
- toolsos-0.1.4.dist-info/RECORD +0 -24
- /toolsos/{database_transfer.py → database/database_transfer.py} +0 -0
- {toolsos-0.1.4.dist-info → toolsos-0.2.6.dist-info}/top_level.txt +0 -0
toolsos/cbs_tools.py
CHANGED
|
@@ -2,8 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import pickle
|
|
5
|
+
from datetime import datetime
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from typing import TYPE_CHECKING, Iterator, Optional
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Iterator, Optional
|
|
7
8
|
|
|
8
9
|
import pandas as pd
|
|
9
10
|
import pyarrow as pa
|
|
@@ -14,18 +15,27 @@ if TYPE_CHECKING:
|
|
|
14
15
|
import pyreadstat
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
def get_batch_size(path, memory_limit):
|
|
19
|
+
df, _ = prs.read_sav(path, row_limit=1000)
|
|
20
|
+
|
|
21
|
+
# memory in megabytes
|
|
22
|
+
mem_size = df.memory_usage().sum() / 1_000_000
|
|
23
|
+
|
|
24
|
+
# The amount of blocks (of a thousand rows fit in the memory_limit)
|
|
25
|
+
n_blocks = memory_limit / mem_size
|
|
26
|
+
|
|
27
|
+
# Calculate the number of rows that fit within the memory limit
|
|
28
|
+
return round(n_blocks * 1000)
|
|
29
|
+
|
|
30
|
+
|
|
17
31
|
class SavToParquet:
|
|
18
32
|
def __init__(
|
|
19
|
-
self,
|
|
20
|
-
file: str,
|
|
21
|
-
folder_out: str,
|
|
22
|
-
chunksize: Optional[int] = None,
|
|
23
|
-
verbose: bool = False,
|
|
33
|
+
self, file: str, folder_out: str, verbose: bool = False, memory_limit=10_000
|
|
24
34
|
) -> None:
|
|
25
35
|
self.file = file
|
|
26
36
|
self.folder_out = folder_out
|
|
27
37
|
self.verbose = verbose
|
|
28
|
-
self.
|
|
38
|
+
self.memory_limit = memory_limit
|
|
29
39
|
|
|
30
40
|
@property
|
|
31
41
|
def path_out(self) -> str:
|
|
@@ -33,20 +43,27 @@ class SavToParquet:
|
|
|
33
43
|
|
|
34
44
|
@property
|
|
35
45
|
def chunks(self) -> Iterator[tuple["pyreadstat.metadata_container", pd.DataFrame]]:
|
|
36
|
-
return prs.read_file_in_chunks(
|
|
37
|
-
prs.read_sav, self.file, chunksize=self.chunksize
|
|
38
|
-
)
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
chunksize = get_batch_size(self.file, self.memory_limit)
|
|
48
|
+
|
|
49
|
+
if self.verbose:
|
|
50
|
+
print(f"Reading file in blocks of {chunksize} rows")
|
|
51
|
+
print("One such block should fit within the memory limit")
|
|
52
|
+
|
|
53
|
+
return prs.read_file_in_chunks(prs.read_sav, self.file, chunksize=chunksize)
|
|
42
54
|
|
|
43
55
|
def write_meta_to_json(self) -> None:
|
|
44
56
|
json_path = self.path_out.replace(".parquet", "_meta.json")
|
|
45
57
|
|
|
46
58
|
meta_dict = {}
|
|
47
|
-
for
|
|
48
|
-
if not
|
|
49
|
-
|
|
59
|
+
for attr_name in dir(self.meta):
|
|
60
|
+
if not attr_name.startswith("__"):
|
|
61
|
+
attr = getattr(self.meta, attr_name)
|
|
62
|
+
|
|
63
|
+
if isinstance(attr, datetime):
|
|
64
|
+
attr = attr.strftime("%Y-%m-%d %H:%M:%S")
|
|
65
|
+
|
|
66
|
+
meta_dict[attr_name] = attr
|
|
50
67
|
|
|
51
68
|
with open(json_path, "w") as file:
|
|
52
69
|
json.dump(meta_dict, file)
|
|
@@ -58,10 +75,12 @@ class SavToParquet:
|
|
|
58
75
|
pickle.dump(self.meta, file)
|
|
59
76
|
|
|
60
77
|
def write_to_parquet(self) -> None:
|
|
61
|
-
meta_df, self.meta = self.get_meta()
|
|
62
|
-
schema = table = pa.Table.from_pandas(meta_df).schema
|
|
63
78
|
|
|
64
79
|
print("Writing table")
|
|
80
|
+
|
|
81
|
+
line1, self.meta = prs.read_sav(self.file, row_limit=1)
|
|
82
|
+
schema = pa.Table.from_pandas(line1).schema
|
|
83
|
+
|
|
65
84
|
with pq.ParquetWriter(self.path_out, schema) as writer:
|
|
66
85
|
for idx, (df, _) in enumerate(self.chunks):
|
|
67
86
|
if self.verbose:
|
|
@@ -2,8 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import getpass
|
|
4
4
|
import json
|
|
5
|
+
import os
|
|
5
6
|
import subprocess
|
|
6
7
|
from json import JSONDecodeError
|
|
8
|
+
from pathlib import Path
|
|
7
9
|
from typing import Optional
|
|
8
10
|
|
|
9
11
|
import keyring
|
|
@@ -33,7 +35,7 @@ def get_db_connection_strings(
|
|
|
33
35
|
for dbname, params in db_info.items():
|
|
34
36
|
flush = dbname in reset_pw if reset_pw else False
|
|
35
37
|
|
|
36
|
-
if params["
|
|
38
|
+
if params["password"] == "access_token":
|
|
37
39
|
pw = get_azure_access_token()
|
|
38
40
|
else:
|
|
39
41
|
pw = get_pw_from_keyring(dbname=dbname, user=params["user"], reset_pw=flush)
|
|
@@ -94,21 +96,60 @@ def get_azure_access_token():
|
|
|
94
96
|
result = subprocess.run(command, capture_output=True, shell=True, text=True)
|
|
95
97
|
|
|
96
98
|
try:
|
|
97
|
-
json.loads(result.stdout)["accessToken"]
|
|
99
|
+
return json.loads(result.stdout)["accessToken"]
|
|
98
100
|
except JSONDecodeError:
|
|
99
101
|
subprocess.run("az login", shell=True)
|
|
100
102
|
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
def get_token_from_pgpass() -> None:
|
|
105
|
+
p = Path(os.getenv("APPDATA")) / "postgresql" / "pgpass.conf"
|
|
106
|
+
with open(p) as f:
|
|
107
|
+
token = f.readline().split(":")[4]
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
# engine_strings = get_db_connection_strings("python/database_config.yml")
|
|
108
|
-
# print(engine_strings.ruimte_analyse222)
|
|
109
|
+
return token
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
|
|
112
|
+
def write_pgpass(
|
|
113
|
+
host: str, port: str, database: str, user: str, path: str | None = None
|
|
114
|
+
) -> None:
|
|
115
|
+
password = get_azure_access_token()
|
|
116
|
+
conn_string = f"{host}:{port}:{database}:{user}:{password}"
|
|
117
|
+
|
|
118
|
+
if not path:
|
|
119
|
+
if os.name == "nt":
|
|
120
|
+
path = Path(os.getenv("APPDATA")) / "postgresql" / "pgpass.conf"
|
|
121
|
+
else:
|
|
122
|
+
path = Path("$home/.pgpass")
|
|
123
|
+
|
|
124
|
+
if not path.parent.exists():
|
|
125
|
+
path.parent.mkdir()
|
|
126
|
+
|
|
127
|
+
with open(path, "w") as f:
|
|
128
|
+
f.write(conn_string)
|
|
129
|
+
|
|
130
|
+
if os.name != "nt":
|
|
131
|
+
path.chmod("0600")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def write_multiple_pgpass(conn_details, path: str | None = None):
|
|
135
|
+
password = get_azure_access_token()
|
|
136
|
+
|
|
137
|
+
conn_strings = []
|
|
138
|
+
for c in conn_details:
|
|
139
|
+
c_string = f'{c["host"]}:{c["port"]}:{c["database"]}:{c["user"]}:{password}'
|
|
140
|
+
conn_strings.append(c_string)
|
|
141
|
+
|
|
142
|
+
if not path:
|
|
143
|
+
if os.name == "nt":
|
|
144
|
+
path = Path(os.getenv("APPDATA")) / "postgresql" / "pgpass.conf"
|
|
145
|
+
else:
|
|
146
|
+
path = Path("$home/.pgpass")
|
|
147
|
+
|
|
148
|
+
if not path.parent.exists():
|
|
149
|
+
path.parent.mkdir()
|
|
150
|
+
|
|
151
|
+
with open(path, "w") as f:
|
|
152
|
+
f.writelines(line + "\n" for line in conn_strings)
|
|
153
|
+
|
|
154
|
+
if os.name != "nt":
|
|
155
|
+
path.chmod("0600")
|
toolsos/geo.py
CHANGED
|
@@ -17,7 +17,7 @@ def get_geo_json(
|
|
|
17
17
|
Returns:
|
|
18
18
|
dict[str, str]: geo json containg of the desired level and year
|
|
19
19
|
"""
|
|
20
|
-
base_url = "https://gitlab.com/os-amsterdam/datavisualisatie-onderzoek-en-statistiek/-/raw/main/geo/"
|
|
20
|
+
base_url = "https://gitlab.com/os-amsterdam/datavisualisatie-onderzoek-en-statistiek/-/raw/main/public/geo/"
|
|
21
21
|
|
|
22
22
|
if mra:
|
|
23
23
|
level = f"{level}-mra"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import plotly.express as px
|
|
2
|
+
|
|
2
3
|
from .styler import BaseStyle
|
|
3
4
|
|
|
4
5
|
basestyle = BaseStyle()
|
|
@@ -14,6 +15,7 @@ def bar(
|
|
|
14
15
|
barmode=None,
|
|
15
16
|
width=750,
|
|
16
17
|
height=490,
|
|
18
|
+
font="Amsterdam Sans",
|
|
17
19
|
**kwargs,
|
|
18
20
|
):
|
|
19
21
|
fig = px.bar(
|
|
@@ -21,7 +23,7 @@ def bar(
|
|
|
21
23
|
x=x,
|
|
22
24
|
y=y,
|
|
23
25
|
color=color,
|
|
24
|
-
template=basestyle.get_base_template("bar", orientation=orientation),
|
|
26
|
+
template=basestyle.get_base_template("bar", orientation=orientation, font=font),
|
|
25
27
|
width=width,
|
|
26
28
|
color_discrete_sequence=color_discrete_sequence,
|
|
27
29
|
height=height,
|
|
@@ -43,6 +45,7 @@ def stacked_single(
|
|
|
43
45
|
color: str = None,
|
|
44
46
|
color_discrete_sequence: list = None,
|
|
45
47
|
orientation="v",
|
|
48
|
+
font="Amsterdam Sans",
|
|
46
49
|
**kwargs,
|
|
47
50
|
):
|
|
48
51
|
fig = bar(
|
|
@@ -53,6 +56,7 @@ def stacked_single(
|
|
|
53
56
|
color_discrete_sequence=color_discrete_sequence,
|
|
54
57
|
barmode="relative",
|
|
55
58
|
orientation=orientation,
|
|
59
|
+
font=font,
|
|
56
60
|
**kwargs,
|
|
57
61
|
)
|
|
58
62
|
|
|
@@ -71,6 +75,7 @@ def stacked_multiple(
|
|
|
71
75
|
color: str = None,
|
|
72
76
|
color_discrete_sequence: list = None,
|
|
73
77
|
orientation="v",
|
|
78
|
+
font="Amsterdam Sans",
|
|
74
79
|
**kwargs,
|
|
75
80
|
):
|
|
76
81
|
fig = bar(
|
|
@@ -81,6 +86,7 @@ def stacked_multiple(
|
|
|
81
86
|
color_discrete_sequence=color_discrete_sequence,
|
|
82
87
|
barmode="stack",
|
|
83
88
|
orientation=orientation,
|
|
89
|
+
font=font,
|
|
84
90
|
**kwargs,
|
|
85
91
|
)
|
|
86
92
|
|
|
@@ -94,6 +100,7 @@ def grouped(
|
|
|
94
100
|
color: str = None,
|
|
95
101
|
color_discrete_sequence: list = None,
|
|
96
102
|
orientation="v",
|
|
103
|
+
font="Amsterdam Sans",
|
|
97
104
|
**kwargs,
|
|
98
105
|
):
|
|
99
106
|
fig = bar(
|
|
@@ -104,6 +111,7 @@ def grouped(
|
|
|
104
111
|
color_discrete_sequence=color_discrete_sequence,
|
|
105
112
|
barmode="group",
|
|
106
113
|
orientation=orientation,
|
|
114
|
+
font=font,
|
|
107
115
|
**kwargs,
|
|
108
116
|
)
|
|
109
117
|
|
|
@@ -116,6 +124,7 @@ def single(
|
|
|
116
124
|
y: str,
|
|
117
125
|
color_discrete_sequence: list = None,
|
|
118
126
|
orientation="v",
|
|
127
|
+
font="Amsterdam Sans",
|
|
119
128
|
**kwargs,
|
|
120
129
|
):
|
|
121
130
|
fig = bar(
|
|
@@ -124,6 +133,7 @@ def single(
|
|
|
124
133
|
y=y,
|
|
125
134
|
color_discrete_sequence=color_discrete_sequence,
|
|
126
135
|
orientation=orientation,
|
|
136
|
+
font=font,
|
|
127
137
|
**kwargs,
|
|
128
138
|
)
|
|
129
139
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
font = "Amsterdam Sans ExtraBold, Corbel"
|
|
2
|
+
# font = "Arial"
|
|
3
|
+
|
|
1
4
|
STYLE_OLD = {
|
|
2
|
-
"font": {"family": "Corbel", "
|
|
5
|
+
"font": {"family": "Corbel", "size": 15},
|
|
3
6
|
"plot_bgcolor": "#FFFFFF",
|
|
4
7
|
"gridline_color": "#E8E8E8",
|
|
5
8
|
"gridlinewidth": 0.75,
|
|
@@ -12,13 +15,18 @@ STYLE_OLD = {
|
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
STYLE_NEW = {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
18
|
+
"font_bold": {"family": "Amsterdam Sans ExtraBold, Corbel", "size": 15},
|
|
19
|
+
"font_bold_corbel": {"family": "Corbel Bold", "size": 15},
|
|
20
|
+
"font": {"family": "Amsterdam Sans, Corbel", "size": 15},
|
|
21
|
+
"font_corbel": {"family": "Corbel", "size": 15},
|
|
22
|
+
"axis_font": {"family": "Amsterdam Sans ExtraBold, Corbel", "size": 15},
|
|
17
23
|
"plot_bgcolor": "#FFFFFF",
|
|
18
|
-
"gridline_color": "#
|
|
19
|
-
"
|
|
24
|
+
"gridline_color": "#dbdbdb",
|
|
25
|
+
"gridline_width": 0.75,
|
|
20
26
|
"showline": True,
|
|
21
27
|
"showgrid": False,
|
|
22
|
-
"zerolinecolor": "#
|
|
23
|
-
"gridcolor": "#
|
|
28
|
+
"zerolinecolor": "#dbdbdb",
|
|
29
|
+
"gridcolor": "#dbdbdb",
|
|
30
|
+
"gridlinecolor": "#dbdbdb",
|
|
31
|
+
"separators": ",",
|
|
24
32
|
}
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import plotly.express as px
|
|
2
|
-
from .styler import BaseStyle
|
|
3
2
|
|
|
3
|
+
from .styler import BaseStyle
|
|
4
4
|
|
|
5
5
|
basestyle = BaseStyle()
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def line(
|
|
8
|
+
def line(
|
|
9
|
+
data,
|
|
10
|
+
x,
|
|
11
|
+
y,
|
|
12
|
+
color: None,
|
|
13
|
+
width=750,
|
|
14
|
+
height=490,
|
|
15
|
+
color_discrete_sequence=None,
|
|
16
|
+
font="Amsterdam Sans",
|
|
17
|
+
**kwargs,
|
|
18
|
+
):
|
|
9
19
|
fig = px.line(
|
|
10
20
|
data_frame=data,
|
|
11
21
|
x=x,
|
|
@@ -13,8 +23,13 @@ def line(data, x, y, color: None, width=750, height=490, **kwargs):
|
|
|
13
23
|
color=color,
|
|
14
24
|
width=width,
|
|
15
25
|
height=height,
|
|
16
|
-
|
|
26
|
+
color_discrete_sequence=color_discrete_sequence,
|
|
27
|
+
template=BaseStyle().get_base_template(graph_type="line", font=font),
|
|
17
28
|
**kwargs,
|
|
18
29
|
)
|
|
19
30
|
|
|
31
|
+
fig.update_layout(
|
|
32
|
+
dict(xaxis_title_text="", yaxis_title_text="", legend_title_text="")
|
|
33
|
+
)
|
|
34
|
+
|
|
20
35
|
return fig
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import plotly.express as px
|
|
2
|
+
|
|
2
3
|
from .styler import BaseStyle
|
|
3
4
|
|
|
4
5
|
basestyle = BaseStyle()
|
|
@@ -12,6 +13,8 @@ def pie(
|
|
|
12
13
|
width=750,
|
|
13
14
|
height=490,
|
|
14
15
|
text_format: str = None,
|
|
16
|
+
color_discrete_sequence=None,
|
|
17
|
+
font="Amsterdam Sans",
|
|
15
18
|
**kwargs,
|
|
16
19
|
):
|
|
17
20
|
fig = px.pie(
|
|
@@ -21,7 +24,8 @@ def pie(
|
|
|
21
24
|
width=width,
|
|
22
25
|
height=height,
|
|
23
26
|
hole=hole,
|
|
24
|
-
template=BaseStyle().get_base_template(),
|
|
27
|
+
template=BaseStyle().get_base_template(font=font),
|
|
28
|
+
color_discrete_sequence=color_discrete_sequence,
|
|
25
29
|
**kwargs,
|
|
26
30
|
)
|
|
27
31
|
|
|
@@ -4,56 +4,153 @@ import plotly.graph_objects as go
|
|
|
4
4
|
import requests
|
|
5
5
|
from requests import ConnectionError
|
|
6
6
|
|
|
7
|
+
from .graph_styles import STYLE_NEW
|
|
8
|
+
|
|
9
|
+
# class BaseStyle:
|
|
10
|
+
# style_url = (
|
|
11
|
+
# "https://raw.githubusercontent.com/jbosga-ams/oistyle/main/base_style.json"
|
|
12
|
+
# )
|
|
13
|
+
|
|
14
|
+
# def __init__(self):
|
|
15
|
+
# self.grab_styling()
|
|
16
|
+
|
|
17
|
+
# def grab_styling(self, style_path: str = None):
|
|
18
|
+
# if not style_path:
|
|
19
|
+
# try:
|
|
20
|
+
# res = requests.get(self.style_url).json()
|
|
21
|
+
# except ConnectionError:
|
|
22
|
+
# print("Failed grabbing basestyle from the interwebs")
|
|
23
|
+
# # Add option to manually provide json file
|
|
24
|
+
# else:
|
|
25
|
+
# res = json.loads()
|
|
26
|
+
|
|
27
|
+
# for k, v in res.items():
|
|
28
|
+
# setattr(self, k, v)
|
|
29
|
+
|
|
30
|
+
# self.font = "Amsterdam Sans ExtraBold, Corbel"
|
|
31
|
+
|
|
32
|
+
# def _get_axis_format(self):
|
|
33
|
+
# self.gridline_color = "#dbdbdb" # Jorren vragen om deze aan te passen
|
|
34
|
+
|
|
35
|
+
# return {
|
|
36
|
+
# "zerolinecolor": self.gridline_color,
|
|
37
|
+
# "gridcolor": self.gridline_color,
|
|
38
|
+
# "gridwidth": self.gridline_width,
|
|
39
|
+
# "showline": True,
|
|
40
|
+
# "linewidth": self.gridline_width,
|
|
41
|
+
# "linecolor": self.gridline_color,
|
|
42
|
+
# # "mirror": True,
|
|
43
|
+
# "showgrid": False,
|
|
44
|
+
# }
|
|
45
|
+
|
|
46
|
+
# def _get_base_template_layout(self):
|
|
47
|
+
# return go.layout.Template(
|
|
48
|
+
# layout={
|
|
49
|
+
# "font": {"family": self.font, "size": self.font_size},
|
|
50
|
+
# "plot_bgcolor": self.plot_bgcolor,
|
|
51
|
+
# "colorway": self.colors["darkblue_lightblue_gradient_5"],
|
|
52
|
+
# "separators": ",", # Jorren vragen om deze toe te voegen
|
|
53
|
+
# }
|
|
54
|
+
# )
|
|
55
|
+
|
|
56
|
+
# def get_base_template(
|
|
57
|
+
# self, graph_type: str = None, orientation: str = None, colors: str = None
|
|
58
|
+
# ):
|
|
59
|
+
# """[summary]
|
|
60
|
+
|
|
61
|
+
# Args:
|
|
62
|
+
# graph_type (str, optional): Pick 'bar', 'line' or 'bar'. Defaults to None.
|
|
63
|
+
# orientation (str, optional): [description]. Pick horizontal ('h') or vertical 'v'. Defaults to None.
|
|
64
|
+
# colors (str, optional): Set basecolors. Defaults to None.
|
|
65
|
+
|
|
66
|
+
# Raises:
|
|
67
|
+
# ValueError: [description]
|
|
68
|
+
|
|
69
|
+
# Returns:
|
|
70
|
+
# [type]: [description]
|
|
71
|
+
# """
|
|
72
|
+
# base_template = self._get_base_template_layout()
|
|
73
|
+
# axis_format = self._get_axis_format()
|
|
74
|
+
|
|
75
|
+
# if graph_type == "bar":
|
|
76
|
+
# if orientation in ["v", "vertical"]:
|
|
77
|
+
# base_template.layout.xaxis.update(axis_format)
|
|
78
|
+
# base_template.layout.yaxis.update(zeroline=False)
|
|
79
|
+
# elif orientation in ["h", "horizontal"]:
|
|
80
|
+
# base_template.layout.yaxis.update(axis_format)
|
|
81
|
+
# base_template.layout.xaxis.update(zeroline=False)
|
|
82
|
+
# else:
|
|
83
|
+
# raise ValueError(
|
|
84
|
+
# "Orientation ('v'/'vertical' or 'h'/'horizontal') should be supplied with graph_type=='bar'"
|
|
85
|
+
# )
|
|
86
|
+
|
|
87
|
+
# elif graph_type == "line":
|
|
88
|
+
# base_template.layout.xaxis.update(axis_format)
|
|
89
|
+
|
|
90
|
+
# if colors:
|
|
91
|
+
# base_template.layout.update({"colorway": colors})
|
|
92
|
+
|
|
93
|
+
# return base_template
|
|
94
|
+
|
|
95
|
+
# def get_ois_colors(self, colorscale):
|
|
96
|
+
# colorscale = self.colors.get(colorscale, [])
|
|
97
|
+
# if not colorscale:
|
|
98
|
+
# raise Exception(f"Kies uit {self.colors.keys()}")
|
|
99
|
+
|
|
100
|
+
# return colorscale
|
|
101
|
+
|
|
7
102
|
|
|
8
103
|
class BaseStyle:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def __init__(self):
|
|
14
|
-
self.grab_styling()
|
|
15
|
-
|
|
16
|
-
def grab_styling(self, style_path: str = None):
|
|
17
|
-
if not style_path:
|
|
18
|
-
try:
|
|
19
|
-
res = requests.get(self.style_url).json()
|
|
20
|
-
except ConnectionError:
|
|
21
|
-
print("Failed grabbing basestyle from the interwebs")
|
|
22
|
-
# Add option to manually provide json file
|
|
104
|
+
def __init__(self, style_path=None):
|
|
105
|
+
if style_path is None:
|
|
106
|
+
self.style = STYLE_NEW
|
|
23
107
|
else:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
for k, v in res.items():
|
|
27
|
-
setattr(self, k, v)
|
|
28
|
-
|
|
29
|
-
self.font = "Amsterdam Sans ExtraBold, Corbel"
|
|
108
|
+
with open(style_path) as file:
|
|
109
|
+
self.style = json.load(file)
|
|
30
110
|
|
|
31
111
|
def _get_axis_format(self):
|
|
32
|
-
self.gridline_color = "#dbdbdb" # Jorren vragen om deze aan te passen
|
|
33
112
|
|
|
34
113
|
return {
|
|
35
|
-
"zerolinecolor": self.gridline_color,
|
|
36
|
-
"gridcolor": self.gridline_color,
|
|
37
|
-
"gridwidth": self.gridline_width,
|
|
114
|
+
"zerolinecolor": self.style["gridline_color"],
|
|
115
|
+
"gridcolor": self.style["gridline_color"],
|
|
116
|
+
"gridwidth": self.style["gridline_width"],
|
|
38
117
|
"showline": True,
|
|
39
|
-
"linewidth": self.gridline_width,
|
|
40
|
-
"linecolor": self.gridline_color,
|
|
41
|
-
|
|
42
|
-
"showgrid": False,
|
|
118
|
+
"linewidth": self.style["gridline_width"],
|
|
119
|
+
"linecolor": self.style["gridline_color"],
|
|
120
|
+
"showgrid": self.style["showgrid"],
|
|
43
121
|
}
|
|
44
122
|
|
|
45
|
-
def _get_base_template_layout(self):
|
|
123
|
+
def _get_base_template_layout(self, font):
|
|
124
|
+
if font == "Amsterdam Sans":
|
|
125
|
+
font_ = self.style["font"]
|
|
126
|
+
font_bold_ = self.style["font_bold"]
|
|
127
|
+
elif font == "Corbel":
|
|
128
|
+
font_ = self.style["font_corbel"]
|
|
129
|
+
font_bold_ = self.style["font_bold_corbel"]
|
|
130
|
+
else:
|
|
131
|
+
raise ValueError("Font should be 'Amsterdam Sans' or 'Corbel'")
|
|
132
|
+
|
|
46
133
|
return go.layout.Template(
|
|
47
134
|
layout={
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
135
|
+
"xaxis": {
|
|
136
|
+
"tickfont": font_bold_,
|
|
137
|
+
},
|
|
138
|
+
"yaxis": {
|
|
139
|
+
"tickfont": font_bold_,
|
|
140
|
+
},
|
|
141
|
+
"legend": {"font": font_},
|
|
142
|
+
"plot_bgcolor": self.style["plot_bgcolor"],
|
|
143
|
+
"separators": ",",
|
|
144
|
+
"font": font_bold_,
|
|
52
145
|
}
|
|
53
146
|
)
|
|
54
147
|
|
|
55
148
|
def get_base_template(
|
|
56
|
-
self,
|
|
149
|
+
self,
|
|
150
|
+
graph_type: str = None,
|
|
151
|
+
orientation: str = None,
|
|
152
|
+
colors: str = None,
|
|
153
|
+
font: str = "Amsterdam Sans",
|
|
57
154
|
):
|
|
58
155
|
"""[summary]
|
|
59
156
|
|
|
@@ -68,7 +165,7 @@ class BaseStyle:
|
|
|
68
165
|
Returns:
|
|
69
166
|
[type]: [description]
|
|
70
167
|
"""
|
|
71
|
-
base_template = self._get_base_template_layout()
|
|
168
|
+
base_template = self._get_base_template_layout(font)
|
|
72
169
|
axis_format = self._get_axis_format()
|
|
73
170
|
|
|
74
171
|
if graph_type == "bar":
|
|
@@ -90,14 +187,3 @@ class BaseStyle:
|
|
|
90
187
|
base_template.layout.update({"colorway": colors})
|
|
91
188
|
|
|
92
189
|
return base_template
|
|
93
|
-
|
|
94
|
-
def get_ois_colors(self, colorscale):
|
|
95
|
-
colorscale = self.colors.get(colorscale, [])
|
|
96
|
-
if not colorscale:
|
|
97
|
-
raise Exception(f"Kies uit {self.colors.keys()}")
|
|
98
|
-
|
|
99
|
-
return colorscale
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
class Basestyle2:
|
|
103
|
-
def __init__(self, style): ...
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import win32com.client as win32
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def remove_underscores_from_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
8
|
+
df.columns = df.columns.str.replace("_", " ")
|
|
9
|
+
return df
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_excel_files_from_folder(folder: str) -> list[str]:
|
|
13
|
+
return [str(f.resolve()) for f in Path("to_merge").glob("*") if f.suffix == ".xlsx"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def combine_excel_files(out_path: str, files: list[str] = None, overwrite: bool = True):
|
|
17
|
+
out_path = Path(out_path)
|
|
18
|
+
|
|
19
|
+
if overwrite:
|
|
20
|
+
if out_path.exists():
|
|
21
|
+
out_path.unlink()
|
|
22
|
+
|
|
23
|
+
# INITIALIZE EXCEL COM APP
|
|
24
|
+
try:
|
|
25
|
+
xlapp = win32.gencache.EnsureDispatch("Excel.Application")
|
|
26
|
+
|
|
27
|
+
# constants
|
|
28
|
+
xlPasteValues = -4163
|
|
29
|
+
lPasteFormats = -4122
|
|
30
|
+
xlWorkbookDefault = 51
|
|
31
|
+
|
|
32
|
+
# create new workbook
|
|
33
|
+
new_wb = xlapp.Workbooks.Add()
|
|
34
|
+
new_wb.SaveAs(Filename=str(out_path), FileFormat=xlWorkbookDefault)
|
|
35
|
+
|
|
36
|
+
dup_count = 1
|
|
37
|
+
|
|
38
|
+
for wb in files:
|
|
39
|
+
xlwb = xlapp.Workbooks.Open(wb)
|
|
40
|
+
|
|
41
|
+
for xlsh in xlwb.Worksheets:
|
|
42
|
+
new_sh = new_wb.Worksheets.Add()
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
new_sh.Name = xlsh.Name
|
|
46
|
+
|
|
47
|
+
# Ugly non defined exception. Be aware that this wil caputre
|
|
48
|
+
except Exception as e:
|
|
49
|
+
new_sh.Name = f"{xlsh.Name}_{dup_count}"
|
|
50
|
+
dup_count += 1
|
|
51
|
+
|
|
52
|
+
new_wb.Save()
|
|
53
|
+
new_sh.Move(After=new_wb.Worksheets(new_wb.Worksheets.Count))
|
|
54
|
+
|
|
55
|
+
xlsh.Cells.Copy(new_sh.Cells)
|
|
56
|
+
new_sh = None
|
|
57
|
+
|
|
58
|
+
xlwb.Close(True)
|
|
59
|
+
xlwb = None
|
|
60
|
+
|
|
61
|
+
# remove default blad1
|
|
62
|
+
new_wb.Worksheets("Blad1").Delete()
|
|
63
|
+
new_wb.Save()
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
print(e)
|
|
67
|
+
|
|
68
|
+
# RELEASE RESOURCES
|
|
69
|
+
finally:
|
|
70
|
+
xlsh = None
|
|
71
|
+
new_sh = None
|
|
72
|
+
xlwb = None
|
|
73
|
+
new_wb = None
|
|
74
|
+
xlapp.Quit()
|
|
75
|
+
xlapp = None
|
|
76
|
+
xlwb = None
|
|
@@ -46,6 +46,38 @@ def set_global_style(style: str) -> None:
|
|
|
46
46
|
STYLES = STYLE_NEW
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
def cols_to_str(df: pd.DataFrame) -> pd.DataFrame:
|
|
50
|
+
"""Change column names in to string. Multiindex column names are nog changed because
|
|
51
|
+
these are always strings
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
df (pd.DataFrame): Dataframe
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
pd.DataFrame: Dataframe with column names as strings
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
# Multiindex columns are always strings and therefore can't be casted as string
|
|
61
|
+
if df.columns.nlevels == 1:
|
|
62
|
+
df.columns = df.columns.astype(str)
|
|
63
|
+
|
|
64
|
+
return df
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_max_col_widths(data: pd.DataFrame | np.ndarray) -> list[float]:
|
|
68
|
+
col_widths = []
|
|
69
|
+
if isinstance(data, pd.DataFrame):
|
|
70
|
+
for col in zip(*flatten_multiindex_columns(data)):
|
|
71
|
+
col_widths.append(max(len(e) for e in col))
|
|
72
|
+
else:
|
|
73
|
+
for col in zip(*data):
|
|
74
|
+
col_widths.append(max(len(str(e)) for e in col))
|
|
75
|
+
|
|
76
|
+
col_widths = [col_width * 1.13 for col_width in col_widths]
|
|
77
|
+
|
|
78
|
+
return col_widths
|
|
79
|
+
|
|
80
|
+
|
|
49
81
|
def flatten_multiindex_columns(df):
|
|
50
82
|
column_multi = []
|
|
51
83
|
for level in range(df.columns.nlevels):
|
|
@@ -67,20 +99,32 @@ def df_to_array(df: pd.DataFrame) -> np.ndarray:
|
|
|
67
99
|
return np.vstack([column_names, df.to_numpy()])
|
|
68
100
|
|
|
69
101
|
|
|
70
|
-
def get_cells_to_merge(df
|
|
71
|
-
|
|
72
|
-
|
|
102
|
+
def get_cells_to_merge(df: pd.DataFrame) -> dict[int : list[int, int]]:
|
|
103
|
+
"""Pandas dataframes sometimes have mutliindex columns. For all but the last level
|
|
104
|
+
a dictionary is created to merge the cells. The last level isn't merged because these
|
|
105
|
+
are these contain unique column names
|
|
73
106
|
|
|
74
|
-
|
|
75
|
-
|
|
107
|
+
Args:
|
|
108
|
+
df (pd.DataFrame): Pandas dataframe. If the dataframe has multicolumn indices
|
|
109
|
+
a dictionary containg the cells to merge is returned
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
dict[int: list[int, int]]: Dictionary containg the cells to merge
|
|
113
|
+
"""
|
|
114
|
+
levels = flatten_multiindex_columns(df)[:-1]
|
|
76
115
|
|
|
77
|
-
|
|
116
|
+
cells_to_merge = {}
|
|
117
|
+
for level_idx, level in enumerate(levels):
|
|
118
|
+
start_col = 1
|
|
119
|
+
start_col = 1
|
|
120
|
+
merge_cells = []
|
|
78
121
|
for _, val in groupby(level):
|
|
79
122
|
val = list(val)
|
|
80
123
|
merge_cells.append([start_col, start_col + len(val) - 1])
|
|
81
124
|
start_col += len(val)
|
|
125
|
+
cells_to_merge[level_idx] = merge_cells
|
|
82
126
|
|
|
83
|
-
return
|
|
127
|
+
return cells_to_merge
|
|
84
128
|
|
|
85
129
|
|
|
86
130
|
def get_fmt_table(arr: np.ndarray) -> Fmt:
|
|
@@ -329,14 +373,15 @@ def cell_formatting(
|
|
|
329
373
|
return fmt
|
|
330
374
|
|
|
331
375
|
|
|
332
|
-
def
|
|
376
|
+
def write_to_worksheet(
|
|
333
377
|
ws: Any,
|
|
334
378
|
arr: np.ndarray,
|
|
335
379
|
fmt: Fmt,
|
|
336
380
|
title: str | None = None,
|
|
337
381
|
source: str | None = None,
|
|
338
382
|
col_filter: bool | None = None,
|
|
339
|
-
|
|
383
|
+
col_widths: list | None = None,
|
|
384
|
+
min_column_width: int | None = None,
|
|
340
385
|
cells_to_merge: list[list[int]] | None = None,
|
|
341
386
|
) -> None:
|
|
342
387
|
"""Writing data to worksheet. Used for writing values to cells and formatting the cells
|
|
@@ -377,51 +422,61 @@ def write_worksheet(
|
|
|
377
422
|
filters = ws.auto_filter
|
|
378
423
|
filters.ref = f"A1:{excel_style(len(fmt), len(fmt[0]))}"
|
|
379
424
|
|
|
380
|
-
if autofit_columns:
|
|
381
|
-
_autofit_columns(ws)
|
|
382
|
-
|
|
383
425
|
if source:
|
|
384
426
|
_insert_source(ws, source, arr)
|
|
385
427
|
|
|
428
|
+
if col_widths:
|
|
429
|
+
_set_column_widths(ws, col_widths, min_column_width)
|
|
430
|
+
|
|
386
431
|
if title:
|
|
387
432
|
_insert_title(ws, title)
|
|
388
433
|
|
|
389
434
|
if cells_to_merge:
|
|
390
|
-
_merge_cells(ws, cells_to_merge)
|
|
435
|
+
_merge_cells(ws, cells_to_merge, title)
|
|
391
436
|
|
|
392
437
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
438
|
+
def _set_column_widths(
|
|
439
|
+
ws: Any, col_widths: list[int], min_column_width: int | None
|
|
440
|
+
) -> None:
|
|
441
|
+
for idx, col_width in enumerate(col_widths):
|
|
442
|
+
col_letter = get_column_letter(idx + 1)
|
|
398
443
|
|
|
444
|
+
if min_column_width:
|
|
445
|
+
if col_width < min_column_width:
|
|
446
|
+
col_width = min_column_width
|
|
447
|
+
|
|
448
|
+
ws.column_dimensions[col_letter].width = col_width
|
|
399
449
|
|
|
400
|
-
def _autofit_columns(ws: Any) -> None:
|
|
401
|
-
column_letters = tuple(
|
|
402
|
-
get_column_letter(col_number + 1) for col_number in range(ws.max_column)
|
|
403
|
-
)
|
|
404
|
-
for column_letter in column_letters:
|
|
405
|
-
ws.column_dimensions[column_letter].auto_fit = True
|
|
406
450
|
|
|
451
|
+
def _merge_cells(ws, cells_to_merge, title: str | None = None) -> None:
|
|
452
|
+
add = 0
|
|
453
|
+
if title:
|
|
454
|
+
add = 1
|
|
407
455
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
456
|
+
for row_idx, merge in cells_to_merge.items():
|
|
457
|
+
row_idx = row_idx + add
|
|
458
|
+
for start, stop in merge:
|
|
459
|
+
cell = ws.cell(row_idx + 1, start)
|
|
460
|
+
cell.alignment = Alignment(horizontal="center")
|
|
461
|
+
ws.merge_cells(
|
|
462
|
+
start_row=row_idx + 1,
|
|
463
|
+
end_row=row_idx + 1,
|
|
464
|
+
start_column=start,
|
|
465
|
+
end_column=stop,
|
|
466
|
+
)
|
|
413
467
|
|
|
414
468
|
|
|
415
469
|
def _insert_source(ws, source, arr):
|
|
416
470
|
height, width = arr.shape
|
|
417
|
-
cell = ws.cell(
|
|
418
|
-
cell.font = Font(**STYLES["calibri"]["font"])
|
|
471
|
+
cell = ws.cell(height + 1, width, source)
|
|
419
472
|
cell.alignment = Alignment(horizontal="right")
|
|
473
|
+
cell.font = Font(**STYLES["calibri"]["font"])
|
|
420
474
|
|
|
421
475
|
|
|
422
476
|
def _insert_title(ws: Any, title: str) -> None:
|
|
423
477
|
ws.insert_rows(0)
|
|
424
478
|
cell = ws.cell(1, 1, title)
|
|
479
|
+
cell.alignment = Alignment(horizontal="left")
|
|
425
480
|
for t, kwa in STYLES["title_bold"].items():
|
|
426
481
|
setattr(cell, t, LOOKUP[t](**kwa))
|
|
427
482
|
|
|
@@ -447,10 +502,99 @@ def write_table(
|
|
|
447
502
|
blue_border: bool | None = True,
|
|
448
503
|
blue_border_row_ids: int | list[int] | None = None,
|
|
449
504
|
number_format: str = "0.0",
|
|
450
|
-
autofit_columns:
|
|
505
|
+
autofit_columns: str | None = "column_names",
|
|
506
|
+
min_column_width: int | None = None,
|
|
451
507
|
col_filter: bool | None = False,
|
|
452
508
|
style: str = "old",
|
|
453
|
-
|
|
509
|
+
combine_multiindex: bool | int = False,
|
|
510
|
+
column_names_to_string: bool = True,
|
|
511
|
+
):
|
|
512
|
+
wb = Workbook()
|
|
513
|
+
# Empty sheet is created on Workbook creation
|
|
514
|
+
del wb["Sheet"]
|
|
515
|
+
|
|
516
|
+
set_global_style(style)
|
|
517
|
+
|
|
518
|
+
if not isinstance(data, dict):
|
|
519
|
+
data = {"Sheet1": data}
|
|
520
|
+
|
|
521
|
+
for sheet_name, df in data.items():
|
|
522
|
+
format_worksheet(
|
|
523
|
+
wb=wb,
|
|
524
|
+
df=df,
|
|
525
|
+
sheet_name=sheet_name,
|
|
526
|
+
header_row=header_row,
|
|
527
|
+
title=title,
|
|
528
|
+
source=source,
|
|
529
|
+
total_row=total_row,
|
|
530
|
+
light_blue_row_ids=light_blue_row_ids,
|
|
531
|
+
total_col=total_col,
|
|
532
|
+
right_align_ids=right_align_ids,
|
|
533
|
+
right_align_pattern=right_align_pattern,
|
|
534
|
+
right_align_numeric=right_align_numeric,
|
|
535
|
+
left_align_ids=left_align_ids,
|
|
536
|
+
left_align_pattern=left_align_pattern,
|
|
537
|
+
left_align_string=left_align_string,
|
|
538
|
+
perc_ids=perc_ids,
|
|
539
|
+
perc_pattern=perc_pattern,
|
|
540
|
+
perc_col_format=perc_col_format,
|
|
541
|
+
blue_border=blue_border,
|
|
542
|
+
blue_border_row_ids=blue_border_row_ids,
|
|
543
|
+
number_format=number_format,
|
|
544
|
+
autofit_columns=autofit_columns,
|
|
545
|
+
min_column_width=min_column_width,
|
|
546
|
+
col_filter=col_filter,
|
|
547
|
+
combine_multiindex=combine_multiindex,
|
|
548
|
+
column_names_to_string=column_names_to_string,
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
wb.save(file)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def write_table_from_dict(
|
|
555
|
+
file,
|
|
556
|
+
write_info,
|
|
557
|
+
style: str = "old",
|
|
558
|
+
):
|
|
559
|
+
wb = Workbook()
|
|
560
|
+
# Empty sheet is created on Workbook creation
|
|
561
|
+
del wb["Sheet"]
|
|
562
|
+
|
|
563
|
+
set_global_style(style)
|
|
564
|
+
|
|
565
|
+
for sheet in write_info:
|
|
566
|
+
format_worksheet(wb=wb, **sheet)
|
|
567
|
+
|
|
568
|
+
wb.save(file)
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def format_worksheet(
|
|
572
|
+
wb: Any,
|
|
573
|
+
df: pd.DataFrame,
|
|
574
|
+
sheet_name: str,
|
|
575
|
+
header_row: int = 0,
|
|
576
|
+
title: str | dict[str, str] | None = None,
|
|
577
|
+
source: str | None = None,
|
|
578
|
+
total_row: bool | None = None,
|
|
579
|
+
light_blue_row_ids: int | list[int] | None = None,
|
|
580
|
+
total_col: bool | None = None,
|
|
581
|
+
right_align_ids: list | None = None,
|
|
582
|
+
right_align_pattern: str | None = None,
|
|
583
|
+
right_align_numeric: bool | None = True,
|
|
584
|
+
left_align_ids: list | None = None,
|
|
585
|
+
left_align_pattern: str | None = None,
|
|
586
|
+
left_align_string: bool | None = True,
|
|
587
|
+
perc_ids: list | None = None,
|
|
588
|
+
perc_pattern: str | None = None,
|
|
589
|
+
perc_col_format: str | None = None,
|
|
590
|
+
blue_border: bool | None = True,
|
|
591
|
+
blue_border_row_ids: int | list[int] | None = None,
|
|
592
|
+
number_format: str = "0.0",
|
|
593
|
+
autofit_columns: str | None = "column_names",
|
|
594
|
+
min_column_width: int | None = None,
|
|
595
|
+
col_filter: bool | None = False,
|
|
596
|
+
combine_multiindex: bool | int = False,
|
|
597
|
+
column_names_to_string: bool = True,
|
|
454
598
|
):
|
|
455
599
|
"""_summary_
|
|
456
600
|
|
|
@@ -473,107 +617,112 @@ def write_table(
|
|
|
473
617
|
perc_col_format (str, optional): The formatting string of percentage columns. Defaults to None.
|
|
474
618
|
col_filter (bool, optional): Set filter on columns. Defaults to False.
|
|
475
619
|
"""
|
|
620
|
+
if column_names_to_string == True:
|
|
621
|
+
df = cols_to_str(df)
|
|
476
622
|
|
|
477
|
-
|
|
478
|
-
# Empty sheet is created on Workbook creation
|
|
479
|
-
del wb["Sheet"]
|
|
480
|
-
|
|
481
|
-
set_global_style(style)
|
|
623
|
+
arr = df_to_array(df)
|
|
482
624
|
|
|
483
|
-
|
|
484
|
-
|
|
625
|
+
blue_rows = []
|
|
626
|
+
light_blue_rows = []
|
|
627
|
+
light_blue_cols = []
|
|
628
|
+
blue_border_ids = []
|
|
629
|
+
r_align_ids = []
|
|
630
|
+
l_align_ids = []
|
|
631
|
+
p_ids = []
|
|
632
|
+
cells_to_merge = []
|
|
633
|
+
title_tbl = None
|
|
634
|
+
title_src = None
|
|
485
635
|
|
|
486
|
-
|
|
487
|
-
|
|
636
|
+
if isinstance(header_row, int):
|
|
637
|
+
blue_rows.extend(list(range(0, header_row + 1)))
|
|
488
638
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
l_align_ids = []
|
|
495
|
-
p_ids = []
|
|
496
|
-
cells_to_merge = []
|
|
497
|
-
title_tbl = None
|
|
498
|
-
|
|
499
|
-
if isinstance(header_row, int):
|
|
500
|
-
blue_rows.extend(list(range(0, header_row + 1)))
|
|
501
|
-
|
|
502
|
-
if title:
|
|
503
|
-
if isinstance(title, str):
|
|
504
|
-
title_tbl = title
|
|
505
|
-
elif isinstance(title, dict):
|
|
506
|
-
title_tbl = title.get(sheet_name)
|
|
507
|
-
|
|
508
|
-
if right_align_ids:
|
|
509
|
-
r_align_ids.extend(right_align_ids)
|
|
639
|
+
if title:
|
|
640
|
+
if isinstance(title, str):
|
|
641
|
+
title_tbl = title
|
|
642
|
+
elif isinstance(title, dict):
|
|
643
|
+
title_tbl = title.get(sheet_name)
|
|
510
644
|
|
|
511
|
-
|
|
512
|
-
|
|
645
|
+
if source:
|
|
646
|
+
if isinstance(source, str):
|
|
647
|
+
title_src = source
|
|
648
|
+
elif isinstance(title, dict):
|
|
649
|
+
title_src = source.get(sheet_name)
|
|
513
650
|
|
|
514
|
-
|
|
515
|
-
|
|
651
|
+
if right_align_ids:
|
|
652
|
+
r_align_ids.extend(right_align_ids)
|
|
516
653
|
|
|
517
|
-
|
|
518
|
-
|
|
654
|
+
if right_align_pattern:
|
|
655
|
+
r_align_ids.extend(get_cols_id_with_pattern(df, right_align_pattern))
|
|
519
656
|
|
|
520
|
-
|
|
521
|
-
|
|
657
|
+
if right_align_numeric:
|
|
658
|
+
r_align_ids.extend(get_numeric_col_ids(df))
|
|
522
659
|
|
|
523
|
-
|
|
524
|
-
|
|
660
|
+
if left_align_ids:
|
|
661
|
+
r_align_ids.extend(left_align_ids)
|
|
525
662
|
|
|
526
|
-
|
|
527
|
-
|
|
663
|
+
if left_align_pattern:
|
|
664
|
+
l_align_ids.extend(get_cols_id_with_pattern(df, left_align_pattern))
|
|
528
665
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
p_ids.extend(r_id)
|
|
532
|
-
r_align_ids.extend(r_id)
|
|
666
|
+
if left_align_string:
|
|
667
|
+
l_align_ids.extend(get_string_cols_ids(df))
|
|
533
668
|
|
|
534
|
-
|
|
535
|
-
|
|
669
|
+
if perc_ids:
|
|
670
|
+
p_ids.extend(perc_ids)
|
|
536
671
|
|
|
537
|
-
|
|
538
|
-
|
|
672
|
+
if perc_pattern:
|
|
673
|
+
r_id = get_cols_id_with_pattern(df, perc_pattern)
|
|
674
|
+
p_ids.extend(r_id)
|
|
675
|
+
r_align_ids.extend(r_id)
|
|
539
676
|
|
|
540
|
-
|
|
541
|
-
|
|
677
|
+
if total_row:
|
|
678
|
+
light_blue_rows.append(arr.shape[0] - 1)
|
|
542
679
|
|
|
543
|
-
|
|
544
|
-
|
|
680
|
+
if light_blue_row_ids:
|
|
681
|
+
light_blue_rows.extend(light_blue_row_ids)
|
|
545
682
|
|
|
546
|
-
|
|
547
|
-
|
|
683
|
+
if total_col:
|
|
684
|
+
light_blue_cols.append(arr.shape[1] - 1)
|
|
548
685
|
|
|
549
|
-
|
|
550
|
-
|
|
686
|
+
if blue_border:
|
|
687
|
+
blue_border_ids.append(arr.shape[0] - 1)
|
|
551
688
|
|
|
552
|
-
|
|
689
|
+
if blue_border_row_ids:
|
|
690
|
+
blue_border_ids.extend(blue_border_row_ids)
|
|
553
691
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
default_format=STYLES["calibri"],
|
|
557
|
-
blue_row_ids=blue_rows,
|
|
558
|
-
light_blue_row_ids=light_blue_rows,
|
|
559
|
-
light_blue_col_ids=light_blue_cols,
|
|
560
|
-
left_align_ids=l_align_ids,
|
|
561
|
-
right_align_ids=r_align_ids,
|
|
562
|
-
perc_col_ids=p_ids,
|
|
563
|
-
perc_col_format=perc_col_format,
|
|
564
|
-
number_format=number_format,
|
|
565
|
-
blue_border_ids=blue_border_ids,
|
|
566
|
-
)
|
|
692
|
+
if combine_multiindex:
|
|
693
|
+
cells_to_merge = get_cells_to_merge(df)
|
|
567
694
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
695
|
+
if autofit_columns == "column_names":
|
|
696
|
+
col_widths = get_max_col_widths(df)
|
|
697
|
+
elif autofit_columns == "all_data":
|
|
698
|
+
col_widths = get_max_col_widths(arr)
|
|
699
|
+
else:
|
|
700
|
+
col_widths = None
|
|
701
|
+
|
|
702
|
+
ws = wb.create_sheet(sheet_name)
|
|
703
|
+
|
|
704
|
+
fmt = cell_formatting(
|
|
705
|
+
arr=arr,
|
|
706
|
+
default_format=STYLES["calibri"],
|
|
707
|
+
blue_row_ids=blue_rows,
|
|
708
|
+
light_blue_row_ids=light_blue_rows,
|
|
709
|
+
light_blue_col_ids=light_blue_cols,
|
|
710
|
+
left_align_ids=l_align_ids,
|
|
711
|
+
right_align_ids=r_align_ids,
|
|
712
|
+
perc_col_ids=p_ids,
|
|
713
|
+
perc_col_format=perc_col_format,
|
|
714
|
+
number_format=number_format,
|
|
715
|
+
blue_border_ids=blue_border_ids,
|
|
716
|
+
)
|
|
578
717
|
|
|
579
|
-
|
|
718
|
+
write_to_worksheet(
|
|
719
|
+
ws=ws,
|
|
720
|
+
arr=arr,
|
|
721
|
+
fmt=fmt,
|
|
722
|
+
title=title_tbl,
|
|
723
|
+
source=title_src,
|
|
724
|
+
col_filter=col_filter,
|
|
725
|
+
col_widths=col_widths,
|
|
726
|
+
cells_to_merge=cells_to_merge,
|
|
727
|
+
min_column_width=min_column_width,
|
|
728
|
+
)
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: toolsos
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: OS tools
|
|
5
5
|
Author-email: OS <d.schmitz@amsterdam.nl>
|
|
6
6
|
Keywords: tools,Onderzoek & Statistiek
|
|
7
7
|
Classifier: License :: OSI Approved :: MIT License
|
|
8
8
|
Classifier: Programming Language :: Python
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
Provides-Extra: all
|
|
13
|
-
Requires-Dist: keyring ; extra == 'all'
|
|
14
|
-
Requires-Dist: openpyxl ; extra == 'all'
|
|
15
|
-
Requires-Dist: pandas ; extra == 'all'
|
|
16
|
-
Requires-Dist: plotly ; extra == 'all'
|
|
17
|
-
Requires-Dist: polars ; extra == 'all'
|
|
18
|
-
Requires-Dist: pyarrow ; extra == 'all'
|
|
19
|
-
Requires-Dist: pyreadstat ; extra == 'all'
|
|
20
|
-
Requires-Dist: pyyaml ; extra == 'all'
|
|
21
|
-
Requires-Dist: requests ; extra == 'all'
|
|
22
|
-
Requires-Dist: sqlalchemy ; extra == 'all'
|
|
23
12
|
Provides-Extra: dev
|
|
24
|
-
Requires-Dist: black
|
|
25
|
-
Requires-Dist: bumpver
|
|
26
|
-
Requires-Dist: isort
|
|
27
|
-
Requires-Dist: pip-tools
|
|
28
|
-
Requires-Dist: pytest
|
|
13
|
+
Requires-Dist: black; extra == "dev"
|
|
14
|
+
Requires-Dist: bumpver; extra == "dev"
|
|
15
|
+
Requires-Dist: isort; extra == "dev"
|
|
16
|
+
Requires-Dist: pip-tools; extra == "dev"
|
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
|
18
|
+
Provides-Extra: all
|
|
19
|
+
Requires-Dist: keyring; extra == "all"
|
|
20
|
+
Requires-Dist: openpyxl; extra == "all"
|
|
21
|
+
Requires-Dist: pandas; extra == "all"
|
|
22
|
+
Requires-Dist: plotly; extra == "all"
|
|
23
|
+
Requires-Dist: polars; extra == "all"
|
|
24
|
+
Requires-Dist: pyarrow; extra == "all"
|
|
25
|
+
Requires-Dist: pyreadstat; extra == "all"
|
|
26
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
27
|
+
Requires-Dist: requests; extra == "all"
|
|
28
|
+
Requires-Dist: sqlalchemy; extra == "all"
|
|
29
29
|
|
|
30
30
|
# Tools Onderzoek & Statistiek
|
|
31
31
|
|
|
@@ -63,5 +63,12 @@ Instructions on building a package can be found [here](https://packaging.python.
|
|
|
63
63
|
|
|
64
64
|
- make a pypi account
|
|
65
65
|
- ask to be added as collaborator to toolsos
|
|
66
|
-
- first update twine: py -m pip install --upgrade
|
|
67
|
-
- upload to pypi: twine upload dist/*
|
|
66
|
+
- first update twine: py -m pip install --upgrade twine
|
|
67
|
+
- upload to pypi: twine upload dist/* --skip-existing
|
|
68
|
+
|
|
69
|
+
## Install to local enviroment for testing
|
|
70
|
+
|
|
71
|
+
- python -m venv local (maak een lokale venv aan)
|
|
72
|
+
- local\Scripts\activate (activeer de venv)
|
|
73
|
+
- pip install -e . (installer toolsos)
|
|
74
|
+
- pip install -r local_requirements.txt (installeer de benodigde dependencies)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
toolsos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
toolsos/cbs_tools.py,sha256=361cogk0aIU4D4BKHaa7YSOBh64t5C3zrHlqtWx0iIc,3465
|
|
3
|
+
toolsos/create_tables.py,sha256=43FHK3EERjumBtnGhngIdtthZzcc_Qi37lJ1MgATzBg,908
|
|
4
|
+
toolsos/download.py,sha256=88hehmPL5m5d1nrcJjltuh4xrCItF5EYHaZdHOcSt-g,2652
|
|
5
|
+
toolsos/geo.py,sha256=arapy_ol6_so8KZ5gJk9ywXysSz4W8ah-cjrJ3DuxAo,2419
|
|
6
|
+
toolsos/helpers.py,sha256=VeOl-fLgePCbjEmAQdVmYe7z8OE1pISeDDuP1t5QSxM,997
|
|
7
|
+
toolsos/polars_helpers.py,sha256=P3RHLQFeDL7-9U_Q1n4ma_NSkdYAiker4pnc57uluHw,770
|
|
8
|
+
toolsos/database/database_connection.py,sha256=HCP8H_-iE2BOOQDp9v1HOgfUSX45XS-aqw7Nzf8drAU,4359
|
|
9
|
+
toolsos/database/database_transfer.py,sha256=1ghq5VEtKyOdCKdM45uOyrZSoXMuWsdC35R3WNuFvdU,1827
|
|
10
|
+
toolsos/huisstijl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
toolsos/huisstijl/colors.py,sha256=lSCHCdSjge5cGfLfAObd6mV6TaXq3QGImLOmoGJpGkw,1484
|
|
12
|
+
toolsos/huisstijl/graphs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
toolsos/huisstijl/graphs/bargraph.py,sha256=HYl01_euh23iDYSUhnAzYAXS0DhDpg9eLRjJEpeR6iU,2815
|
|
14
|
+
toolsos/huisstijl/graphs/graph_styles.py,sha256=Z9LLH7j8ODTsYMYK0rslacphuiRDcq5_IpSjEEiK2VY,975
|
|
15
|
+
toolsos/huisstijl/graphs/linegraph.py,sha256=dMUarRe31SXaY78OCXLy-PgnU8LlVJ9KkzKaHhDtuuI,698
|
|
16
|
+
toolsos/huisstijl/graphs/piegraph.py,sha256=aEFiEM-9QuhBOjKHSXVuE5bTh-8uucq4FP6O8Vk1vZI,703
|
|
17
|
+
toolsos/huisstijl/graphs/styler.py,sha256=-uZ7pjY1G39XvmaGHQd31gPRxjxmJGhYZk8xhy2JUWc,6623
|
|
18
|
+
toolsos/huisstijl/tables/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
toolsos/huisstijl/tables/table_helpers.py,sha256=jsQ6lw93sxtGJGrUn8X2_LyA2vYYnytngpUI5A_wpWQ,2037
|
|
20
|
+
toolsos/huisstijl/tables/table_styles.py,sha256=oYU6GJcfqlKpZof5PUjPsA7woJ3Tew78CHPyT0_jY6w,1343
|
|
21
|
+
toolsos/huisstijl/tables/tables.py,sha256=2FO-ByLjgs-DbNgem3cDfYJbLbIDzRDqXtjL75WN7kY,24054
|
|
22
|
+
toolsos-0.2.6.dist-info/METADATA,sha256=Fm7Ng8JDYMiDD3Ccm5AYGlGrl3QPZGmRMHC6oNA5o5o,2683
|
|
23
|
+
toolsos-0.2.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
24
|
+
toolsos-0.2.6.dist-info/top_level.txt,sha256=2ClEjUBbtfDQ8oPwvWRy1Sz2nrkLCXlg0mHaMdCWia0,8
|
|
25
|
+
toolsos-0.2.6.dist-info/RECORD,,
|
toolsos-0.1.4.dist-info/RECORD
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
toolsos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
toolsos/cbs_tools.py,sha256=VLhptzy7m5EaET07s6VeAfDz79g1Hs38qeWPyA14wkw,2823
|
|
3
|
-
toolsos/create_tables.py,sha256=43FHK3EERjumBtnGhngIdtthZzcc_Qi37lJ1MgATzBg,908
|
|
4
|
-
toolsos/database_connection.py,sha256=BlHzLS17KzJP_7R5-IBd8WqgwAt-zDRwJXD4Jx6Tetw,3323
|
|
5
|
-
toolsos/database_transfer.py,sha256=1ghq5VEtKyOdCKdM45uOyrZSoXMuWsdC35R3WNuFvdU,1827
|
|
6
|
-
toolsos/download.py,sha256=88hehmPL5m5d1nrcJjltuh4xrCItF5EYHaZdHOcSt-g,2652
|
|
7
|
-
toolsos/geo.py,sha256=_OexkeUgXcnPW1mw27VN6fMcX2PMUSljLwIg48Xkv3M,2412
|
|
8
|
-
toolsos/helpers.py,sha256=VeOl-fLgePCbjEmAQdVmYe7z8OE1pISeDDuP1t5QSxM,997
|
|
9
|
-
toolsos/polars_helpers.py,sha256=P3RHLQFeDL7-9U_Q1n4ma_NSkdYAiker4pnc57uluHw,770
|
|
10
|
-
toolsos/huisstijl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
toolsos/huisstijl/colors.py,sha256=lSCHCdSjge5cGfLfAObd6mV6TaXq3QGImLOmoGJpGkw,1484
|
|
12
|
-
toolsos/huisstijl/graphs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
toolsos/huisstijl/graphs/bargraph.py,sha256=02-2UCnGyvSKXRSOt-f5WuYu8_-6x9tFEkSi-FdD654,2582
|
|
14
|
-
toolsos/huisstijl/graphs/graph_styles.py,sha256=fuJUp0ZrZMhaXhHbfBJwOlPLVcCnKU_9MSfw6eddfCI,593
|
|
15
|
-
toolsos/huisstijl/graphs/linegraph.py,sha256=eiFNV_bXYM9G-HH0NywPO6WHI9eOZ91kNXoO3aiAvfE,416
|
|
16
|
-
toolsos/huisstijl/graphs/piegraph.py,sha256=Pzc3vrbvlkv40V3OYWh6bDDc5arRRwJq7dfRzplPKWY,571
|
|
17
|
-
toolsos/huisstijl/graphs/styler.py,sha256=0Mm37y_bkSy8mRtNzUgYfW_vrYZrV6_h1GLqQ8y8bXI,3434
|
|
18
|
-
toolsos/huisstijl/tables/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
-
toolsos/huisstijl/tables/table_styles.py,sha256=oYU6GJcfqlKpZof5PUjPsA7woJ3Tew78CHPyT0_jY6w,1343
|
|
20
|
-
toolsos/huisstijl/tables/tables.py,sha256=1x4htBUrX8FUWSmPiOI7nCNm9R4NYv-J-cK33lOXKpM,19462
|
|
21
|
-
toolsos-0.1.4.dist-info/METADATA,sha256=f5Vf3pkNPx8k2rJSziNvA5wNT7neRghAb1-xFe6wYsA,2417
|
|
22
|
-
toolsos-0.1.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
23
|
-
toolsos-0.1.4.dist-info/top_level.txt,sha256=2ClEjUBbtfDQ8oPwvWRy1Sz2nrkLCXlg0mHaMdCWia0,8
|
|
24
|
-
toolsos-0.1.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|