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.
Files changed (17) hide show
  1. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/PKG-INFO +1 -1
  2. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/pyproject.toml +1 -1
  3. python_table_converter-0.2.9/table_converter/__init__.py +2 -0
  4. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/cli.py +8 -2
  5. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/config.py +5 -18
  6. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/convert.py +75 -49
  7. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/functions/assign_id.py +6 -2
  8. python_table_converter-0.2.9/table_converter/core/functions/flatten.py +24 -0
  9. python_table_converter-0.2.9/table_converter/core/functions/search_column_value.py +25 -0
  10. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/functions/set_field_value.py +1 -0
  11. python_table_converter-0.2.7/table_converter/__init__.py +0 -2
  12. python_table_converter-0.2.7/table_converter/core/functions/search_column_value.py +0 -30
  13. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/LICENSE +0 -0
  14. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/README.md +0 -0
  15. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/commands/convert_tables.py +0 -0
  16. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/constants.py +0 -0
  17. {python_table_converter-0.2.7 → python_table_converter-0.2.9}/table_converter/core/functions/get_field_value.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-table-converter
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: A table data converter
5
5
  Home-page: https://github.com/akivajp/python-table-converter
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-table-converter"
3
- version = "0.2.7"
3
+ version = "0.2.9"
4
4
  description = "A table data converter"
5
5
  authors = ["Akiva Miura <akiva.miura@gmail.com>"]
6
6
  license = "MIT"
@@ -0,0 +1,2 @@
1
+ __version__ = "0.2.9"
2
+ __version_tuple__ = (0, 2, 9)
@@ -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 main():
43
- parser = argparse.ArgumentParser(description='Table Data Converter')
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)
@@ -7,13 +7,15 @@ from typing import Mapping
7
7
  from icecream import ic
8
8
  import yaml
9
9
 
10
- type FlatFieldMap = Mapping[str, str]
11
- type FieldMap = Mapping[str, str|FieldMap]
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
  ):
@@ -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
- #set_field_value(new_row, column, dict_constants[column])
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
- params.update(row[STAGING_FIELD])
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
- set_field_value(new_row, f'{STAGING_FIELD}.{column}', formatted)
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
- set_field_value(new_row, column, value)
185
+ new_row[column] = value
164
186
  for column in row.keys():
165
- if column == STAGING_FIELD:
166
- # NOTE: Ignore debug fields
167
- set_field_value(new_row, column, row[column])
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
- set_field_value(new_row, f'{STAGING_FIELD}.{column}', new_value)
203
+ new_row[f'{STAGING_FIELD}.{column}'] = new_value
182
204
  else:
183
- set_field_value(new_row, f'{STAGING_FIELD}.{column}', value)
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
- for index, row in df.iterrows():
262
- orig = OrderedDict(row)
263
- new_row = OrderedDict(row)
264
- set_field_value(new_row, f'{STAGING_FIELD}.{INPUT_FIELD}', orig)
265
- set_field_value(new_row, f'{STAGING_FIELD}.{FILE_FIELD}', input_file)
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
- new_row = map_constants(new_row, config.process.assign_constants)
293
+ new_flat_row = map_constants(new_flat_row, config.process.assign_constants)
268
294
  if config.map:
269
- new_row = remap_columns(new_row, config.map)
295
+ new_flat_row = remap_columns(new_flat_row, config.map)
270
296
  if config.process.split_by_newline:
271
- new_row = apply_fields_split_by_newline(new_row, config.process.split_by_newline)
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
- new_row = assign_id(new_row, config.process.assign_ids, id_context_map)
299
+ new_flat_row = assign_id(new_flat_row, config.process.assign_ids, id_context_map)
274
300
  if config.process.assign_formats:
275
- new_row = map_formats(new_row, config.process.assign_formats)
301
+ new_flat_row = map_formats(new_flat_row, config.process.assign_formats)
276
302
  if config.map:
277
- new_row = remap_columns(new_row, config.map)
303
+ new_flat_row = remap_columns(new_flat_row, config.map)
278
304
  if not output_debug:
279
- new_row.pop(STAGING_FIELD, None)
280
- new_rows.append(new_row)
281
- new_df = pd.DataFrame(new_rows)
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 ..config import (
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
- set_field_value(new_row, f'__debug__.{column}', field_id)
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
@@ -3,6 +3,7 @@ Set the value of a field in a nested dictionary.
3
3
  '''
4
4
 
5
5
  from collections import OrderedDict
6
+ from icecream import ic
6
7
 
7
8
  def set_field_value(
8
9
  data: OrderedDict,
@@ -1,2 +0,0 @@
1
- __version__ = "0.2.7"
2
- __version_tuple__ = (0, 2, 7)
@@ -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