excel-list-transform 0.5__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.
- excel_list_transform/__init__.py +1 -0
- excel_list_transform/__main__.py +11 -0
- excel_list_transform/check_indata_common.py +39 -0
- excel_list_transform/commontypes.py +59 -0
- excel_list_transform/config.py +358 -0
- excel_list_transform/config_enums.py +53 -0
- excel_list_transform/config_excel_list_transform.py +278 -0
- excel_list_transform/config_factory.py +95 -0
- excel_list_transform/config_xls_list_refmt_name.py +40 -0
- excel_list_transform/config_xls_list_refmt_num.py +70 -0
- excel_list_transform/file_extension.py +30 -0
- excel_list_transform/file_must_exist.py +22 -0
- excel_list_transform/generate_cfg.py +374 -0
- excel_list_transform/generate_txt.py +259 -0
- excel_list_transform/handle_csv.py +73 -0
- excel_list_transform/handle_empty_column.py +28 -0
- excel_list_transform/handle_excel.py +161 -0
- excel_list_transform/num_named_conversion.py +60 -0
- excel_list_transform/rewrite_value.py +123 -0
- excel_list_transform/str_to_enum.py +39 -0
- excel_list_transform/transform_cmd.py +146 -0
- excel_list_transform/transform_func.py +50 -0
- excel_list_transform/transform_func_common.py +249 -0
- excel_list_transform/transform_func_named.py +136 -0
- excel_list_transform/transform_func_num.py +157 -0
- excel_list_transform-0.5.dist-info/LICENSE.txt +22 -0
- excel_list_transform-0.5.dist-info/METADATA +114 -0
- excel_list_transform-0.5.dist-info/RECORD +30 -0
- excel_list_transform-0.5.dist-info/WHEEL +5 -0
- excel_list_transform-0.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
#! /usr/local/bin/python3
|
|
2
|
+
"""Generate example configuration files."""
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2024 Tom Björkholm
|
|
5
|
+
# MIT License
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from excel_list_transform.file_extension import fix_file_extension
|
|
9
|
+
from excel_list_transform.config_enums import ColumnRef, FileType, \
|
|
10
|
+
RewriteKind, CaseSensitivity, ExcelLib
|
|
11
|
+
from excel_list_transform.config_factory import config_factory_from_enum
|
|
12
|
+
from excel_list_transform.generate_txt import generate_syntax_txt
|
|
13
|
+
from excel_list_transform.config_xls_list_refmt_name import \
|
|
14
|
+
ConfigXlsListRefmtName
|
|
15
|
+
from excel_list_transform.config_xls_list_refmt_num import \
|
|
16
|
+
ConfigXlsListRefmtNum
|
|
17
|
+
|
|
18
|
+
syntax_o2x_common: str = '''
|
|
19
|
+
|
|
20
|
+
Column names are changed from another language (Swedish) and sometimes from
|
|
21
|
+
a more instuctive naming to the names understood by RRS.
|
|
22
|
+
|
|
23
|
+
The events does not use divisions, boat names and WhatsApp. Thus the
|
|
24
|
+
form did not ask for these. They are inserted as empty columns to please
|
|
25
|
+
RRS.
|
|
26
|
+
|
|
27
|
+
The phone number has to be in international format '+' followed by only
|
|
28
|
+
digits for RRS, but the sailors filling in the form sometimes use
|
|
29
|
+
national format (no + and no country code). Also the sailors filling
|
|
30
|
+
in the form are used to group digits by dashes and spaces for easy
|
|
31
|
+
reading. The s7_rewrite_columns include rewrite instructions to
|
|
32
|
+
"fix" the phone number to the format needed by RRS.
|
|
33
|
+
The rewriting of phone numbers in this example is for Swedish mobile
|
|
34
|
+
phone numbers. For other countries s7_rewrite_columns need to be
|
|
35
|
+
adjusted.
|
|
36
|
+
'''
|
|
37
|
+
|
|
38
|
+
syntax_only_o2r_common: str = '''
|
|
39
|
+
This an example of taking a registration list from the excel file received
|
|
40
|
+
from office forms or google forms and transforming it for importing into
|
|
41
|
+
RRS ( https://www.racingrulesofsailing.org ).
|
|
42
|
+
'''
|
|
43
|
+
|
|
44
|
+
by_name_common: str = '''
|
|
45
|
+
|
|
46
|
+
s8_column_order both tells the order the columns shall be written
|
|
47
|
+
in, and what columns to write out. The columns not listed here
|
|
48
|
+
are ignored (and thus removed from the output).
|
|
49
|
+
|
|
50
|
+
This configuration files references columns "BY_NAME", which is the
|
|
51
|
+
preferred way to reference columns.
|
|
52
|
+
'''
|
|
53
|
+
|
|
54
|
+
by_number_common: str = '''
|
|
55
|
+
|
|
56
|
+
This configuration files references columns "BY_NUMBER".
|
|
57
|
+
The preferred way to reference columns in configuration files
|
|
58
|
+
is "BY_NAME".
|
|
59
|
+
'''
|
|
60
|
+
|
|
61
|
+
syntax_o2r_common: str = syntax_only_o2r_common + syntax_o2x_common
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def generate_syntax_o2r_name(filename: str, colref: ColumnRef) -> None:
|
|
65
|
+
"""Generate config example for office_forms_to_rrs."""
|
|
66
|
+
assert colref == ColumnRef.BY_NAME
|
|
67
|
+
cfg = ConfigXlsListRefmtName()
|
|
68
|
+
cfg.out_excel_library = ExcelLib.OPENPYXL
|
|
69
|
+
cfg.in_type = FileType.EXCEL
|
|
70
|
+
cfg.out_type = FileType.EXCEL
|
|
71
|
+
cfg.s1_split_columns = []
|
|
72
|
+
cfg.s3_merge_columns = []
|
|
73
|
+
cfg.s5_rename_columns = [
|
|
74
|
+
{'column': 'Klass', 'name': 'Class'},
|
|
75
|
+
{'column': 'Nationalitetsbokstäver i seglet', 'name': 'Nationality'},
|
|
76
|
+
{'column': 'Segelnummer (endast siffror)', 'name': 'Sail Number'},
|
|
77
|
+
{'column': 'Förnamn', 'name': 'First Name'},
|
|
78
|
+
{'column': 'Efternamn', 'name': 'Last Name'},
|
|
79
|
+
{'column': 'Klubb', 'name': 'Club Name'},
|
|
80
|
+
{'column': 'Epostadress', 'name': 'Email'},
|
|
81
|
+
{'column': 'Mobiltelefonnummer', 'name': 'Phone'}]
|
|
82
|
+
cfg.s6_insert_columns = [
|
|
83
|
+
{'column': 'Division', 'value': None},
|
|
84
|
+
{'column': 'Boat Name', 'value': None},
|
|
85
|
+
{'column': 'WhatsApp', 'value': None}]
|
|
86
|
+
cfg.s7_rewrite_columns = [
|
|
87
|
+
{'column': 'Phone', 'kind': RewriteKind.STRIP,
|
|
88
|
+
'chars': '', 'case': CaseSensitivity.IGNORE_CASE},
|
|
89
|
+
{'column': 'Phone', 'kind': RewriteKind.REMOVECHARS,
|
|
90
|
+
'chars': [' ', '-'], 'case': CaseSensitivity.MATCH_CASE},
|
|
91
|
+
{'column': 'Phone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
92
|
+
'from': '^07', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
93
|
+
{'column': 'Phone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
94
|
+
'from': '^\\+4607', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
95
|
+
{'column': 'Phone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
96
|
+
'from': '^467', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
97
|
+
{'column': 'Phone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
98
|
+
'from': '^4607', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE}]
|
|
99
|
+
cfg.s8_column_order = ['Class', 'Division', 'Nationality',
|
|
100
|
+
'Sail Number', 'Boat Name', 'First Name',
|
|
101
|
+
'Last Name', 'Club Name', 'Email', 'Phone',
|
|
102
|
+
'WhatsApp']
|
|
103
|
+
cfg.write(to_json_filename=filename)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
TXT_O2R_NAME = syntax_o2r_common + by_name_common
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def generate_syntax_o2r_num(filename: str, colref: ColumnRef) -> None:
|
|
110
|
+
"""Generate config example for office_forms_to_rrs."""
|
|
111
|
+
assert colref == ColumnRef.BY_NUMBER
|
|
112
|
+
cfg = ConfigXlsListRefmtNum()
|
|
113
|
+
cfg.out_excel_library = ExcelLib.OPENPYXL
|
|
114
|
+
cfg.in_type = FileType.EXCEL
|
|
115
|
+
cfg.out_type = FileType.EXCEL
|
|
116
|
+
cfg.s1_split_columns = []
|
|
117
|
+
cfg.s2_remove_columns = [0, 1, 2, 3, 4, 5, 14, 15, 16, 17, 18, 19]
|
|
118
|
+
cfg.s3_merge_columns = []
|
|
119
|
+
cfg.s4_place_columns_first = [5, 6, 7, 0, 1, 4, 2, 3]
|
|
120
|
+
cfg.s5_rename_columns = [
|
|
121
|
+
{'column': 0, 'name': 'Class'},
|
|
122
|
+
{'column': 1, 'name': 'Nationality'},
|
|
123
|
+
{'column': 2, 'name': 'Sail Number'},
|
|
124
|
+
{'column': 3, 'name': 'First Name'},
|
|
125
|
+
{'column': 4, 'name': 'Last Name'},
|
|
126
|
+
{'column': 5, 'name': 'Club Name'},
|
|
127
|
+
{'column': 6, 'name': 'Email'},
|
|
128
|
+
{'column': 7, 'name': 'Phone'}]
|
|
129
|
+
cfg.s6_insert_columns = [
|
|
130
|
+
{'column': 1, 'name': 'Division', 'value': None},
|
|
131
|
+
{'column': 4, 'name': 'Boat Name', 'value': None},
|
|
132
|
+
{'column': 10, 'name': 'WhatsApp', 'value': None}]
|
|
133
|
+
cfg.s7_rewrite_columns = [
|
|
134
|
+
{'column': 9, 'kind': RewriteKind.STRIP,
|
|
135
|
+
'chars': '', 'case': CaseSensitivity.IGNORE_CASE},
|
|
136
|
+
{'column': 9, 'kind': RewriteKind.REMOVECHARS,
|
|
137
|
+
'chars': [' ', '-'], 'case': CaseSensitivity.MATCH_CASE},
|
|
138
|
+
{'column': 9, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
139
|
+
'from': '^07', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
140
|
+
{'column': 9, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
141
|
+
'from': '^\\+4607', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
142
|
+
{'column': 9, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
143
|
+
'from': '^467', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
144
|
+
{'column': 9, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
145
|
+
'from': '^4607', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE}]
|
|
146
|
+
cfg.write(to_json_filename=filename)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
TXT_O2R_NUM = syntax_o2r_common + by_number_common
|
|
150
|
+
|
|
151
|
+
syntax_only_o2s_common: str = '''
|
|
152
|
+
This an example of taking a registration list from the excel file received
|
|
153
|
+
from office forms or google forms and transforming it for importing into
|
|
154
|
+
SailWave regatta scoring program ( https://www.sailwave.com ).
|
|
155
|
+
'''
|
|
156
|
+
|
|
157
|
+
syntax_o2s_common: str = syntax_only_o2s_common + syntax_o2x_common
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def generate_syntax_o2s_name(filename: str, colref: ColumnRef) -> None:
|
|
161
|
+
"""Generate config example for office_forms_to_sw."""
|
|
162
|
+
assert colref == ColumnRef.BY_NAME
|
|
163
|
+
cfg = ConfigXlsListRefmtName()
|
|
164
|
+
cfg.out_excel_library = ExcelLib.OPENPYXL
|
|
165
|
+
cfg.in_type = FileType.EXCEL
|
|
166
|
+
cfg.out_type = FileType.CSV
|
|
167
|
+
cfg.s1_split_columns = []
|
|
168
|
+
cfg.s3_merge_columns = [{'columns': ['Förnamn', 'Efternamn'],
|
|
169
|
+
'separator': ' '}]
|
|
170
|
+
cfg.s5_rename_columns = [
|
|
171
|
+
{'column': 'Klass', 'name': 'Class'},
|
|
172
|
+
{'column': 'Nationalitetsbokstäver i seglet', 'name': 'Nat'},
|
|
173
|
+
{'column': 'Segelnummer (endast siffror)', 'name': 'SailNo'},
|
|
174
|
+
{'column': 'Förnamn', 'name': 'HelmName'},
|
|
175
|
+
{'column': 'Klubb', 'name': 'Club'},
|
|
176
|
+
{'column': 'Epostadress', 'name': 'HelmEmail'},
|
|
177
|
+
{'column': 'Mobiltelefonnummer', 'name': 'HelmPhone'}]
|
|
178
|
+
cfg.s6_insert_columns = [
|
|
179
|
+
{'column': 'Division', 'value': None},
|
|
180
|
+
{'column': 'Boat', 'value': None}]
|
|
181
|
+
cfg.s7_rewrite_columns = [
|
|
182
|
+
{'column': 'HelmPhone', 'kind': RewriteKind.STRIP,
|
|
183
|
+
'chars': '', 'case': CaseSensitivity.IGNORE_CASE},
|
|
184
|
+
{'column': 'HelmPhone', 'kind': RewriteKind.REMOVECHARS,
|
|
185
|
+
'chars': [' ', '-'], 'case': CaseSensitivity.MATCH_CASE},
|
|
186
|
+
{'column': 'HelmPhone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
187
|
+
'from': '^07', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
188
|
+
{'column': 'HelmPhone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
189
|
+
'from': '^\\+4607', 'to': '+467',
|
|
190
|
+
'case': CaseSensitivity.MATCH_CASE},
|
|
191
|
+
{'column': 'HelmPhone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
192
|
+
'from': '^467', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
193
|
+
{'column': 'HelmPhone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
194
|
+
'from': '^4607', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE}]
|
|
195
|
+
cfg.s8_column_order = ['Class', 'Division', 'Nat', 'SailNo', 'Boat',
|
|
196
|
+
'HelmName', 'Club', 'HelmEmail', 'HelmPhone']
|
|
197
|
+
cfg.write(to_json_filename=filename)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
TXT_O2S_NAME = syntax_o2s_common + by_name_common
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def generate_syntax_o2s_num(filename: str, colref: ColumnRef) -> None:
|
|
204
|
+
"""Generate config example for office_forms_to_sw."""
|
|
205
|
+
assert colref == ColumnRef.BY_NUMBER
|
|
206
|
+
cfg = ConfigXlsListRefmtNum()
|
|
207
|
+
cfg.out_excel_library = ExcelLib.OPENPYXL
|
|
208
|
+
cfg.in_type = FileType.EXCEL
|
|
209
|
+
cfg.out_type = FileType.CSV
|
|
210
|
+
cfg.s1_split_columns = []
|
|
211
|
+
cfg.s2_remove_columns = [0, 1, 2, 3, 4, 5, 14, 15, 16, 17, 18, 19]
|
|
212
|
+
cfg.s3_merge_columns = [{'columns': [0, 1], 'separator': ' '}]
|
|
213
|
+
cfg.s4_place_columns_first = [4, 5, 6, 0, 3, 1, 2]
|
|
214
|
+
cfg.s5_rename_columns = [
|
|
215
|
+
{'column': 0, 'name': 'Class'},
|
|
216
|
+
{'column': 1, 'name': 'Nat'},
|
|
217
|
+
{'column': 2, 'name': 'SailNo'},
|
|
218
|
+
{'column': 3, 'name': 'HelmName'},
|
|
219
|
+
{'column': 4, 'name': 'Club'},
|
|
220
|
+
{'column': 5, 'name': 'HelmEmail'},
|
|
221
|
+
{'column': 6, 'name': 'HelmPhone'}]
|
|
222
|
+
cfg.s6_insert_columns = [
|
|
223
|
+
{'column': 1, 'name': 'Division', 'value': None},
|
|
224
|
+
{'column': 4, 'name': 'Boat', 'value': None}]
|
|
225
|
+
cfg.s7_rewrite_columns = [
|
|
226
|
+
{'column': 8, 'kind': RewriteKind.STRIP,
|
|
227
|
+
'chars': '', 'case': CaseSensitivity.IGNORE_CASE},
|
|
228
|
+
{'column': 8, 'kind': RewriteKind.REMOVECHARS,
|
|
229
|
+
'chars': [' ', '-'], 'case': CaseSensitivity.MATCH_CASE},
|
|
230
|
+
{'column': 8, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
231
|
+
'from': '^07', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
232
|
+
{'column': 8, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
233
|
+
'from': '^\\+4607', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
234
|
+
{'column': 8, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
235
|
+
'from': '^467', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE},
|
|
236
|
+
{'column': 8, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
237
|
+
'from': '^4607', 'to': '+467', 'case': CaseSensitivity.MATCH_CASE}]
|
|
238
|
+
cfg.write(to_json_filename=filename)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
TXT_O2S_NUM = syntax_o2s_common + by_number_common
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
syntax_r2s_common: str = '''
|
|
245
|
+
This an example of taking a registration list from the excel file
|
|
246
|
+
exported from RRS ( https://www.racingrulesofsailing.org ) and
|
|
247
|
+
transforming it for importing into SailWave regatta scoring
|
|
248
|
+
program ( https://www.sailwave.com ).
|
|
249
|
+
|
|
250
|
+
The names of the columns are changed from RRS names to SailWave
|
|
251
|
+
names. The phone number exported from RRS is in international
|
|
252
|
+
format, except that the '+' is missing.
|
|
253
|
+
|
|
254
|
+
'''
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def generate_syntax_r2s_name(filename: str, colref: ColumnRef) -> None:
|
|
258
|
+
"""Generate config example for rrs_to_sw."""
|
|
259
|
+
assert colref == ColumnRef.BY_NAME
|
|
260
|
+
cfg = ConfigXlsListRefmtName()
|
|
261
|
+
cfg.out_excel_library = ExcelLib.OPENPYXL
|
|
262
|
+
cfg.in_type = FileType.EXCEL
|
|
263
|
+
cfg.out_type = FileType.CSV
|
|
264
|
+
cfg.s1_split_columns = []
|
|
265
|
+
cfg.s3_merge_columns = [{'columns': ['First Name', 'Last Name'],
|
|
266
|
+
'separator': ' '}]
|
|
267
|
+
cfg.s5_rename_columns = [
|
|
268
|
+
{'column': 'Nationality', 'name': 'Nat'},
|
|
269
|
+
{'column': 'Sail Number', 'name': 'SailNo'},
|
|
270
|
+
{'column': 'First Name', 'name': 'HelmName'},
|
|
271
|
+
{'column': 'Club Name', 'name': 'Club'},
|
|
272
|
+
{'column': 'Email', 'name': 'HelmEmail'},
|
|
273
|
+
{'column': 'Mobile Phone', 'name': 'HelmPhone'},
|
|
274
|
+
{'column': 'Boat Name', 'name': 'Boat'}]
|
|
275
|
+
cfg.s6_insert_columns = []
|
|
276
|
+
cfg.s7_rewrite_columns = [
|
|
277
|
+
{'column': 'HelmPhone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
278
|
+
'from': '^', 'to': '+', 'case': CaseSensitivity.MATCH_CASE},
|
|
279
|
+
{'column': 'HelmPhone', 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
280
|
+
'from': '^\\+\\+', 'to': '+', 'case': CaseSensitivity.MATCH_CASE}]
|
|
281
|
+
cfg.s8_column_order = ['Class', 'Division', 'Nat', 'SailNo', 'Boat',
|
|
282
|
+
'HelmName', 'Club', 'HelmEmail', 'HelmPhone']
|
|
283
|
+
cfg.write(to_json_filename=filename)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
TXT_R2S_NAME = syntax_r2s_common + by_name_common
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def generate_syntax_r2s_num(filename: str, colref: ColumnRef) -> None:
|
|
290
|
+
"""Generate config example for rrs_to_sw."""
|
|
291
|
+
assert colref == ColumnRef.BY_NUMBER
|
|
292
|
+
cfg = ConfigXlsListRefmtNum()
|
|
293
|
+
cfg.out_excel_library = ExcelLib.OPENPYXL
|
|
294
|
+
cfg.in_type = FileType.EXCEL
|
|
295
|
+
cfg.out_type = FileType.CSV
|
|
296
|
+
cfg.s1_split_columns = []
|
|
297
|
+
cfg.s2_remove_columns = [10] # remove WhatsApp
|
|
298
|
+
cfg.s3_merge_columns = [{'columns': [5, 6], 'separator': ' '}]
|
|
299
|
+
cfg.s4_place_columns_first = []
|
|
300
|
+
cfg.s5_rename_columns = [
|
|
301
|
+
{'column': 0, 'name': 'Class'},
|
|
302
|
+
{'column': 1, 'name': 'Division'},
|
|
303
|
+
{'column': 2, 'name': 'Nat'},
|
|
304
|
+
{'column': 3, 'name': 'SailNo'},
|
|
305
|
+
{'column': 4, 'name': 'Boat'},
|
|
306
|
+
{'column': 5, 'name': 'HelmName'},
|
|
307
|
+
{'column': 6, 'name': 'Club'},
|
|
308
|
+
{'column': 7, 'name': 'HelmEmail'},
|
|
309
|
+
{'column': 8, 'name': 'HelmPhone'}]
|
|
310
|
+
cfg.s6_insert_columns = []
|
|
311
|
+
cfg.s7_rewrite_columns = [
|
|
312
|
+
{'column': 8, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
313
|
+
'from': '^', 'to': '+', 'case': CaseSensitivity.MATCH_CASE},
|
|
314
|
+
{'column': 8, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
315
|
+
'from': '^\\+\\+', 'to': '+', 'case': CaseSensitivity.MATCH_CASE},
|
|
316
|
+
{'column': 8, 'kind': RewriteKind.REGEX_SUBSTITUTE,
|
|
317
|
+
'from': '^\\+H', 'to': 'H', 'case': CaseSensitivity.MATCH_CASE}]
|
|
318
|
+
cfg.write(to_json_filename=filename)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
TXT_R2S_NUM = syntax_r2s_common + by_number_common
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def generate_syntax_example(filename: str, colref: ColumnRef) -> None:
|
|
325
|
+
"""Generate config example for example."""
|
|
326
|
+
cfg = config_factory_from_enum(colref)
|
|
327
|
+
cfg.out_type = FileType.CSV
|
|
328
|
+
cfg.in_csv_dialect['name'] = 'csv.unix_dialect'
|
|
329
|
+
cfg.write(to_json_filename=filename)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
TXT_SYNTAX_WXAMPLE = '''
|
|
333
|
+
This example configuration file does not match any known use case.
|
|
334
|
+
It is written as an example configuration with 2 objectives:
|
|
335
|
+
to demonstrates all configuration options, and to be small.
|
|
336
|
+
'''
|
|
337
|
+
|
|
338
|
+
dispatch = {'forms_to_rrs': {ColumnRef.BY_NAME: generate_syntax_o2r_name,
|
|
339
|
+
ColumnRef.BY_NUMBER: generate_syntax_o2r_num},
|
|
340
|
+
'forms_to_sw': {ColumnRef.BY_NAME: generate_syntax_o2s_name,
|
|
341
|
+
ColumnRef.BY_NUMBER: generate_syntax_o2s_num},
|
|
342
|
+
'rrs_to_sw': {ColumnRef.BY_NAME: generate_syntax_r2s_name,
|
|
343
|
+
ColumnRef.BY_NUMBER: generate_syntax_r2s_num},
|
|
344
|
+
'example': {ColumnRef.BY_NAME: generate_syntax_example,
|
|
345
|
+
ColumnRef.BY_NUMBER: generate_syntax_example}}
|
|
346
|
+
|
|
347
|
+
txts = {'forms_to_rrs': {ColumnRef.BY_NAME: TXT_O2R_NAME,
|
|
348
|
+
ColumnRef.BY_NUMBER: TXT_O2R_NUM},
|
|
349
|
+
'forms_to_sw': {ColumnRef.BY_NAME: TXT_O2S_NAME,
|
|
350
|
+
ColumnRef.BY_NUMBER: TXT_O2S_NUM},
|
|
351
|
+
'rrs_to_sw': {ColumnRef.BY_NAME: TXT_R2S_NAME,
|
|
352
|
+
ColumnRef.BY_NUMBER: TXT_R2S_NUM},
|
|
353
|
+
'example': {ColumnRef.BY_NAME: TXT_SYNTAX_WXAMPLE,
|
|
354
|
+
ColumnRef.BY_NUMBER: TXT_SYNTAX_WXAMPLE}}
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def get_example_names() -> list[str]:
|
|
358
|
+
"""Return names of example usages with example cfg files."""
|
|
359
|
+
return list(dispatch.keys())
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def generate_examplecfg(filename: str, cfgtype: str,
|
|
363
|
+
colref: ColumnRef) -> int:
|
|
364
|
+
"""Generate example cfg file for one type of usage."""
|
|
365
|
+
fixedname = fix_file_extension(filename=filename, ext_to_add='.cfg')
|
|
366
|
+
syntaxfilename = fix_file_extension(filename=fixedname, ext_to_add='.txt',
|
|
367
|
+
ext_to_remove='.cfg')
|
|
368
|
+
generate_syntax_txt(filename=syntaxfilename,
|
|
369
|
+
example_description=txts[cfgtype][colref],
|
|
370
|
+
cfgfilename=fixedname)
|
|
371
|
+
|
|
372
|
+
dispatch[cfgtype][colref](filename=fixedname, colref=colref)
|
|
373
|
+
print(f'Wrote files {fixedname} and {syntaxfilename}')
|
|
374
|
+
return 0
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
#! /usr/local/bin/python3
|
|
2
|
+
"""Generate example configuration files."""
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2024 Tom Björkholm
|
|
5
|
+
# MIT License
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def generate_syntax_txt(filename: str, example_description: str,
|
|
9
|
+
cfgfilename: str) -> None:
|
|
10
|
+
"""Generate a text file with configuration syntax description."""
|
|
11
|
+
msg = '''
|
|
12
|
+
Description of how to write/change the configuration file.
|
|
13
|
+
==========================================================
|
|
14
|
+
|
|
15
|
+
The configuration file is in JSON syntax.
|
|
16
|
+
https://en.wikipedia.org/wiki/JSON
|
|
17
|
+
The keywords and the nesting is important. The order of keywords
|
|
18
|
+
have no significance (the examples use alphabetical order).
|
|
19
|
+
Indentation and line breaks hanve no significance.
|
|
20
|
+
|
|
21
|
+
It is recommended that you let the command generate a configuration
|
|
22
|
+
file and then edit that file to match your needs. It is NOT recommended
|
|
23
|
+
that the user writes the configuration file from scratch.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
column_ref
|
|
27
|
+
==========
|
|
28
|
+
The keyword column_ref is used to tell if the configuration
|
|
29
|
+
references columns by name (value "BY_NAME") or by number
|
|
30
|
+
(value "BY_NUMBER"). The syntax of parts of the configuration file
|
|
31
|
+
is slightly different depending on the how columns are referenced.
|
|
32
|
+
Thus, you first have to decide how you want to reference columns.
|
|
33
|
+
|
|
34
|
+
Generally it is easier to write a correct configuration using "BY_NAME",
|
|
35
|
+
so "BY_NAME" is recommended when possible.
|
|
36
|
+
"BY_NAME" imposes a few restrictions:
|
|
37
|
+
- each column need to have a unique text at the first line to use
|
|
38
|
+
as column name.
|
|
39
|
+
- the column names need to be stable and known.
|
|
40
|
+
|
|
41
|
+
If you cannot meet these restrictions you will have to reference the
|
|
42
|
+
columns by number "BY_NUMBER". When referencing the columns by number
|
|
43
|
+
it is important to note that the column numbers change when splitting,
|
|
44
|
+
merging, inserting or removing columns.
|
|
45
|
+
|
|
46
|
+
When referencing "BY_NUMBER" the columns are numbered from left to
|
|
47
|
+
right. The leftmost column is number 0.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
Type of input and output file
|
|
51
|
+
==============================
|
|
52
|
+
The type of input file to read is determined by "in_type".
|
|
53
|
+
The type of input file to read is determined by "out_type".
|
|
54
|
+
"in_type" and "out_type" can have values "CSV" or "EXCEL".
|
|
55
|
+
|
|
56
|
+
Excel files can be read and written using three libraries.
|
|
57
|
+
"in_excel_library" and "out_excel_library" can have values
|
|
58
|
+
"OPENPYXL", "XLSXWRITER" or "PYLIGHTXL". These are different
|
|
59
|
+
third party libraries that can read/write excel. My experience
|
|
60
|
+
is that "PYLIGHTXL" most often is able to read and write excel
|
|
61
|
+
files correctly. If you have trouble reading/writing your
|
|
62
|
+
particular excel file, please try different combinations of
|
|
63
|
+
these libraries. "in_excel_library" is always needed in
|
|
64
|
+
the configuration file but is not used if the input is CSV.
|
|
65
|
+
"out_excel_library" is always needed in the configuration file
|
|
66
|
+
but is not used if the output is CSV.
|
|
67
|
+
|
|
68
|
+
Currently "PYLIGHTXL" appears to be best to write files for
|
|
69
|
+
Microsoft Excel to open without complaints. But "OPENPYXL"
|
|
70
|
+
appears to be best to write files to be imported by
|
|
71
|
+
https://www.racingrulesofsailing.org .
|
|
72
|
+
When newer versions arrive this might change...
|
|
73
|
+
|
|
74
|
+
Comma separated values files (CSV files) may differ slightly
|
|
75
|
+
depening on the programs used to read/write them and the locale
|
|
76
|
+
used. "in_csv_dialect" and "out_csv_dialect" changes how CSV files
|
|
77
|
+
are read and written. They are always needed in the configuration
|
|
78
|
+
file, but are not used if the input and output are excel files.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
Column manipulation
|
|
82
|
+
====================
|
|
83
|
+
A number or records starting with "s" and a number describe
|
|
84
|
+
column manipulation to be done. These manipulations are done
|
|
85
|
+
in order of the number: s1_(something) before s2_(something),
|
|
86
|
+
before s3_(something). As the "s" number records might add, remove
|
|
87
|
+
or rename columns, it is important to keep track of the order that
|
|
88
|
+
they are applied.
|
|
89
|
+
|
|
90
|
+
Some "s" numbers are only used if columns are referenced
|
|
91
|
+
by name while other "s" numbers are only used if columns are
|
|
92
|
+
referenced by number.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
"s1_split_columns"
|
|
96
|
+
==================
|
|
97
|
+
The first operation that is done is splitting of columns.
|
|
98
|
+
The key "s1_split_columns" have an array of splits to be
|
|
99
|
+
done. (When the list of splits has more than one split,
|
|
100
|
+
the least confusion is to split columns to the right before
|
|
101
|
+
columns to the left. Named references also helps to avoid
|
|
102
|
+
confusion.)
|
|
103
|
+
|
|
104
|
+
Each split has the following keys: "column", "separator" and
|
|
105
|
+
"where". In the case of number column references the there is
|
|
106
|
+
also the key "store_single". In the case of named column references
|
|
107
|
+
there is instead the key "right_name".
|
|
108
|
+
|
|
109
|
+
"column" take as argument the column to be split. This is a
|
|
110
|
+
column number in the case of "BY_NUMBER", and a column name/title
|
|
111
|
+
in the sace of "BY_NAME".
|
|
112
|
+
|
|
113
|
+
"separator" is a string of characters that indicate the position
|
|
114
|
+
where the column shall be split. (For instance a single space
|
|
115
|
+
if splitting between two words.)
|
|
116
|
+
|
|
117
|
+
"where" can either have the value "RIGHTMOST" or the value
|
|
118
|
+
"LEFTMOST". This is used if the separator string is present
|
|
119
|
+
more than once in the column value to split. The split is then
|
|
120
|
+
done at the leftmost or rightmost occurence according to the
|
|
121
|
+
value of "where".
|
|
122
|
+
|
|
123
|
+
"right_name" is only used if column references are "BY_NAME".
|
|
124
|
+
After the split the left column uses the original column name,
|
|
125
|
+
and "right_name" is used as the name of the new right column.
|
|
126
|
+
|
|
127
|
+
"store_single" is used only if column references are "BY_NUMBER".
|
|
128
|
+
If the column value before split does not include the separator,
|
|
129
|
+
the result of the split is a single value.
|
|
130
|
+
"store_single" can have the value "RIGHTMOST" or the value
|
|
131
|
+
"LEFTMOST". This determines which column the single value shall
|
|
132
|
+
be stored in. (The other column will be empty.)
|
|
133
|
+
In the case of column references "BY_NAME" the single value is
|
|
134
|
+
always stored in the column with the original name.
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
"s2_remove_columns"
|
|
138
|
+
===================
|
|
139
|
+
"s2_remove_columns" is only used with column references "BY_NUMBER".
|
|
140
|
+
The value of "s2_remove_columns" is a list of column numbers to
|
|
141
|
+
remove.
|
|
142
|
+
(For columns references "BY_NAME" see "s8_column_order".)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
"s3_merge_columns"
|
|
146
|
+
==================
|
|
147
|
+
The key "s3_merge_columns" have an array of merges to be done.
|
|
148
|
+
Each merge have the keys "columns" and "separator"
|
|
149
|
+
|
|
150
|
+
"columns" have a list of column references. If "BY_NAME" the
|
|
151
|
+
column references are column names/titles. If "BY_NUMBER" the
|
|
152
|
+
column references are column numbers.
|
|
153
|
+
|
|
154
|
+
"separator" is a string of characters that is inserted between
|
|
155
|
+
the column values being merged.
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
"s4_place_columns_first"
|
|
159
|
+
========================
|
|
160
|
+
"s4_place_columns_first" is only used with column references "BY_NUMBER".
|
|
161
|
+
The key "s4_place_columns_first" has a value that is a list of the
|
|
162
|
+
column numbers to be placed first in order. This step re-orders the
|
|
163
|
+
columns.
|
|
164
|
+
(For columns references "BY_NAME" see "s8_column_order".)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
"s5_rename_columns"
|
|
168
|
+
===================
|
|
169
|
+
The key "s5_rename_columns" has a value that is a list of column rename
|
|
170
|
+
operations. Each column rename operation has the keys "column" and "name"
|
|
171
|
+
|
|
172
|
+
"column" is the number/name of the column before renameing. This is a
|
|
173
|
+
column number in the case of "BY_NUMBER", and a column name/title
|
|
174
|
+
in the sace of "BY_NAME".
|
|
175
|
+
|
|
176
|
+
"name" is the new name/title of the column identified by "column".
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
"s6_insert_columns"
|
|
180
|
+
===================
|
|
181
|
+
The key "s6_insert_columns" has a value that is a list of columns to
|
|
182
|
+
insert. Each column to insert is described by the keys: "column",
|
|
183
|
+
"value" and for "BY_NUMBER" only "name",
|
|
184
|
+
|
|
185
|
+
"column" is the column reference of the inserted column.
|
|
186
|
+
In the case of "BY_NUMBER" this is also the position where
|
|
187
|
+
the column is inserted. In the case of "BY_NAME" this is the
|
|
188
|
+
name of the inserted column.
|
|
189
|
+
|
|
190
|
+
"value" is the value that this column shall have for every row.
|
|
191
|
+
The special word null can be used to state that column shall be
|
|
192
|
+
empty (with no value).
|
|
193
|
+
|
|
194
|
+
"name" is the name/title of the column in the case of "BY_NUMBER".
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
"s7_rewrite_columns"
|
|
198
|
+
====================
|
|
199
|
+
The key "s7_rewrite_columns" has a value that is a list of
|
|
200
|
+
rewrite operations that will be applied in order. Each rewrite
|
|
201
|
+
operation is described by several keys.
|
|
202
|
+
|
|
203
|
+
"kind" key is used to specify which kind of rewrite operation
|
|
204
|
+
this is. Different values for "kind" will have different other
|
|
205
|
+
keys. "kind" can have the values: "STRIP", "REMOVECHARS",
|
|
206
|
+
"STR_SUBSTITUTE" and "REGEX_SUBSTITUTE".
|
|
207
|
+
|
|
208
|
+
"kind" value "STRIP" shall have the keys: "kind", "case", "chars"
|
|
209
|
+
and "column". This case means that the characters in "chars" shall
|
|
210
|
+
be stripped off the beginning and end of the column value. If key
|
|
211
|
+
"chars" is empty string, then white space is stripped off.
|
|
212
|
+
|
|
213
|
+
"kind" value "REMOVECHARS" shall have the keys: "kind", "case",
|
|
214
|
+
"chars" and "column". This case means that the characters in "chars"
|
|
215
|
+
shall be removed from the column (also when the characters are in the
|
|
216
|
+
middle of the column value).
|
|
217
|
+
|
|
218
|
+
"kind" value "REGEX_SUBSTITUTE" shall have the keys: "kind", "case",
|
|
219
|
+
"from", "to" and "column". This case means that "from" value is used
|
|
220
|
+
as a regular expression, and if it matches the matching part is replaced
|
|
221
|
+
with the value of key "to".
|
|
222
|
+
|
|
223
|
+
"kind" value "STR_SUBSTITUTE" shall have the keys: "kind", "case",
|
|
224
|
+
"from", "to" and "column". This case means that "from" value is a string,
|
|
225
|
+
and if a sub-string of the column value equals this string that part is
|
|
226
|
+
replaced with the value of key "to".
|
|
227
|
+
|
|
228
|
+
"column" identifies the column to rewrite. This is a column number in the
|
|
229
|
+
case of "BY_NUMBER", and a column name/title in the sace of "BY_NAME".
|
|
230
|
+
|
|
231
|
+
"chars" is a list of characters written as a string. These characters
|
|
232
|
+
are removed or stripped off the beginning/end depending on the "kind"
|
|
233
|
+
value.
|
|
234
|
+
|
|
235
|
+
"case" specifies if comparison of column value to value of "chars" or
|
|
236
|
+
value of "from" shall be case sensitive or not. Possible values are
|
|
237
|
+
"MATCH_CASE" and "IGNORE_CASE".
|
|
238
|
+
|
|
239
|
+
"from" specifies what the part to substitute shall match. The from part
|
|
240
|
+
in the substitute from something to something.
|
|
241
|
+
|
|
242
|
+
"to" specifies the string that substitution will replace "from" with.
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
"s8_column_order"
|
|
246
|
+
=================
|
|
247
|
+
The key "s8_column_order" is used only in the "BY_NAME" case.
|
|
248
|
+
The value of the "s8_column_order" key is a list of column names.
|
|
249
|
+
The columns will be output in this order.
|
|
250
|
+
Columns not mentioned in "s8_column_order" will not be output,
|
|
251
|
+
and will thus be removed.
|
|
252
|
+
(For "BY_NUMBER" see "s2_remove_columns" and "s4_place_columns_first".)
|
|
253
|
+
'''
|
|
254
|
+
with open(file=filename, mode='w', encoding='utf-8') as file:
|
|
255
|
+
print(f'Explanation for example configuration file {cfgfilename}',
|
|
256
|
+
file=file)
|
|
257
|
+
print(example_description, file=file)
|
|
258
|
+
print('\n\n\n', file=file)
|
|
259
|
+
print(msg, file=file)
|