python-table-converter 0.2.7__tar.gz → 0.2.9__tar.gz
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.
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/PKG-INFO +1 -1
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/pyproject.toml +1 -1
- python_table_converter-0.2.9/table_converter/__init__.py +2 -0
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/cli.py +8 -2
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/config.py +5 -18
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/convert.py +75 -49
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/functions/assign_id.py +6 -2
- python_table_converter-0.2.9/table_converter/core/functions/flatten.py +24 -0
- python_table_converter-0.2.9/table_converter/core/functions/search_column_value.py +25 -0
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/functions/set_field_value.py +1 -0
- python_table_converter-0.2.7/table_converter/__init__.py +0 -2
- python_table_converter-0.2.7/table_converter/core/functions/search_column_value.py +0 -30
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/LICENSE +0 -0
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/README.md +0 -0
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/commands/convert_tables.py +0 -0
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/constants.py +0 -0
- {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/functions/get_field_value.py +0 -0
|
@@ -39,12 +39,17 @@ def command_convert_tables(
|
|
|
39
39
|
if parser is None:
|
|
40
40
|
parse_and_run(command_parser)
|
|
41
41
|
|
|
42
|
-
def
|
|
43
|
-
parser
|
|
42
|
+
def setup_common_args(
|
|
43
|
+
parser: argparse.ArgumentParser,
|
|
44
|
+
):
|
|
44
45
|
parser.add_argument(
|
|
45
46
|
'--verbose', '-v',
|
|
46
47
|
action='store_true',
|
|
47
48
|
)
|
|
49
|
+
|
|
50
|
+
def main():
|
|
51
|
+
parser = argparse.ArgumentParser(description='Table Data Converter')
|
|
52
|
+
setup_common_args(parser)
|
|
48
53
|
parser.set_defaults(handler=None)
|
|
49
54
|
subparsers = parser.add_subparsers(dest='command')
|
|
50
55
|
|
|
@@ -52,6 +57,7 @@ def main():
|
|
|
52
57
|
'convert',
|
|
53
58
|
help='Convert a table to a different format.'
|
|
54
59
|
)
|
|
60
|
+
setup_common_args(parser_convert_tables)
|
|
55
61
|
command_convert_tables(parser_convert_tables)
|
|
56
62
|
|
|
57
63
|
parse_and_run(parser)
|
{python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/config.py
RENAMED
|
@@ -7,13 +7,15 @@ from typing import Mapping
|
|
|
7
7
|
from icecream import ic
|
|
8
8
|
import yaml
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
from . functions.flatten import (
|
|
11
|
+
FieldMap,
|
|
12
|
+
FlatFieldMap,
|
|
13
|
+
flatten,
|
|
14
|
+
)
|
|
12
15
|
|
|
13
16
|
@dataclasses.dataclass
|
|
14
17
|
class AssignIdConfig:
|
|
15
18
|
primary: list[str]
|
|
16
|
-
#given: list[str] | None = None
|
|
17
19
|
context: list[str] | None = None
|
|
18
20
|
|
|
19
21
|
@dataclasses.dataclass
|
|
@@ -32,21 +34,6 @@ class Config:
|
|
|
32
34
|
map: FieldMap = dataclasses.field(default_factory=OrderedDict)
|
|
33
35
|
process: ProcessConfig = dataclasses.field(default_factory=ProcessConfig)
|
|
34
36
|
|
|
35
|
-
def flatten(
|
|
36
|
-
mapping: FieldMap,
|
|
37
|
-
parent_key: str = '',
|
|
38
|
-
new_mapping: FlatFieldMap | None = None,
|
|
39
|
-
) -> FlatFieldMap:
|
|
40
|
-
if new_mapping is None:
|
|
41
|
-
new_mapping = OrderedDict()
|
|
42
|
-
for key, mapped in mapping.items():
|
|
43
|
-
new_key = f'{parent_key}.{key}' if parent_key else key
|
|
44
|
-
if isinstance(mapped, Mapping):
|
|
45
|
-
flatten(mapped, new_key, new_mapping)
|
|
46
|
-
else:
|
|
47
|
-
new_mapping[new_key] = mapped
|
|
48
|
-
return new_mapping
|
|
49
|
-
|
|
50
37
|
def setup_config(
|
|
51
38
|
config_path: str | None = None,
|
|
52
39
|
):
|
{python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/convert.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import math
|
|
4
5
|
import os
|
|
5
6
|
|
|
6
7
|
from collections import OrderedDict
|
|
@@ -24,7 +25,11 @@ from . functions.assign_id import (
|
|
|
24
25
|
create_id_context_map,
|
|
25
26
|
setup_assign_ids,
|
|
26
27
|
)
|
|
28
|
+
from . functions.flatten import (
|
|
29
|
+
flatten,
|
|
30
|
+
)
|
|
27
31
|
from . functions.get_field_value import get_field_value
|
|
32
|
+
from . functions.search_column_value import search_column_value
|
|
28
33
|
from . functions.set_field_value import set_field_value
|
|
29
34
|
|
|
30
35
|
dict_loaders: dict[str, callable] = {}
|
|
@@ -52,6 +57,37 @@ def load_excel(
|
|
|
52
57
|
df = pd.read_excel(input_file)
|
|
53
58
|
return df
|
|
54
59
|
|
|
60
|
+
@register_loader('.json')
|
|
61
|
+
def load_json(
|
|
62
|
+
input_file: str,
|
|
63
|
+
):
|
|
64
|
+
with open(input_file, 'r') as f:
|
|
65
|
+
data = json.load(f)
|
|
66
|
+
if not isinstance(data, list):
|
|
67
|
+
raise ValueError(f'Invalid JSON array data: {input_file}')
|
|
68
|
+
ic(data[0])
|
|
69
|
+
rows = []
|
|
70
|
+
for row in data:
|
|
71
|
+
new_row = flatten(row)
|
|
72
|
+
rows.append(new_row)
|
|
73
|
+
df = pd.DataFrame(rows)
|
|
74
|
+
return df
|
|
75
|
+
|
|
76
|
+
def nest(
|
|
77
|
+
row: OrderedDict,
|
|
78
|
+
remove_nan: bool = True,
|
|
79
|
+
):
|
|
80
|
+
new_row = OrderedDict()
|
|
81
|
+
for key, value in row.items():
|
|
82
|
+
if isinstance(value, OrderedDict):
|
|
83
|
+
value = nest(value)
|
|
84
|
+
if isinstance(value, float):
|
|
85
|
+
if math.isnan(value):
|
|
86
|
+
if remove_nan:
|
|
87
|
+
continue
|
|
88
|
+
set_field_value(new_row, key, value)
|
|
89
|
+
return new_row
|
|
90
|
+
|
|
55
91
|
@register_saver('.json')
|
|
56
92
|
def save_json(
|
|
57
93
|
df: pd.DataFrame,
|
|
@@ -67,6 +103,9 @@ def save_json(
|
|
|
67
103
|
#)
|
|
68
104
|
#ic(df.iloc[0])
|
|
69
105
|
data = df.to_dict(orient='records')
|
|
106
|
+
ic(data[0])
|
|
107
|
+
data = [nest(row) for row in data]
|
|
108
|
+
ic(data[0])
|
|
70
109
|
with open(output_file, 'w') as f:
|
|
71
110
|
json.dump(
|
|
72
111
|
data,
|
|
@@ -97,34 +136,13 @@ def save_jsonl(
|
|
|
97
136
|
)
|
|
98
137
|
f.write('\n')
|
|
99
138
|
|
|
100
|
-
def search_column_value(
|
|
101
|
-
row: OrderedDict,
|
|
102
|
-
column: str,
|
|
103
|
-
):
|
|
104
|
-
if STAGING_FIELD in row:
|
|
105
|
-
value, found = get_field_value(row[STAGING_FIELD], column)
|
|
106
|
-
if found:
|
|
107
|
-
return value, True
|
|
108
|
-
value, found = get_field_value(row[STAGING_FIELD], column)
|
|
109
|
-
original, found = get_field_value(row, f'{STAGING_FIELD}.{INPUT_FIELD}')
|
|
110
|
-
if found:
|
|
111
|
-
value, found = get_field_value(original, column)
|
|
112
|
-
if found:
|
|
113
|
-
return value, True
|
|
114
|
-
value, found = get_field_value(row, column)
|
|
115
|
-
if found:
|
|
116
|
-
set_field_value(row, column, value)
|
|
117
|
-
return value, True
|
|
118
|
-
return None, False
|
|
119
|
-
|
|
120
139
|
def map_constants(
|
|
121
140
|
row: OrderedDict,
|
|
122
141
|
dict_constants: OrderedDict,
|
|
123
142
|
):
|
|
124
143
|
new_row = OrderedDict(row)
|
|
125
144
|
for column in dict_constants.keys():
|
|
126
|
-
|
|
127
|
-
set_field_value(new_row, f'{STAGING_FIELD}.{column}', dict_constants[column])
|
|
145
|
+
new_row[f'{STAGING_FIELD}.{column}'] = dict_constants[column]
|
|
128
146
|
return new_row
|
|
129
147
|
|
|
130
148
|
def map_formats(
|
|
@@ -135,7 +153,11 @@ def map_formats(
|
|
|
135
153
|
for column in dict_formats.keys():
|
|
136
154
|
template = dict_formats[column]
|
|
137
155
|
params = {}
|
|
138
|
-
|
|
156
|
+
for key, value in row.items():
|
|
157
|
+
prefix = f'{STAGING_FIELD}.'
|
|
158
|
+
if key.startswith(prefix):
|
|
159
|
+
rest = key[len(prefix):]
|
|
160
|
+
params[rest] = value
|
|
139
161
|
params.update(row)
|
|
140
162
|
formatted = None
|
|
141
163
|
while formatted is None:
|
|
@@ -149,7 +171,7 @@ def map_formats(
|
|
|
149
171
|
params[key] = f'__{key}__undefined__'
|
|
150
172
|
except:
|
|
151
173
|
raise
|
|
152
|
-
|
|
174
|
+
new_row[f'{STAGING_FIELD}.{column}'] = formatted
|
|
153
175
|
return new_row
|
|
154
176
|
|
|
155
177
|
def remap_columns(
|
|
@@ -160,11 +182,12 @@ def remap_columns(
|
|
|
160
182
|
for column in dict_remap.keys():
|
|
161
183
|
value, found = search_column_value(row, dict_remap[column])
|
|
162
184
|
if found:
|
|
163
|
-
|
|
185
|
+
new_row[column] = value
|
|
164
186
|
for column in row.keys():
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
187
|
+
prefix = f'{STAGING_FIELD}.'
|
|
188
|
+
if column.startswith(prefix):
|
|
189
|
+
# NOTE: Leave staging fields as is
|
|
190
|
+
new_row[column] = row[column]
|
|
168
191
|
return new_row
|
|
169
192
|
|
|
170
193
|
def apply_fields_split_by_newline(
|
|
@@ -174,13 +197,12 @@ def apply_fields_split_by_newline(
|
|
|
174
197
|
new_row = OrderedDict(row)
|
|
175
198
|
for column in dict_fields:
|
|
176
199
|
value, found = search_column_value(row, dict_fields[column])
|
|
177
|
-
#ic(value, found)
|
|
178
200
|
if found:
|
|
179
201
|
if isinstance(value, str):
|
|
180
202
|
new_value = value.split('\n')
|
|
181
|
-
|
|
203
|
+
new_row[f'{STAGING_FIELD}.{column}'] = new_value
|
|
182
204
|
else:
|
|
183
|
-
|
|
205
|
+
new_row[f'{STAGING_FIELD}.{column}'] = value
|
|
184
206
|
return new_row
|
|
185
207
|
|
|
186
208
|
def convert(
|
|
@@ -254,31 +276,35 @@ def convert(
|
|
|
254
276
|
# NOTE: NaN を None に変換しておかないと厄介
|
|
255
277
|
df = df.replace([np.nan], [None])
|
|
256
278
|
#ic(df)
|
|
257
|
-
ic(len(df))
|
|
258
|
-
ic(df.columns)
|
|
259
|
-
ic(df.iloc[0])
|
|
260
|
-
new_rows = []
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
279
|
+
#ic(len(df))
|
|
280
|
+
#ic(df.columns)
|
|
281
|
+
#ic(df.iloc[0])
|
|
282
|
+
#new_rows = []
|
|
283
|
+
new_flat_rows = []
|
|
284
|
+
for index, flat_row in df.iterrows():
|
|
285
|
+
orig = OrderedDict(flat_row)
|
|
286
|
+
new_flat_row = OrderedDict(flat_row)
|
|
287
|
+
new_nested_row = nest(new_flat_row)
|
|
288
|
+
if STAGING_FIELD not in new_nested_row:
|
|
289
|
+
new_flat_row[f'{STAGING_FIELD}.{FILE_FIELD}'] = input_file
|
|
290
|
+
for key, value in orig.items():
|
|
291
|
+
new_flat_row[f'{STAGING_FIELD}.{INPUT_FIELD}.{key}'] = value
|
|
266
292
|
if config.process.assign_constants:
|
|
267
|
-
|
|
293
|
+
new_flat_row = map_constants(new_flat_row, config.process.assign_constants)
|
|
268
294
|
if config.map:
|
|
269
|
-
|
|
295
|
+
new_flat_row = remap_columns(new_flat_row, config.map)
|
|
270
296
|
if config.process.split_by_newline:
|
|
271
|
-
|
|
297
|
+
new_flat_row = apply_fields_split_by_newline(new_flat_row, config.process.split_by_newline)
|
|
272
298
|
if config.process.assign_ids:
|
|
273
|
-
|
|
299
|
+
new_flat_row = assign_id(new_flat_row, config.process.assign_ids, id_context_map)
|
|
274
300
|
if config.process.assign_formats:
|
|
275
|
-
|
|
301
|
+
new_flat_row = map_formats(new_flat_row, config.process.assign_formats)
|
|
276
302
|
if config.map:
|
|
277
|
-
|
|
303
|
+
new_flat_row = remap_columns(new_flat_row, config.map)
|
|
278
304
|
if not output_debug:
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
new_df = pd.DataFrame(
|
|
305
|
+
new_flat_row.pop(STAGING_FIELD, None)
|
|
306
|
+
new_flat_rows.append(new_flat_row)
|
|
307
|
+
new_df = pd.DataFrame(new_flat_rows)
|
|
282
308
|
df_list.append(new_df)
|
|
283
309
|
all_df = pd.concat(df_list)
|
|
284
310
|
#ic(all_df)
|
|
@@ -16,7 +16,11 @@ import pandas as pd
|
|
|
16
16
|
|
|
17
17
|
# local
|
|
18
18
|
|
|
19
|
-
from ..
|
|
19
|
+
from .. constants import (
|
|
20
|
+
STAGING_FIELD,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from .. config import (
|
|
20
24
|
AssignIdConfig,
|
|
21
25
|
Config,
|
|
22
26
|
)
|
|
@@ -87,7 +91,7 @@ def assign_id(
|
|
|
87
91
|
id_map.dict_id_to_value[field_id] = primary_value
|
|
88
92
|
else:
|
|
89
93
|
field_id = id_map.dict_value_to_id[primary_value]
|
|
90
|
-
|
|
94
|
+
new_row[f'{STAGING_FIELD}.{column}'] = field_id
|
|
91
95
|
return new_row
|
|
92
96
|
|
|
93
97
|
def setup_assign_ids(
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'''
|
|
2
|
+
This module contains the function to flatten a nested dictionary.
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
from collections import OrderedDict
|
|
6
|
+
from typing import Mapping
|
|
7
|
+
|
|
8
|
+
type FlatFieldMap = Mapping[str]
|
|
9
|
+
type FieldMap = Mapping[str, str|FieldMap]
|
|
10
|
+
|
|
11
|
+
def flatten(
|
|
12
|
+
mapping: FieldMap,
|
|
13
|
+
parent_key: str = '',
|
|
14
|
+
new_mapping: FlatFieldMap | None = None,
|
|
15
|
+
) -> FlatFieldMap:
|
|
16
|
+
if new_mapping is None:
|
|
17
|
+
new_mapping = OrderedDict()
|
|
18
|
+
for key, mapped in mapping.items():
|
|
19
|
+
new_key = f'{parent_key}.{key}' if parent_key else key
|
|
20
|
+
if isinstance(mapped, Mapping):
|
|
21
|
+
flatten(mapped, new_key, new_mapping)
|
|
22
|
+
else:
|
|
23
|
+
new_mapping[new_key] = mapped
|
|
24
|
+
return new_mapping
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'''
|
|
2
|
+
This function is used to search for a column value in a row. It will first search in the '__debug__' field, then in the '__debug__.__original__' field, and finally in the row itself. If the value is found, it will be set in the row and returned.
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
from .. constants import (
|
|
6
|
+
INPUT_FIELD,
|
|
7
|
+
STAGING_FIELD,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from collections import OrderedDict
|
|
11
|
+
|
|
12
|
+
def search_column_value(
|
|
13
|
+
row: OrderedDict,
|
|
14
|
+
column: str,
|
|
15
|
+
):
|
|
16
|
+
if f'{STAGING_FIELD}.{column}' in row:
|
|
17
|
+
value = row[f'{STAGING_FIELD}.{column}']
|
|
18
|
+
return value, True
|
|
19
|
+
if f'{STAGING_FIELD}.{INPUT_FIELD}.{column}' in row:
|
|
20
|
+
value = row[f'{STAGING_FIELD}.{INPUT_FIELD}.{column}']
|
|
21
|
+
return value, True
|
|
22
|
+
if column in row:
|
|
23
|
+
value = row[column]
|
|
24
|
+
return value, True
|
|
25
|
+
return None, False
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
'''
|
|
2
|
-
This function is used to search for a column value in a row. It will first search in the '__debug__' field, then in the '__debug__.__original__' field, and finally in the row itself. If the value is found, it will be set in the row and returned.
|
|
3
|
-
'''
|
|
4
|
-
|
|
5
|
-
from .. constants import STAGING_FIELD
|
|
6
|
-
|
|
7
|
-
from collections import OrderedDict
|
|
8
|
-
|
|
9
|
-
from . get_field_value import get_field_value
|
|
10
|
-
from . set_field_value import set_field_value
|
|
11
|
-
|
|
12
|
-
def search_column_value(
|
|
13
|
-
row: OrderedDict,
|
|
14
|
-
column: str,
|
|
15
|
-
):
|
|
16
|
-
if STAGING_FIELD in row:
|
|
17
|
-
value, found = get_field_value(row[STAGING_FIELD], column)
|
|
18
|
-
if found:
|
|
19
|
-
return value, True
|
|
20
|
-
value, found = get_field_value(row[STAGING_FIELD], column)
|
|
21
|
-
original, found = get_field_value(row, f'{STAGING_FIELD}.__original__')
|
|
22
|
-
if found:
|
|
23
|
-
value, found = get_field_value(original, column)
|
|
24
|
-
if found:
|
|
25
|
-
return value, True
|
|
26
|
-
value, found = get_field_value(row, column)
|
|
27
|
-
if found:
|
|
28
|
-
set_field_value(row, column, value)
|
|
29
|
-
return value, True
|
|
30
|
-
return None, False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/constants.py
RENAMED
|
File without changes
|
|
File without changes
|