python-table-converter 0.2.13__tar.gz → 0.2.14__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.13 → python_table_converter-0.2.14}/PKG-INFO +2 -1
- {python_table_converter-0.2.13 → python_table_converter-0.2.14}/pyproject.toml +2 -1
- python_table_converter-0.2.14/table_converter/__init__.py +2 -0
- {python_table_converter-0.2.13 → python_table_converter-0.2.14}/table_converter/commands/convert_tables.py +9 -36
- python_table_converter-0.2.14/table_converter/core/actions.py +423 -0
- python_table_converter-0.2.14/table_converter/core/config.py +365 -0
- python_table_converter-0.2.14/table_converter/core/convert.py +322 -0
- python_table_converter-0.2.14/table_converter/core/functions/assign_id.py +75 -0
- python_table_converter-0.2.13/table_converter/core/functions/flatten.py → python_table_converter-0.2.14/table_converter/core/functions/flatten_row.py +2 -2
- python_table_converter-0.2.13/table_converter/core/functions/get_field_value.py → python_table_converter-0.2.14/table_converter/core/functions/get_nested_field_value.py +2 -2
- python_table_converter-0.2.14/table_converter/core/functions/nest_row.py +25 -0
- {python_table_converter-0.2.13 → python_table_converter-0.2.14}/table_converter/core/functions/search_column_value.py +11 -10
- python_table_converter-0.2.14/table_converter/core/functions/set_flat_field_value.py +23 -0
- python_table_converter-0.2.13/table_converter/core/functions/set_field_value.py → python_table_converter-0.2.14/table_converter/core/functions/set_nested_field_value.py +3 -3
- python_table_converter-0.2.14/table_converter/core/functions/set_row_value.py +33 -0
- python_table_converter-0.2.14/table_converter/core/types.py +100 -0
- python_table_converter-0.2.13/table_converter/__init__.py +0 -2
- python_table_converter-0.2.13/table_converter/core/config.py +0 -215
- python_table_converter-0.2.13/table_converter/core/convert.py +0 -386
- python_table_converter-0.2.13/table_converter/core/functions/assign_id.py +0 -112
- {python_table_converter-0.2.13 → python_table_converter-0.2.14}/LICENSE +0 -0
- {python_table_converter-0.2.13 → python_table_converter-0.2.14}/README.md +0 -0
- {python_table_converter-0.2.13 → python_table_converter-0.2.14}/table_converter/cli.py +0 -0
- {python_table_converter-0.2.13 → python_table_converter-0.2.14}/table_converter/core/constants.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-table-converter
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.14
|
|
4
4
|
Summary: A table data converter
|
|
5
5
|
Home-page: https://github.com/akivajp/python-table-converter
|
|
6
6
|
License: MIT
|
|
@@ -18,6 +18,7 @@ Requires-Dist: openpyxl (>=3.1.5,<4.0.0)
|
|
|
18
18
|
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
|
19
19
|
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
20
20
|
Requires-Dist: tqdm (>=4.67.0,<5.0.0)
|
|
21
|
+
Requires-Dist: xlsxwriter (>=3.2.0,<4.0.0)
|
|
21
22
|
Project-URL: Repository, https://github.com/akivajp/python-table-converter
|
|
22
23
|
Description-Content-Type: text/markdown
|
|
23
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-table-converter"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.14"
|
|
4
4
|
description = "A table data converter"
|
|
5
5
|
authors = ["Akiva Miura <akiva.miura@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -26,6 +26,7 @@ tqdm = "^4.67.0"
|
|
|
26
26
|
pandas = "^2.2.3"
|
|
27
27
|
openpyxl = "^3.1.5"
|
|
28
28
|
pyyaml = "^6.0.2"
|
|
29
|
+
xlsxwriter = "^3.2.0"
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
[build-system]
|
|
@@ -13,14 +13,10 @@ def run(
|
|
|
13
13
|
input_files = args.input_files,
|
|
14
14
|
output_file = args.output_file,
|
|
15
15
|
config_path = args.config,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
str_filters = args.filters,
|
|
19
|
-
pickup_columns= args.pickup_columns,
|
|
20
|
-
fields_to_split_by_newline = args.split_by_newline,
|
|
21
|
-
fields_to_assign_ids = args.assign_ids,
|
|
22
|
-
str_omit_fields= args.omit_fields,
|
|
16
|
+
list_actions = args.do_actions,
|
|
17
|
+
list_pick_columns = args.pick_columns,
|
|
23
18
|
output_debug = args.output_debug,
|
|
19
|
+
verbose = args.verbose,
|
|
24
20
|
)
|
|
25
21
|
|
|
26
22
|
def setup_parser(
|
|
@@ -44,39 +40,16 @@ def setup_parser(
|
|
|
44
40
|
help='Path to the configuration file.',
|
|
45
41
|
)
|
|
46
42
|
parser.add_argument(
|
|
47
|
-
'--
|
|
43
|
+
'--pick-columns', '--pick',
|
|
48
44
|
type=str,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
parser.add_argument(
|
|
52
|
-
'--split-by-newline',
|
|
53
|
-
type=str,
|
|
54
|
-
help='Fields to split by newline',
|
|
55
|
-
)
|
|
56
|
-
parser.add_argument(
|
|
57
|
-
'--assign-ids',
|
|
58
|
-
type=str,
|
|
59
|
-
help='Field to assign ids',
|
|
60
|
-
)
|
|
61
|
-
parser.add_argument(
|
|
62
|
-
'--assign-constants',
|
|
63
|
-
type=str,
|
|
64
|
-
help='Field to assign constants',
|
|
65
|
-
)
|
|
66
|
-
parser.add_argument(
|
|
67
|
-
'--assign-formats',
|
|
68
|
-
type=str,
|
|
69
|
-
help='Field to assign formats',
|
|
70
|
-
)
|
|
71
|
-
parser.add_argument(
|
|
72
|
-
'--filters', '--filter', '-f',
|
|
73
|
-
type=str,
|
|
74
|
-
help='Expression list to filter records',
|
|
45
|
+
nargs='+',
|
|
46
|
+
help='Pick column map',
|
|
75
47
|
)
|
|
76
48
|
parser.add_argument(
|
|
77
|
-
'--
|
|
49
|
+
'--do-actions', '--actions', '--do',
|
|
50
|
+
nargs='+',
|
|
78
51
|
type=str,
|
|
79
|
-
help='
|
|
52
|
+
help='Actions to do',
|
|
80
53
|
)
|
|
81
54
|
parser.add_argument(
|
|
82
55
|
'--output-debug',
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Actions are used to transform the data in the table.
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
from collections import OrderedDict
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import (
|
|
8
|
+
Any,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from icecream import ic
|
|
12
|
+
|
|
13
|
+
from . config import (
|
|
14
|
+
Config,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from . constants import (
|
|
18
|
+
INPUT_FIELD,
|
|
19
|
+
STAGING_FIELD,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from . types import (
|
|
23
|
+
AssignConstantConfig,
|
|
24
|
+
AssignFormatConfig,
|
|
25
|
+
AssignIdConfig,
|
|
26
|
+
FilterConfig,
|
|
27
|
+
GlobalStatus,
|
|
28
|
+
JoinConfig,
|
|
29
|
+
OmitConfig,
|
|
30
|
+
PickConfig,
|
|
31
|
+
SplitConfig,
|
|
32
|
+
Row,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
from . functions.assign_id import assign_id
|
|
36
|
+
from . functions.flatten_row import flatten_row
|
|
37
|
+
from . functions.nest_row import nest_row
|
|
38
|
+
from . functions.search_column_value import search_column_value
|
|
39
|
+
from . functions.set_flat_field_value import set_flat_field_value
|
|
40
|
+
from . functions.set_row_value import (
|
|
41
|
+
set_row_staging_value,
|
|
42
|
+
)
|
|
43
|
+
from . functions.set_nested_field_value import set_nested_field_value
|
|
44
|
+
|
|
45
|
+
def setup_actions_with_args(
|
|
46
|
+
config: Config,
|
|
47
|
+
list_actions: list[str],
|
|
48
|
+
):
|
|
49
|
+
ic(list_actions)
|
|
50
|
+
for str_action in list_actions:
|
|
51
|
+
fields = str_action.split(':')
|
|
52
|
+
if len(fields) >= 1:
|
|
53
|
+
action_name = fields[0].strip()
|
|
54
|
+
if action_name == 'assign-format':
|
|
55
|
+
setup_assign_format_action(config, str_action)
|
|
56
|
+
continue
|
|
57
|
+
if action_name == 'filter':
|
|
58
|
+
setup_filter_action(config, str_action)
|
|
59
|
+
continue
|
|
60
|
+
if len(fields) not in [2,3]:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f'Action must have 2 or 3 colon-separated fields: {str_action}'
|
|
63
|
+
)
|
|
64
|
+
str_fields = fields[1].strip()
|
|
65
|
+
if len(fields) == 3:
|
|
66
|
+
str_options = fields[2].strip()
|
|
67
|
+
else:
|
|
68
|
+
str_options = ''
|
|
69
|
+
options = OrderedDict()
|
|
70
|
+
if str_options:
|
|
71
|
+
for str_option in str_options.split(','):
|
|
72
|
+
if '=' in str_option:
|
|
73
|
+
key, value = str_option.split('=')
|
|
74
|
+
options[key.strip()] = value.strip()
|
|
75
|
+
else:
|
|
76
|
+
options[str_option.strip()] = True
|
|
77
|
+
fields = str_fields.split(',')
|
|
78
|
+
for field in fields:
|
|
79
|
+
if '=' in field:
|
|
80
|
+
target, source = field.split('=')
|
|
81
|
+
target = target.strip()
|
|
82
|
+
source = source.strip()
|
|
83
|
+
else:
|
|
84
|
+
target = field.strip()
|
|
85
|
+
source = field.strip()
|
|
86
|
+
if action_name == 'assign-constant':
|
|
87
|
+
str_type = options.get('type', 'str')
|
|
88
|
+
if str_type in ['str', 'string']:
|
|
89
|
+
value = source
|
|
90
|
+
elif str_type in ['int', 'integer']:
|
|
91
|
+
value = int(source)
|
|
92
|
+
elif str_type == 'float':
|
|
93
|
+
value = float(source)
|
|
94
|
+
elif str_type in ['bool', 'boolean']:
|
|
95
|
+
value = bool(source)
|
|
96
|
+
else:
|
|
97
|
+
raise ValueError(
|
|
98
|
+
f'Unsupported type: {str_type}'
|
|
99
|
+
)
|
|
100
|
+
config.actions.append(AssignConstantConfig(
|
|
101
|
+
target = target,
|
|
102
|
+
value = value,
|
|
103
|
+
))
|
|
104
|
+
continue
|
|
105
|
+
if action_name == 'assign-id':
|
|
106
|
+
context = options.get('context', None)
|
|
107
|
+
if context:
|
|
108
|
+
context = context.split(',')
|
|
109
|
+
config.actions.append(AssignIdConfig(
|
|
110
|
+
target = target,
|
|
111
|
+
primary = [source],
|
|
112
|
+
context = context,
|
|
113
|
+
))
|
|
114
|
+
continue
|
|
115
|
+
if action_name == 'join':
|
|
116
|
+
delimiter = options.get('delimiter', None)
|
|
117
|
+
config.actions.append(JoinConfig(
|
|
118
|
+
target = target,
|
|
119
|
+
source = source,
|
|
120
|
+
delimiter = delimiter,
|
|
121
|
+
))
|
|
122
|
+
continue
|
|
123
|
+
if action_name == 'omit':
|
|
124
|
+
config.actions.append(OmitConfig(
|
|
125
|
+
field = target,
|
|
126
|
+
))
|
|
127
|
+
continue
|
|
128
|
+
if action_name == 'split':
|
|
129
|
+
delimiter = options.get('delimiter', None)
|
|
130
|
+
config.actions.append(SplitConfig(
|
|
131
|
+
target = target,
|
|
132
|
+
source = source,
|
|
133
|
+
delimiter = delimiter,
|
|
134
|
+
))
|
|
135
|
+
continue
|
|
136
|
+
raise ValueError(
|
|
137
|
+
f'Unsupported action: {action_name}'
|
|
138
|
+
)
|
|
139
|
+
return config
|
|
140
|
+
|
|
141
|
+
def setup_assign_format_action(
|
|
142
|
+
config: Config,
|
|
143
|
+
str_action: str,
|
|
144
|
+
):
|
|
145
|
+
action_fields = str_action.split(':', 1)
|
|
146
|
+
if len(action_fields) != 2:
|
|
147
|
+
raise ValueError(
|
|
148
|
+
f'Expected 2 fields separated by ":": {str_action}'
|
|
149
|
+
)
|
|
150
|
+
action_name = action_fields[0].strip()
|
|
151
|
+
assert action_name == 'assign-format'
|
|
152
|
+
assignment_fields = action_fields[1].split('=')
|
|
153
|
+
if len(assignment_fields) != 2:
|
|
154
|
+
raise ValueError(
|
|
155
|
+
f'Expected 2 fields separated by "=": {action_fields[1]}'
|
|
156
|
+
)
|
|
157
|
+
target = assignment_fields[0].strip()
|
|
158
|
+
format = assignment_fields[1].strip()
|
|
159
|
+
config.actions.append(AssignFormatConfig(
|
|
160
|
+
target = target,
|
|
161
|
+
format = format,
|
|
162
|
+
))
|
|
163
|
+
return config
|
|
164
|
+
|
|
165
|
+
def setup_filter_action(
|
|
166
|
+
config: Config,
|
|
167
|
+
str_action: str,
|
|
168
|
+
):
|
|
169
|
+
action_fields = str_action.split(':', 1)
|
|
170
|
+
if len(action_fields) != 2:
|
|
171
|
+
raise ValueError(
|
|
172
|
+
f'Expected 2 fields separated by ":": {str_action}'
|
|
173
|
+
)
|
|
174
|
+
action_name = action_fields[0].strip()
|
|
175
|
+
assert action_name == 'filter'
|
|
176
|
+
str_filter = action_fields[1].strip()
|
|
177
|
+
if '==' in str_filter:
|
|
178
|
+
field, value = str_filter.split('==')
|
|
179
|
+
config.actions.append(FilterConfig(
|
|
180
|
+
field = field.strip(),
|
|
181
|
+
operator = '==',
|
|
182
|
+
value = value.strip(),
|
|
183
|
+
))
|
|
184
|
+
return config
|
|
185
|
+
if '!=' in str_filter:
|
|
186
|
+
field, value = str_filter.split('!=')
|
|
187
|
+
config.actions.append(FilterConfig(
|
|
188
|
+
field = field.strip(),
|
|
189
|
+
operator = '!=',
|
|
190
|
+
value = value.strip(),
|
|
191
|
+
))
|
|
192
|
+
return config
|
|
193
|
+
raise ValueError(
|
|
194
|
+
f'Unsupported filter: {str_filter}'
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def do_actions(
|
|
198
|
+
status: GlobalStatus,
|
|
199
|
+
row: Row,
|
|
200
|
+
actions: list[AssignConstantConfig],
|
|
201
|
+
):
|
|
202
|
+
for action in actions:
|
|
203
|
+
row = do_action(status, row, action)
|
|
204
|
+
if row is None:
|
|
205
|
+
return None
|
|
206
|
+
return row
|
|
207
|
+
|
|
208
|
+
def do_action(
|
|
209
|
+
status: GlobalStatus,
|
|
210
|
+
row: Row,
|
|
211
|
+
action: AssignConstantConfig,
|
|
212
|
+
):
|
|
213
|
+
if isinstance(action, AssignConstantConfig):
|
|
214
|
+
return assign_constant(row, action)
|
|
215
|
+
if isinstance(action, AssignFormatConfig):
|
|
216
|
+
return assign_format(row, action)
|
|
217
|
+
if isinstance(action, AssignIdConfig):
|
|
218
|
+
return assign_id(status.id_context_map, row, action)
|
|
219
|
+
if isinstance(action, FilterConfig):
|
|
220
|
+
if filter_row(row, action):
|
|
221
|
+
return row
|
|
222
|
+
return None
|
|
223
|
+
if isinstance(action, JoinConfig):
|
|
224
|
+
return join_field(row, action)
|
|
225
|
+
if isinstance(action, OmitConfig):
|
|
226
|
+
return omit_field(row, action)
|
|
227
|
+
if isinstance(action, SplitConfig):
|
|
228
|
+
return split_field(row, action)
|
|
229
|
+
raise ValueError(
|
|
230
|
+
f'Unsupported action: {action}'
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
def prepare_row(
|
|
234
|
+
flat_row: OrderedDict | None = None,
|
|
235
|
+
):
|
|
236
|
+
if flat_row is None:
|
|
237
|
+
flat_row = OrderedDict()
|
|
238
|
+
nested_row = nest_row(flat_row)
|
|
239
|
+
return Row(
|
|
240
|
+
flat = OrderedDict(flat_row),
|
|
241
|
+
nested = nested_row,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
def delete_flat_row_value(
|
|
245
|
+
flat_row: OrderedDict,
|
|
246
|
+
target: str,
|
|
247
|
+
):
|
|
248
|
+
prefix = f'{target}.'
|
|
249
|
+
for key in list(flat_row.keys()):
|
|
250
|
+
if key == target or key.startswith(prefix):
|
|
251
|
+
del flat_row[key]
|
|
252
|
+
|
|
253
|
+
def pop_nested_row_value(
|
|
254
|
+
nested_row: OrderedDict,
|
|
255
|
+
key: str,
|
|
256
|
+
default: Any = None,
|
|
257
|
+
):
|
|
258
|
+
keys = key.split('.')
|
|
259
|
+
for key in keys[:-1]:
|
|
260
|
+
if key not in nested_row:
|
|
261
|
+
return default, False
|
|
262
|
+
nested_row = nested_row[key]
|
|
263
|
+
return nested_row.pop(keys[-1], default), True
|
|
264
|
+
|
|
265
|
+
def pop_row_value(
|
|
266
|
+
row: Row,
|
|
267
|
+
key: str,
|
|
268
|
+
default: Any = None,
|
|
269
|
+
):
|
|
270
|
+
delete_flat_row_value(row.flat, key)
|
|
271
|
+
return pop_nested_row_value(row.nested, key, default)
|
|
272
|
+
|
|
273
|
+
def pop_row_staging(
|
|
274
|
+
row: Row,
|
|
275
|
+
default: Any = None,
|
|
276
|
+
):
|
|
277
|
+
return pop_row_value(row, STAGING_FIELD, default)
|
|
278
|
+
|
|
279
|
+
def assign_constant(
|
|
280
|
+
row: Row,
|
|
281
|
+
config: AssignConstantConfig,
|
|
282
|
+
):
|
|
283
|
+
set_row_staging_value(row, config.target, config.value)
|
|
284
|
+
return row
|
|
285
|
+
|
|
286
|
+
def split_field(
|
|
287
|
+
row: Row,
|
|
288
|
+
config: SplitConfig,
|
|
289
|
+
):
|
|
290
|
+
value, found = search_column_value(row.flat, config.source)
|
|
291
|
+
if found:
|
|
292
|
+
if isinstance(value, str):
|
|
293
|
+
new_value = value.split(config.delimiter)
|
|
294
|
+
new_value = list(filter(None, new_value))
|
|
295
|
+
value = new_value
|
|
296
|
+
set_row_staging_value(row, config.target, value)
|
|
297
|
+
return row
|
|
298
|
+
|
|
299
|
+
def remap_columns(
|
|
300
|
+
row: Row,
|
|
301
|
+
list_config: list[PickConfig],
|
|
302
|
+
):
|
|
303
|
+
if not list_config:
|
|
304
|
+
list_config = []
|
|
305
|
+
for key in row.nested[STAGING_FIELD][INPUT_FIELD].keys():
|
|
306
|
+
list_config.append(PickConfig(
|
|
307
|
+
source = key,
|
|
308
|
+
target = key,
|
|
309
|
+
))
|
|
310
|
+
new_flat_row = OrderedDict()
|
|
311
|
+
picked = []
|
|
312
|
+
for config in list_config:
|
|
313
|
+
value, key = search_column_value(row.nested, config.source)
|
|
314
|
+
if key:
|
|
315
|
+
set_flat_field_value(new_flat_row, config.target, value)
|
|
316
|
+
picked.append(key)
|
|
317
|
+
for key in row.flat.keys():
|
|
318
|
+
if key in picked:
|
|
319
|
+
if not key.startswith(f'{STAGING_FIELD}.{INPUT_FIELD}.'):
|
|
320
|
+
continue
|
|
321
|
+
if key in new_flat_row:
|
|
322
|
+
continue
|
|
323
|
+
if key.startswith(f'{STAGING_FIELD}.'):
|
|
324
|
+
# NOTE: Skip staging fields
|
|
325
|
+
new_flat_row[key] = row.flat[key]
|
|
326
|
+
else:
|
|
327
|
+
input_key = f'{STAGING_FIELD}.{INPUT_FIELD}.{key}'
|
|
328
|
+
if input_key in row.flat:
|
|
329
|
+
value = row.flat[key]
|
|
330
|
+
input_value = row.flat[input_key]
|
|
331
|
+
if value == input_value:
|
|
332
|
+
# NOTE: Skip if the same value in the input field
|
|
333
|
+
continue
|
|
334
|
+
# NOTE: Set the unused value to the staging field
|
|
335
|
+
new_flat_row[f'{STAGING_FIELD}.{key}'] = row.flat[key]
|
|
336
|
+
row.flat = new_flat_row
|
|
337
|
+
row.nested = nest_row(new_flat_row)
|
|
338
|
+
return row
|
|
339
|
+
|
|
340
|
+
def assign_format(
|
|
341
|
+
row: Row,
|
|
342
|
+
config: AssignFormatConfig,
|
|
343
|
+
):
|
|
344
|
+
template = config.format
|
|
345
|
+
params = {}
|
|
346
|
+
for key, value in row.flat.items():
|
|
347
|
+
for prefix in [
|
|
348
|
+
f'{STAGING_FIELD}.{INPUT_FIELD}.',
|
|
349
|
+
f'{STAGING_FIELD}.',
|
|
350
|
+
]:
|
|
351
|
+
if key.startswith(prefix):
|
|
352
|
+
rest = key[len(prefix):]
|
|
353
|
+
params[rest] = value
|
|
354
|
+
params.update(row.flat)
|
|
355
|
+
formatted = None
|
|
356
|
+
while formatted is None:
|
|
357
|
+
try:
|
|
358
|
+
formatted = template.format(**params)
|
|
359
|
+
except KeyError as e:
|
|
360
|
+
#ic(e)
|
|
361
|
+
#ic(e.args)
|
|
362
|
+
#ic(e.args[0])
|
|
363
|
+
key = e.args[0]
|
|
364
|
+
params[key] = f'__{key}__undefined__'
|
|
365
|
+
except:
|
|
366
|
+
#ic(params)
|
|
367
|
+
ic(params.keys())
|
|
368
|
+
raise
|
|
369
|
+
set_row_staging_value(row, config.target, formatted)
|
|
370
|
+
return row
|
|
371
|
+
|
|
372
|
+
def filter_row(
|
|
373
|
+
row: Row,
|
|
374
|
+
config: list[FilterConfig],
|
|
375
|
+
):
|
|
376
|
+
value, found = search_column_value(row.nested, config.field)
|
|
377
|
+
#ic(config, value, found)
|
|
378
|
+
if config.operator == '==':
|
|
379
|
+
if not found:
|
|
380
|
+
return False
|
|
381
|
+
if value != config.value and str(value) != str(config.value):
|
|
382
|
+
return False
|
|
383
|
+
elif config.operator == '!=':
|
|
384
|
+
if str(value) == str(config.value) or value == config.value:
|
|
385
|
+
return False
|
|
386
|
+
elif config.operator == 'not-in':
|
|
387
|
+
if isinstance(config.value, list):
|
|
388
|
+
if value in config.value:
|
|
389
|
+
return False
|
|
390
|
+
if str(value) in config.value:
|
|
391
|
+
return False
|
|
392
|
+
else:
|
|
393
|
+
raise ValueError(f'Unsupported filter value type: type{config.value}')
|
|
394
|
+
else:
|
|
395
|
+
raise ValueError(f'Unsupported operator: {config.operator}')
|
|
396
|
+
return True
|
|
397
|
+
|
|
398
|
+
def omit_field(
|
|
399
|
+
row: Row,
|
|
400
|
+
config: OmitConfig,
|
|
401
|
+
):
|
|
402
|
+
value, found = pop_row_value(row, config.field)
|
|
403
|
+
if not found:
|
|
404
|
+
return row
|
|
405
|
+
if f'{STAGING_FIELD}.{config.field}' not in row.flat:
|
|
406
|
+
set_row_staging_value(row, config.field, value)
|
|
407
|
+
return row
|
|
408
|
+
|
|
409
|
+
def join_field(
|
|
410
|
+
row: Row,
|
|
411
|
+
config: JoinConfig,
|
|
412
|
+
):
|
|
413
|
+
value, found = search_column_value(row.nested, config.source)
|
|
414
|
+
if found:
|
|
415
|
+
delimiter = config.delimiter
|
|
416
|
+
if delimiter is None:
|
|
417
|
+
delimiter = ';'
|
|
418
|
+
if delimiter == '\\n':
|
|
419
|
+
delimiter = '\n'
|
|
420
|
+
if isinstance(value, list):
|
|
421
|
+
value = delimiter.join(value)
|
|
422
|
+
set_row_staging_value(row, config.target, value)
|
|
423
|
+
return row
|