python-table-converter 0.1.2__tar.gz → 0.2.1__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.1.2 → python_table_converter-0.2.1}/PKG-INFO +2 -1
- {python_table_converter-0.1.2 → python_table_converter-0.2.1}/pyproject.toml +2 -1
- python_table_converter-0.2.1/table_converter/__init__.py +2 -0
- {python_table_converter-0.1.2 → python_table_converter-0.2.1}/table_converter/cli.py +9 -3
- {python_table_converter-0.1.2 → python_table_converter-0.2.1}/table_converter/commands/convert_tables.py +11 -1
- python_table_converter-0.2.1/table_converter/core/config.py +59 -0
- {python_table_converter-0.1.2 → python_table_converter-0.2.1}/table_converter/core/convert.py +21 -18
- python_table_converter-0.1.2/table_converter/__init__.py +0 -2
- {python_table_converter-0.1.2 → python_table_converter-0.2.1}/LICENSE +0 -0
- {python_table_converter-0.1.2 → python_table_converter-0.2.1}/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-table-converter
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A table data converter
|
|
5
5
|
Home-page: https://github.com/akivajp/python-table-converter
|
|
6
6
|
License: MIT
|
|
@@ -16,6 +16,7 @@ Requires-Dist: icecream (>=2.1.3,<3.0.0)
|
|
|
16
16
|
Requires-Dist: logzero (>=1.7.0,<2.0.0)
|
|
17
17
|
Requires-Dist: openpyxl (>=3.1.5,<4.0.0)
|
|
18
18
|
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
|
19
|
+
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
19
20
|
Requires-Dist: tqdm (>=4.67.0,<5.0.0)
|
|
20
21
|
Project-URL: Repository, https://github.com/akivajp/python-table-converter
|
|
21
22
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-table-converter"
|
|
3
|
-
version = "0.1
|
|
3
|
+
version = "0.2.1"
|
|
4
4
|
description = "A table data converter"
|
|
5
5
|
authors = ["Akiva Miura <akiva.miura@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -25,6 +25,7 @@ logzero = "^1.7.0"
|
|
|
25
25
|
tqdm = "^4.67.0"
|
|
26
26
|
pandas = "^2.2.3"
|
|
27
27
|
openpyxl = "^3.1.5"
|
|
28
|
+
pyyaml = "^6.0.2"
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
[build-system]
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
5
|
+
import os
|
|
5
6
|
import sys
|
|
6
7
|
|
|
7
8
|
from icecream import ic
|
|
@@ -9,9 +10,14 @@ from icecream import ic
|
|
|
9
10
|
def parse_and_run(
|
|
10
11
|
parser: argparse.ArgumentParser,
|
|
11
12
|
):
|
|
12
|
-
|
|
13
|
+
if os.environ.get('DEBUG', '').lower() in ['1', 'true', 'yes', 'on']:
|
|
14
|
+
pass
|
|
15
|
+
else:
|
|
16
|
+
ic.disable()
|
|
13
17
|
ic()
|
|
14
18
|
args = parser.parse_args()
|
|
19
|
+
if args.verbose:
|
|
20
|
+
ic.enable()
|
|
15
21
|
ic(args)
|
|
16
22
|
if args.handler:
|
|
17
23
|
args.handler(args)
|
|
@@ -28,10 +34,10 @@ def command_convert_tables(
|
|
|
28
34
|
)
|
|
29
35
|
else:
|
|
30
36
|
command_parser = parser
|
|
31
|
-
from .
|
|
37
|
+
from table_converter.commands.convert_tables import setup_parser
|
|
32
38
|
setup_parser(command_parser)
|
|
33
39
|
if parser is None:
|
|
34
|
-
parse_and_run(
|
|
40
|
+
parse_and_run(command_parser)
|
|
35
41
|
|
|
36
42
|
def main():
|
|
37
43
|
parser = argparse.ArgumentParser(description='Table Data Converter')
|
|
@@ -12,6 +12,7 @@ def run(
|
|
|
12
12
|
convert(
|
|
13
13
|
input_files = args.input_files,
|
|
14
14
|
output_file = args.output_file,
|
|
15
|
+
config_path = args.config,
|
|
15
16
|
assign_constants = args.assign_constants,
|
|
16
17
|
assign_formats = args.assign_formats,
|
|
17
18
|
pickup_columns= args.pickup_columns,
|
|
@@ -23,6 +24,10 @@ def run(
|
|
|
23
24
|
def setup_parser(
|
|
24
25
|
parser: argparse.ArgumentParser,
|
|
25
26
|
):
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
'--verbose', '-v',
|
|
29
|
+
action='store_true',
|
|
30
|
+
)
|
|
26
31
|
parser.add_argument(
|
|
27
32
|
'input_files',
|
|
28
33
|
metavar='INPUT_FILE',
|
|
@@ -30,11 +35,16 @@ def setup_parser(
|
|
|
30
35
|
help='Path to the input file.'
|
|
31
36
|
)
|
|
32
37
|
parser.add_argument(
|
|
33
|
-
'-
|
|
38
|
+
'--output-file', '-o',
|
|
34
39
|
metavar='OUTPUT_FILE',
|
|
35
40
|
required=True,
|
|
36
41
|
help='Path to the output file.'
|
|
37
42
|
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
'--config', '-c',
|
|
45
|
+
type=str,
|
|
46
|
+
help='Path to the configuration file.',
|
|
47
|
+
)
|
|
38
48
|
parser.add_argument(
|
|
39
49
|
'--pickup-columns',
|
|
40
50
|
type=str,
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from collections import OrderedDict
|
|
4
|
+
import dataclasses
|
|
5
|
+
from typing import Mapping
|
|
6
|
+
|
|
7
|
+
from icecream import ic
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
type FlatFieldMap = Mapping[str, str]
|
|
11
|
+
type FieldMap = Mapping[str, str|FieldMap]
|
|
12
|
+
|
|
13
|
+
@dataclasses.dataclass
|
|
14
|
+
class ProcessConfig:
|
|
15
|
+
split_by_newline: FlatFieldMap = dataclasses.field(default_factory=OrderedDict)
|
|
16
|
+
|
|
17
|
+
@dataclasses.dataclass
|
|
18
|
+
class Config:
|
|
19
|
+
map: FieldMap = dataclasses.field(default_factory=OrderedDict)
|
|
20
|
+
process: ProcessConfig = dataclasses.field(default_factory=ProcessConfig)
|
|
21
|
+
|
|
22
|
+
def flatten(
|
|
23
|
+
mapping: FieldMap,
|
|
24
|
+
parent_key: str = '',
|
|
25
|
+
new_mapping: FlatFieldMap | None = None,
|
|
26
|
+
) -> FlatFieldMap:
|
|
27
|
+
if new_mapping is None:
|
|
28
|
+
new_mapping = OrderedDict()
|
|
29
|
+
for key, mapped in mapping.items():
|
|
30
|
+
new_key = f'{parent_key}.{key}' if parent_key else key
|
|
31
|
+
if isinstance(mapped, Mapping):
|
|
32
|
+
flatten(mapped, new_key, new_mapping)
|
|
33
|
+
else:
|
|
34
|
+
new_mapping[new_key] = mapped
|
|
35
|
+
return new_mapping
|
|
36
|
+
|
|
37
|
+
def setup_config(
|
|
38
|
+
config_path: str | None = None,
|
|
39
|
+
):
|
|
40
|
+
config = Config()
|
|
41
|
+
if config_path:
|
|
42
|
+
if config_path.endswith('.yaml'):
|
|
43
|
+
yaml.add_constructor(
|
|
44
|
+
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
|
45
|
+
lambda loader, node: OrderedDict(loader.construct_pairs(node)),
|
|
46
|
+
)
|
|
47
|
+
with open(config_path, 'r') as f:
|
|
48
|
+
loaded = yaml.load(f, yaml.Loader)
|
|
49
|
+
else:
|
|
50
|
+
raise ValueError(
|
|
51
|
+
'Only YAML configuration files are supported.'
|
|
52
|
+
)
|
|
53
|
+
ic(loaded)
|
|
54
|
+
if 'map' in loaded:
|
|
55
|
+
config.map = flatten(loaded['map'])
|
|
56
|
+
if 'process' in loaded:
|
|
57
|
+
if 'split_by_newline' in loaded['process']:
|
|
58
|
+
config.process.split_by_newline = flatten(loaded['process']['split_by_newline'])
|
|
59
|
+
return config
|
{python_table_converter-0.1.2 → python_table_converter-0.2.1}/table_converter/core/convert.py
RENAMED
|
@@ -5,9 +5,15 @@ import os
|
|
|
5
5
|
|
|
6
6
|
from collections import OrderedDict
|
|
7
7
|
|
|
8
|
+
# 3-rd party modules
|
|
9
|
+
|
|
10
|
+
from icecream import ic
|
|
8
11
|
import numpy as np
|
|
9
12
|
import pandas as pd
|
|
10
|
-
|
|
13
|
+
|
|
14
|
+
# local
|
|
15
|
+
|
|
16
|
+
from . config import setup_config
|
|
11
17
|
|
|
12
18
|
dict_loaders: dict[str, callable] = {}
|
|
13
19
|
def register_loader(
|
|
@@ -227,6 +233,7 @@ def assign_id(
|
|
|
227
233
|
def convert(
|
|
228
234
|
input_files: list[str],
|
|
229
235
|
output_file: str | None = None,
|
|
236
|
+
config_path: str | None = None,
|
|
230
237
|
assign_constants: str | None = None,
|
|
231
238
|
assign_formats: str | None = None,
|
|
232
239
|
pickup_columns: str | None = None,
|
|
@@ -234,16 +241,16 @@ def convert(
|
|
|
234
241
|
fields_to_assign_ids: str | None = None,
|
|
235
242
|
output_debug: bool = False,
|
|
236
243
|
):
|
|
244
|
+
ic.enable()
|
|
237
245
|
ic()
|
|
238
246
|
ic(input_files)
|
|
239
247
|
df_list = []
|
|
240
248
|
dict_constants: OrderedDict | None = None
|
|
241
|
-
dict_columns: OrderedDict | None = None
|
|
242
249
|
dict_formats: OrderedDict | None = None
|
|
243
|
-
#list_fields_to_split_by_newline = None
|
|
244
|
-
dict_split_by_newline: OrderedDict | None = None
|
|
245
250
|
dict_assign_ids= None
|
|
246
251
|
root_id_stat = create_id_stat_node()
|
|
252
|
+
config = setup_config(config_path)
|
|
253
|
+
ic(config)
|
|
247
254
|
if assign_constants:
|
|
248
255
|
dict_constants = OrderedDict()
|
|
249
256
|
fields = assign_constants.split(',')
|
|
@@ -263,22 +270,19 @@ def convert(
|
|
|
263
270
|
else:
|
|
264
271
|
raise ValueError(f'Invalid template assignment: {field}')
|
|
265
272
|
if pickup_columns:
|
|
266
|
-
dict_columns = OrderedDict()
|
|
267
273
|
fields = pickup_columns.split(',')
|
|
268
274
|
for field in fields:
|
|
269
275
|
if '=' in field:
|
|
270
276
|
dst, value = field.split('=')
|
|
271
|
-
|
|
277
|
+
config.map[dst] = value
|
|
272
278
|
else:
|
|
273
|
-
|
|
279
|
+
config.map[field] = field
|
|
274
280
|
if fields_to_split_by_newline:
|
|
275
|
-
#list_fields_to_split_by_newline = fields_to_split_by_newline.split(',')
|
|
276
|
-
dict_split_by_newline = OrderedDict()
|
|
277
281
|
fields = fields_to_split_by_newline.split(',')
|
|
278
282
|
for field in fields:
|
|
279
283
|
if '=' in field:
|
|
280
284
|
dst, src = field.split('=')
|
|
281
|
-
|
|
285
|
+
config.process.split_by_newline[dst] = src
|
|
282
286
|
else:
|
|
283
287
|
raise ValueError(f'Invalid split by newline: {field}')
|
|
284
288
|
if fields_to_assign_ids:
|
|
@@ -295,6 +299,7 @@ def convert(
|
|
|
295
299
|
if ext not in dict_savers:
|
|
296
300
|
raise ValueError(f'Unsupported file type: {ext}')
|
|
297
301
|
saver = dict_savers[ext]
|
|
302
|
+
ic(config)
|
|
298
303
|
for input_file in input_files:
|
|
299
304
|
ic(input_file)
|
|
300
305
|
if not os.path.exists(input_file):
|
|
@@ -318,18 +323,16 @@ def convert(
|
|
|
318
323
|
set_field_value(new_row, '__debug__.__file__', input_file)
|
|
319
324
|
if dict_constants:
|
|
320
325
|
new_row = map_constants(new_row, dict_constants)
|
|
321
|
-
if
|
|
322
|
-
new_row = remap_columns(new_row,
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if dict_split_by_newline:
|
|
326
|
-
new_row = apply_fields_split_by_newline(new_row, dict_split_by_newline)
|
|
326
|
+
if config.map:
|
|
327
|
+
new_row = remap_columns(new_row, config.map)
|
|
328
|
+
if config.process.split_by_newline:
|
|
329
|
+
new_row = apply_fields_split_by_newline(new_row, config.process.split_by_newline)
|
|
327
330
|
if dict_assign_ids:
|
|
328
331
|
new_row = assign_id(new_row, dict_assign_ids, root_id_stat)
|
|
329
332
|
if dict_formats:
|
|
330
333
|
new_row = map_formats(new_row, dict_formats)
|
|
331
|
-
if
|
|
332
|
-
new_row = remap_columns(new_row,
|
|
334
|
+
if config.map:
|
|
335
|
+
new_row = remap_columns(new_row, config.map)
|
|
333
336
|
if not output_debug:
|
|
334
337
|
new_row.pop('__debug__', None)
|
|
335
338
|
new_rows.append(new_row)
|
|
File without changes
|
|
File without changes
|