backlogops 1.1__tar.gz → 1.2__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.
- {backlogops-1.1 → backlogops-1.2}/PKG-INFO +6 -6
- {backlogops-1.1 → backlogops-1.2}/README_pypi.md +2 -2
- {backlogops-1.1 → backlogops-1.2}/backlogops.egg-info/PKG-INFO +6 -6
- {backlogops-1.1 → backlogops-1.2}/backlogops.egg-info/requires.txt +3 -3
- {backlogops-1.1 → backlogops-1.2}/setup.py +4 -4
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/available_teams_config.py +28 -17
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/backlog_ops_config.py +41 -15
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/io_config.py +34 -18
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_io_config.py +12 -6
- {backlogops-1.1 → backlogops-1.2}/LICENSE.txt +0 -0
- {backlogops-1.1 → backlogops-1.2}/backlogops.egg-info/SOURCES.txt +0 -0
- {backlogops-1.1 → backlogops-1.2}/backlogops.egg-info/dependency_links.txt +0 -0
- {backlogops-1.1 → backlogops-1.2}/backlogops.egg-info/top_level.txt +0 -0
- {backlogops-1.1 → backlogops-1.2}/pyproject.toml +0 -0
- {backlogops-1.1 → backlogops-1.2}/setup.cfg +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/__init__.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/apply_format_rules.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/available_teams.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/backlog.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/backlog_helpers.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/backlog_in_release_order.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/backlog_ops_wizard.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/backlog_releases.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/backlog_releases_io.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/blo_version_reporter.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/config_descriptions.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/config_editing.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/config_file_io.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/date_ranges.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/demo_backlog.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/estimate_ready_date.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/format_rules.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/io_preset_wizard.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_connect.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_order_releases.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_rank_backlog.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_rank_by_keys.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_rank_move_keys.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_read.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_rename_releases.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_search.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_token.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_update_backlog.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_update_releases.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_wizard.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_write.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_write_fields.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_write_format.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/jira_write_releases.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/key_list_io.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/levels.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/move_keys_first.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/name_list_io.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/no_text_io.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/order_by_dependencies.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/person.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/py.typed +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/release_backlog_updates.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/release_change_io.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/releases.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/rename_list_io.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/table_create.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/table_rows.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/team.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/wizard_forms.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/wizard_helpers.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/wizard_navigator.py +0 -0
- {backlogops-1.1 → backlogops-1.2}/src/backlogops/work_hours.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: backlogops
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: Library with backlog operations.
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -21,14 +21,14 @@ Classifier: Typing :: Typed
|
|
|
21
21
|
Requires-Python: >=3.12
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE.txt
|
|
24
|
-
Requires-Dist: config-as-json>=1.
|
|
24
|
+
Requires-Dist: config-as-json>=1.7
|
|
25
25
|
Requires-Dist: cryptography>=50.0.1
|
|
26
26
|
Requires-Dist: jira[cli,opt]>=3.10.5
|
|
27
27
|
Requires-Dist: tableio>=1.1
|
|
28
|
-
Requires-Dist: tableio-cfg-json>=1.
|
|
28
|
+
Requires-Dist: tableio-cfg-json>=1.4
|
|
29
29
|
Requires-Dist: wizard-ui-bridge[textual]>=1.3
|
|
30
30
|
Requires-Dist: versionreporter>=0.4
|
|
31
|
-
Requires-Dist: edit-cfg-json>=0.
|
|
31
|
+
Requires-Dist: edit-cfg-json>=0.2.0
|
|
32
32
|
Dynamic: author
|
|
33
33
|
Dynamic: license-file
|
|
34
34
|
Dynamic: requires-dist
|
|
@@ -325,10 +325,10 @@ For the full set of public names see the API documentation linked above.
|
|
|
325
325
|
|
|
326
326
|
## Test summary
|
|
327
327
|
|
|
328
|
-
- Test result:
|
|
328
|
+
- Test result: 2309 passed, 1 deselected in 58s
|
|
329
329
|
- No flake8 warnings.
|
|
330
330
|
- No mypy errors found.
|
|
331
331
|
- No pylint warnings.
|
|
332
332
|
- No python layout warnings.
|
|
333
|
-
- Built version(s): 1.
|
|
333
|
+
- Built version(s): 1.2
|
|
334
334
|
- Build and test using Python 3.14.7
|
|
@@ -289,10 +289,10 @@ For the full set of public names see the API documentation linked above.
|
|
|
289
289
|
|
|
290
290
|
## Test summary
|
|
291
291
|
|
|
292
|
-
- Test result:
|
|
292
|
+
- Test result: 2309 passed, 1 deselected in 58s
|
|
293
293
|
- No flake8 warnings.
|
|
294
294
|
- No mypy errors found.
|
|
295
295
|
- No pylint warnings.
|
|
296
296
|
- No python layout warnings.
|
|
297
|
-
- Built version(s): 1.
|
|
297
|
+
- Built version(s): 1.2
|
|
298
298
|
- Build and test using Python 3.14.7
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: backlogops
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2
|
|
4
4
|
Summary: Library with backlog operations.
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -21,14 +21,14 @@ Classifier: Typing :: Typed
|
|
|
21
21
|
Requires-Python: >=3.12
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE.txt
|
|
24
|
-
Requires-Dist: config-as-json>=1.
|
|
24
|
+
Requires-Dist: config-as-json>=1.7
|
|
25
25
|
Requires-Dist: cryptography>=50.0.1
|
|
26
26
|
Requires-Dist: jira[cli,opt]>=3.10.5
|
|
27
27
|
Requires-Dist: tableio>=1.1
|
|
28
|
-
Requires-Dist: tableio-cfg-json>=1.
|
|
28
|
+
Requires-Dist: tableio-cfg-json>=1.4
|
|
29
29
|
Requires-Dist: wizard-ui-bridge[textual]>=1.3
|
|
30
30
|
Requires-Dist: versionreporter>=0.4
|
|
31
|
-
Requires-Dist: edit-cfg-json>=0.
|
|
31
|
+
Requires-Dist: edit-cfg-json>=0.2.0
|
|
32
32
|
Dynamic: author
|
|
33
33
|
Dynamic: license-file
|
|
34
34
|
Dynamic: requires-dist
|
|
@@ -325,10 +325,10 @@ For the full set of public names see the API documentation linked above.
|
|
|
325
325
|
|
|
326
326
|
## Test summary
|
|
327
327
|
|
|
328
|
-
- Test result:
|
|
328
|
+
- Test result: 2309 passed, 1 deselected in 58s
|
|
329
329
|
- No flake8 warnings.
|
|
330
330
|
- No mypy errors found.
|
|
331
331
|
- No pylint warnings.
|
|
332
332
|
- No python layout warnings.
|
|
333
|
-
- Built version(s): 1.
|
|
333
|
+
- Built version(s): 1.2
|
|
334
334
|
- Build and test using Python 3.14.7
|
|
@@ -5,7 +5,7 @@ from setuptools import setup
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name='backlogops',
|
|
8
|
-
version='1.
|
|
8
|
+
version='1.2',
|
|
9
9
|
description='Library with backlog operations.',
|
|
10
10
|
author='Tom Björkholm',
|
|
11
11
|
author_email='klausuler_linnet0q@icloud.com',
|
|
@@ -14,13 +14,13 @@ setup(
|
|
|
14
14
|
package_dir={'backlogops': 'src/backlogops'},
|
|
15
15
|
package_data={'backlogops': ['src/py.typed']},
|
|
16
16
|
install_requires=[
|
|
17
|
-
'config-as-json >= 1.
|
|
17
|
+
'config-as-json >= 1.7',
|
|
18
18
|
'cryptography >= 50.0.1',
|
|
19
19
|
'jira[cli,opt] >= 3.10.5',
|
|
20
20
|
'tableio >= 1.1',
|
|
21
|
-
'tableio-cfg-json >= 1.
|
|
21
|
+
'tableio-cfg-json >= 1.4',
|
|
22
22
|
'wizard-ui-bridge[textual] >= 1.3',
|
|
23
23
|
'versionreporter >= 0.4',
|
|
24
|
-
'edit-cfg-json >= 0.
|
|
24
|
+
'edit-cfg-json >= 0.2.0',
|
|
25
25
|
]
|
|
26
26
|
)
|
|
@@ -117,16 +117,20 @@ class _BridgeConfig(Config):
|
|
|
117
117
|
return {}
|
|
118
118
|
|
|
119
119
|
def __init__(self, from_json_data_text: Optional[str],
|
|
120
|
-
from_json_filename: Optional[PathOrStr],
|
|
121
|
-
|
|
120
|
+
from_json_filename: Optional[PathOrStr], stderr_file: TextIO,
|
|
121
|
+
*, member_name: Optional[str] = None) -> None:
|
|
122
122
|
"""Run the Config lifecycle for a bridge instance.
|
|
123
123
|
|
|
124
124
|
Each bridge first creates its data class attributes, then calls
|
|
125
125
|
this constructor to read JSON, apply defaults, and validate.
|
|
126
|
+
``member_name`` is the path for reaching this object from the top
|
|
127
|
+
level configuration, so that a diagnostic about a value inside it
|
|
128
|
+
names the whole path; it is None for a bridge that is a whole
|
|
129
|
+
configuration file of its own.
|
|
126
130
|
"""
|
|
127
131
|
Config.__init__(self, from_json_data_text=from_json_data_text,
|
|
128
132
|
from_json_filename=from_json_filename,
|
|
129
|
-
stderr_file=stderr_file)
|
|
133
|
+
stderr_file=stderr_file, member_name=member_name)
|
|
130
134
|
|
|
131
135
|
@staticmethod
|
|
132
136
|
def _consistency() -> WholeConfigValidationStep:
|
|
@@ -154,12 +158,13 @@ class FteExceptionConfig(FteException, _BridgeConfig):
|
|
|
154
158
|
|
|
155
159
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
156
160
|
from_json_filename: Optional[PathOrStr] = None,
|
|
157
|
-
stderr_file: TextIO = sys.stderr
|
|
161
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
162
|
+
member_name: Optional[str] = None) -> None:
|
|
158
163
|
"""Create placeholder defaults, then read one FTE exception."""
|
|
159
164
|
FteException.__init__(self, start_date=date(2000, 1, 1),
|
|
160
165
|
end_date=date(2000, 1, 1), fte=1.0)
|
|
161
166
|
_BridgeConfig.__init__(self, from_json_data_text, from_json_filename,
|
|
162
|
-
stderr_file)
|
|
167
|
+
stderr_file, member_name=member_name)
|
|
163
168
|
|
|
164
169
|
@override
|
|
165
170
|
def get_validation_plan(self, stderr_file: TextIO) -> ValidationPlan:
|
|
@@ -179,13 +184,14 @@ class ExceptionWorkHoursConfig(ExceptionWorkHours, _BridgeConfig):
|
|
|
179
184
|
|
|
180
185
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
181
186
|
from_json_filename: Optional[PathOrStr] = None,
|
|
182
|
-
stderr_file: TextIO = sys.stderr
|
|
187
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
188
|
+
member_name: Optional[str] = None) -> None:
|
|
183
189
|
"""Create placeholder defaults, then read one work-hours exception."""
|
|
184
190
|
ExceptionWorkHours.__init__(self, start_date=date(2000, 1, 1),
|
|
185
191
|
end_date=date(2000, 1, 1),
|
|
186
192
|
hours_per_day=0.0)
|
|
187
193
|
_BridgeConfig.__init__(self, from_json_data_text, from_json_filename,
|
|
188
|
-
stderr_file)
|
|
194
|
+
stderr_file, member_name=member_name)
|
|
189
195
|
|
|
190
196
|
@override
|
|
191
197
|
def get_validation_plan(self, stderr_file: TextIO) -> ValidationPlan:
|
|
@@ -205,11 +211,12 @@ class MembershipConfig(Membership, _BridgeConfig):
|
|
|
205
211
|
|
|
206
212
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
207
213
|
from_json_filename: Optional[PathOrStr] = None,
|
|
208
|
-
stderr_file: TextIO = sys.stderr
|
|
214
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
215
|
+
member_name: Optional[str] = None) -> None:
|
|
209
216
|
"""Create placeholder defaults, then read one membership."""
|
|
210
217
|
Membership.__init__(self, person_name='person')
|
|
211
218
|
_BridgeConfig.__init__(self, from_json_data_text, from_json_filename,
|
|
212
|
-
stderr_file)
|
|
219
|
+
stderr_file, member_name=member_name)
|
|
213
220
|
|
|
214
221
|
@override
|
|
215
222
|
def nested_configs(self) -> NestedConfigs:
|
|
@@ -241,12 +248,13 @@ class TeamConfig(Team, _BridgeConfig):
|
|
|
241
248
|
|
|
242
249
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
243
250
|
from_json_filename: Optional[PathOrStr] = None,
|
|
244
|
-
stderr_file: TextIO = sys.stderr
|
|
251
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
252
|
+
member_name: Optional[str] = None) -> None:
|
|
245
253
|
"""Create placeholder defaults, then read one team."""
|
|
246
254
|
Team.__init__(self, name='team', velocity=0.0, sum_fte_at_velocity=1.0,
|
|
247
255
|
sprint_length=1)
|
|
248
256
|
_BridgeConfig.__init__(self, from_json_data_text, from_json_filename,
|
|
249
|
-
stderr_file)
|
|
257
|
+
stderr_file, member_name=member_name)
|
|
250
258
|
|
|
251
259
|
@override
|
|
252
260
|
def nested_configs(self) -> NestedConfigs:
|
|
@@ -266,11 +274,12 @@ class PersonConfig(Person, _BridgeConfig):
|
|
|
266
274
|
|
|
267
275
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
268
276
|
from_json_filename: Optional[PathOrStr] = None,
|
|
269
|
-
stderr_file: TextIO = sys.stderr
|
|
277
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
278
|
+
member_name: Optional[str] = None) -> None:
|
|
270
279
|
"""Create placeholder defaults, then read one person."""
|
|
271
280
|
Person.__init__(self, name='person')
|
|
272
281
|
_BridgeConfig.__init__(self, from_json_data_text, from_json_filename,
|
|
273
|
-
stderr_file)
|
|
282
|
+
stderr_file, member_name=member_name)
|
|
274
283
|
|
|
275
284
|
@override
|
|
276
285
|
def nested_configs(self) -> NestedConfigs:
|
|
@@ -291,12 +300,13 @@ class CompanyWorkHoursConfig(CompanyWorkHours, _BridgeConfig):
|
|
|
291
300
|
|
|
292
301
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
293
302
|
from_json_filename: Optional[PathOrStr] = None,
|
|
294
|
-
stderr_file: TextIO = sys.stderr
|
|
303
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
304
|
+
member_name: Optional[str] = None) -> None:
|
|
295
305
|
"""Create defaults, then read the company work hours."""
|
|
296
306
|
CompanyWorkHours.__init__(self)
|
|
297
307
|
self._unchecked_dicts = ['work_hours']
|
|
298
308
|
_BridgeConfig.__init__(self, from_json_data_text, from_json_filename,
|
|
299
|
-
stderr_file)
|
|
309
|
+
stderr_file, member_name=member_name)
|
|
300
310
|
|
|
301
311
|
@override
|
|
302
312
|
def nested_configs(self) -> NestedConfigs:
|
|
@@ -328,7 +338,8 @@ class AvailableTeamsConfig(AvailableTeams, _BridgeConfig):
|
|
|
328
338
|
def __init__(self, *, neutral: Optional[AvailableTeams] = None,
|
|
329
339
|
from_json_data_text: Optional[str] = None,
|
|
330
340
|
from_json_filename: Optional[PathOrStr] = None,
|
|
331
|
-
stderr_file: TextIO = sys.stderr
|
|
341
|
+
stderr_file: TextIO = sys.stderr,
|
|
342
|
+
member_name: Optional[str] = None) -> None:
|
|
332
343
|
"""Create the bridge from a neutral workforce or from JSON.
|
|
333
344
|
|
|
334
345
|
``AvailableTeams.__init__`` is intentionally not invoked because
|
|
@@ -340,7 +351,7 @@ class AvailableTeamsConfig(AvailableTeams, _BridgeConfig):
|
|
|
340
351
|
neutral = AvailableTeams(persons={}, teams=[])
|
|
341
352
|
Config.copy_initial_data(neutral, self)
|
|
342
353
|
_BridgeConfig.__init__(self, from_json_data_text, from_json_filename,
|
|
343
|
-
stderr_file)
|
|
354
|
+
stderr_file, member_name=member_name)
|
|
344
355
|
|
|
345
356
|
@override
|
|
346
357
|
def nested_configs(self) -> NestedConfigs:
|
|
@@ -39,7 +39,8 @@ from config_as_json import CallingWholeConfigValidator, Config, \
|
|
|
39
39
|
ConfigAutoChangeHook, ConfigNesting, ConfigNestingKind, ConfigPath, \
|
|
40
40
|
JsonType, MemberValidationStep, MemberValidator, NestedConfigs, \
|
|
41
41
|
PathOrStr, ReadOldConfiguration, RocfKeyMove, SerializeConverter, \
|
|
42
|
-
SerializeConverters, ValidationPlan, WholeConfigValidationStep
|
|
42
|
+
SerializeConverters, ValidationPlan, WholeConfigValidationStep, \
|
|
43
|
+
member_path
|
|
43
44
|
from backlogops.available_teams import AvailableTeams
|
|
44
45
|
from backlogops.available_teams_config import AvailableTeamsConfig
|
|
45
46
|
from backlogops.backlog import Status
|
|
@@ -75,19 +76,32 @@ def _as_str_list(name: str, value: object, stderr_file: TextIO) -> list[str]:
|
|
|
75
76
|
return result
|
|
76
77
|
|
|
77
78
|
|
|
78
|
-
def _level_from_dict(data: dict[str, object],
|
|
79
|
-
|
|
79
|
+
def _level_from_dict(name: str, data: dict[str, object],
|
|
80
|
+
stderr_file: TextIO) -> Level:
|
|
81
|
+
"""Return one Level from its JSON object, checking the field shape.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
name: Reported path of the level itself, such as ``levels[2]``,
|
|
85
|
+
which each field name is appended to.
|
|
86
|
+
data: The JSON object holding the fields of one level.
|
|
87
|
+
stderr_file: The file to report errors to.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
The level the JSON object describes.
|
|
91
|
+
"""
|
|
80
92
|
unknown = sorted(set(data) - {'level', 'name', 'aliases'})
|
|
81
93
|
if unknown:
|
|
82
|
-
report_bad_value(
|
|
94
|
+
report_bad_value(name, data, f'unknown level fields {unknown}',
|
|
83
95
|
stderr_file, 'Level')
|
|
84
96
|
if 'level' not in data or 'name' not in data:
|
|
85
|
-
report_bad_value(
|
|
97
|
+
report_bad_value(name, data, 'requires "level" and "name"',
|
|
86
98
|
stderr_file, 'Level')
|
|
87
|
-
return Level(level=_as_int('level', data['level'],
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
return Level(level=_as_int(member_path(name, 'level'), data['level'],
|
|
100
|
+
stderr_file),
|
|
101
|
+
name=_as_str(member_path(name, 'name'), data['name'],
|
|
102
|
+
stderr_file),
|
|
103
|
+
aliases=_as_str_list(member_path(name, 'aliases'),
|
|
104
|
+
data.get('aliases', []), stderr_file))
|
|
91
105
|
|
|
92
106
|
|
|
93
107
|
def _level_from_obj(name: str, value: object, stderr_file: TextIO) -> Level:
|
|
@@ -96,7 +110,7 @@ def _level_from_obj(name: str, value: object, stderr_file: TextIO) -> Level:
|
|
|
96
110
|
return value
|
|
97
111
|
if not isinstance(value, dict):
|
|
98
112
|
report_wrong_type(name, value, dict, stderr_file, 'Level')
|
|
99
|
-
return _level_from_dict(value, stderr_file)
|
|
113
|
+
return _level_from_dict(name, value, stderr_file)
|
|
100
114
|
|
|
101
115
|
|
|
102
116
|
_NO_LEVELS = ('no levels at all; a configuration that leaves the levels out '
|
|
@@ -192,11 +206,15 @@ DEF_STATUS_INPUT_MAP: dict[str, Status] = {
|
|
|
192
206
|
class BacklogOpsConfig(Config): # pylint: disable=too-many-instance-attributes
|
|
193
207
|
"""Top-level backlog-ops configuration stored as config-as-json."""
|
|
194
208
|
|
|
209
|
+
# The library contract fixes five of these; the workforce is the one
|
|
210
|
+
# argument of this application, and every one of them is keyword only.
|
|
211
|
+
# pylint: disable-next=too-many-arguments
|
|
195
212
|
def __init__(self, *, available_teams: Optional[AvailableTeams] = None,
|
|
196
213
|
from_json_data_text: Optional[str] = None,
|
|
197
214
|
from_json_filename: Optional[PathOrStr] = None,
|
|
198
215
|
auto_ch_hook: Optional[ConfigAutoChangeHook] = None,
|
|
199
|
-
stderr_file: TextIO = sys.stderr
|
|
216
|
+
stderr_file: TextIO = sys.stderr,
|
|
217
|
+
member_name: Optional[str] = None) -> None:
|
|
200
218
|
"""Create defaults, or read the configuration from JSON.
|
|
201
219
|
|
|
202
220
|
The supplied workforce establishes the schema of the nested
|
|
@@ -204,7 +222,11 @@ class BacklogOpsConfig(Config): # pylint: disable=too-many-instance-attributes
|
|
|
204
222
|
into an :class:`AvailableTeamsConfig`. The presets default to
|
|
205
223
|
empty maps and the levels default to ``None`` (use the defaults).
|
|
206
224
|
The ``auto_ch_hook`` is notified when an old file needed
|
|
207
|
-
backward-compatible normalization while reading.
|
|
225
|
+
backward-compatible normalization while reading. The default
|
|
226
|
+
nested sections are created under the path of the member holding
|
|
227
|
+
each of them, so that a diagnostic names the whole path even
|
|
228
|
+
before any file is read; ``member_name`` is None for a whole
|
|
229
|
+
configuration file, which is a member of nothing.
|
|
208
230
|
"""
|
|
209
231
|
self.available_teams: AvailableTeams = (
|
|
210
232
|
AvailableTeams(persons={}, teams=[]) if available_teams is None
|
|
@@ -212,14 +234,18 @@ class BacklogOpsConfig(Config): # pylint: disable=too-many-instance-attributes
|
|
|
212
234
|
self.input_configs: dict[str, InputFormatConfig] = {}
|
|
213
235
|
self.output_configs: dict[str, OutputFormatConfig] = {}
|
|
214
236
|
self.gui_display: GuiDisplayConfig = GuiDisplayConfig(
|
|
215
|
-
stderr_file=stderr_file
|
|
237
|
+
stderr_file=stderr_file,
|
|
238
|
+
member_name=member_path(member_name, 'gui_display'))
|
|
216
239
|
self.status_input_map: dict[str, Status] = DEF_STATUS_INPUT_MAP
|
|
217
|
-
self.jira: JiraIOConfig = JiraIOConfig(
|
|
240
|
+
self.jira: JiraIOConfig = JiraIOConfig(
|
|
241
|
+
stderr_file=stderr_file,
|
|
242
|
+
member_name=member_path(member_name, 'jira'))
|
|
218
243
|
self.levels: Optional[list[Level]] = None
|
|
219
244
|
self._unchecked_dicts = ['status_input_map']
|
|
220
245
|
Config.__init__(self, from_json_data_text=from_json_data_text,
|
|
221
246
|
from_json_filename=from_json_filename,
|
|
222
|
-
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file
|
|
247
|
+
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file,
|
|
248
|
+
member_name=member_name)
|
|
223
249
|
|
|
224
250
|
@override
|
|
225
251
|
def _get_read_old_config(self) -> ReadOldConfiguration:
|
|
@@ -48,7 +48,7 @@ from config_as_json import Config, ConfigAutoChangeHook, ConfigNesting, \
|
|
|
48
48
|
ConfigNestingKind, ConfigPath, InvalidConfiguration, \
|
|
49
49
|
MemberValidationStep, MemberValidator, NestedConfigs, ParseConverter, \
|
|
50
50
|
PathOrStr, ReadOldConfiguration, RocfKeyMove, RocfValueMigration, \
|
|
51
|
-
RocfValueWrite, ValidationPlan
|
|
51
|
+
RocfValueWrite, ValidationPlan, member_path
|
|
52
52
|
from tableio import Capabilities, FileAccess, access_capabilities
|
|
53
53
|
from tableio_cfg_json import TioJsonConfig, tio_json_config_default
|
|
54
54
|
from backlogops.backlog import Status
|
|
@@ -195,23 +195,25 @@ def _capabilities(file_access: FileAccess, stderr_file: TextIO
|
|
|
195
195
|
|
|
196
196
|
|
|
197
197
|
def _tio_default(file_access: FileAccess, format_name: Optional[str] = None,
|
|
198
|
-
stderr_file: TextIO = sys.stderr
|
|
198
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
199
|
+
member_name: Optional[str] = None) -> TioJsonConfig:
|
|
199
200
|
"""Return a default TableIO config for a format and file access."""
|
|
200
201
|
return tio_json_config_default(
|
|
201
202
|
capabilities=_capabilities(file_access, stderr_file),
|
|
202
203
|
file_access=file_access, format_name=format_name,
|
|
203
|
-
stderr_file=stderr_file)
|
|
204
|
+
stderr_file=stderr_file, member_name=member_name)
|
|
204
205
|
|
|
205
206
|
|
|
206
207
|
def _tio_from_json(file_access: FileAccess, from_json_data_text: Optional[str],
|
|
207
208
|
from_json_filename: Optional[PathOrStr],
|
|
208
|
-
stderr_file: TextIO
|
|
209
|
+
stderr_file: TextIO,
|
|
210
|
+
member_name: Optional[str]) -> TioJsonConfig:
|
|
209
211
|
"""Return a TableIO config read from JSON for a file access mode."""
|
|
210
212
|
return TioJsonConfig(capabilities=_capabilities(file_access, stderr_file),
|
|
211
213
|
file_access=file_access,
|
|
212
214
|
from_json_data_text=from_json_data_text,
|
|
213
215
|
from_json_filename=from_json_filename,
|
|
214
|
-
stderr_file=stderr_file)
|
|
216
|
+
stderr_file=stderr_file, member_name=member_name)
|
|
215
217
|
|
|
216
218
|
|
|
217
219
|
class _FormatConfig(Config):
|
|
@@ -230,22 +232,31 @@ class _FormatConfig(Config):
|
|
|
230
232
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
231
233
|
from_json_filename: Optional[PathOrStr] = None,
|
|
232
234
|
auto_ch_hook: Optional[ConfigAutoChangeHook] = None,
|
|
233
|
-
stderr_file: TextIO = sys.stderr
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
235
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
236
|
+
member_name: Optional[str] = None) -> None:
|
|
237
|
+
"""Create default settings or read them from a JSON source.
|
|
238
|
+
|
|
239
|
+
The default TableIO endpoint is created under the path of the
|
|
240
|
+
``tableio`` member of this endpoint, so that a TableIO diagnostic
|
|
241
|
+
names which preset it is about even before any file is read.
|
|
242
|
+
"""
|
|
243
|
+
self.tableio: TioJsonConfig = _tio_default(
|
|
244
|
+
self._FILE_ACCESS, stderr_file=stderr_file,
|
|
245
|
+
member_name=member_path(member_name, 'tableio'))
|
|
237
246
|
self._unchecked_dicts = list(self._MAP_NAMES) + \
|
|
238
247
|
list(self._UNCHECKED_EXTRA)
|
|
239
248
|
Config.__init__(self, from_json_data_text=from_json_data_text,
|
|
240
249
|
from_json_filename=from_json_filename,
|
|
241
|
-
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file
|
|
250
|
+
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file,
|
|
251
|
+
member_name=member_name)
|
|
242
252
|
|
|
243
253
|
def _tio_factory(self, *, from_json_data_text: Optional[str] = None,
|
|
244
254
|
from_json_filename: Optional[PathOrStr] = None,
|
|
245
|
-
stderr_file: TextIO = sys.stderr
|
|
255
|
+
stderr_file: TextIO = sys.stderr,
|
|
256
|
+
member_name: Optional[str] = None) -> TioJsonConfig:
|
|
246
257
|
"""Construct the nested TableIO config from JSON when reading."""
|
|
247
258
|
return _tio_from_json(self._FILE_ACCESS, from_json_data_text,
|
|
248
|
-
from_json_filename, stderr_file)
|
|
259
|
+
from_json_filename, stderr_file, member_name)
|
|
249
260
|
|
|
250
261
|
def _map_validator(self) -> MemberValidator:
|
|
251
262
|
"""Return the validator applied to each column-name map member."""
|
|
@@ -334,7 +345,8 @@ class InputFormatConfig(_FormatConfig):
|
|
|
334
345
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
335
346
|
from_json_filename: Optional[PathOrStr] = None,
|
|
336
347
|
auto_ch_hook: Optional[ConfigAutoChangeHook] = None,
|
|
337
|
-
stderr_file: TextIO = sys.stderr
|
|
348
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
349
|
+
member_name: Optional[str] = None) -> None:
|
|
338
350
|
"""Create the input map defaults, then run the shared constructor."""
|
|
339
351
|
self.backlog_to_internal = {}
|
|
340
352
|
self.release_to_internal = {}
|
|
@@ -342,7 +354,8 @@ class InputFormatConfig(_FormatConfig):
|
|
|
342
354
|
_FormatConfig.__init__(self, from_json_data_text=from_json_data_text,
|
|
343
355
|
from_json_filename=from_json_filename,
|
|
344
356
|
auto_ch_hook=auto_ch_hook,
|
|
345
|
-
stderr_file=stderr_file
|
|
357
|
+
stderr_file=stderr_file,
|
|
358
|
+
member_name=member_name)
|
|
346
359
|
|
|
347
360
|
@override
|
|
348
361
|
def _get_read_old_config(self) -> ReadOldConfiguration:
|
|
@@ -381,7 +394,8 @@ class OutputFormatConfig(_FormatConfig):
|
|
|
381
394
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
382
395
|
from_json_filename: Optional[PathOrStr] = None,
|
|
383
396
|
auto_ch_hook: Optional[ConfigAutoChangeHook] = None,
|
|
384
|
-
stderr_file: TextIO = sys.stderr
|
|
397
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
398
|
+
member_name: Optional[str] = None) -> None:
|
|
385
399
|
"""Create the output defaults, then run the shared constructor."""
|
|
386
400
|
self.backlog_to_external = {}
|
|
387
401
|
self.release_to_external = {}
|
|
@@ -389,7 +403,8 @@ class OutputFormatConfig(_FormatConfig):
|
|
|
389
403
|
_FormatConfig.__init__(self, from_json_data_text=from_json_data_text,
|
|
390
404
|
from_json_filename=from_json_filename,
|
|
391
405
|
auto_ch_hook=auto_ch_hook,
|
|
392
|
-
stderr_file=stderr_file
|
|
406
|
+
stderr_file=stderr_file,
|
|
407
|
+
member_name=member_name)
|
|
393
408
|
|
|
394
409
|
@override
|
|
395
410
|
def parse_converters(self) -> dict[str, ParseConverter]:
|
|
@@ -450,7 +465,8 @@ class GuiDisplayConfig(Config):
|
|
|
450
465
|
|
|
451
466
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
452
467
|
from_json_filename: Optional[PathOrStr] = None,
|
|
453
|
-
stderr_file: TextIO = sys.stderr
|
|
468
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
469
|
+
member_name: Optional[str] = None) -> None:
|
|
454
470
|
"""Create the display defaults, then read them from JSON."""
|
|
455
471
|
self.backlog_to_external = {}
|
|
456
472
|
self.release_to_external = {}
|
|
@@ -458,7 +474,7 @@ class GuiDisplayConfig(Config):
|
|
|
458
474
|
self._unchecked_dicts = ['backlog_to_external', 'release_to_external']
|
|
459
475
|
Config.__init__(self, from_json_data_text=from_json_data_text,
|
|
460
476
|
from_json_filename=from_json_filename,
|
|
461
|
-
stderr_file=stderr_file)
|
|
477
|
+
stderr_file=stderr_file, member_name=member_name)
|
|
462
478
|
|
|
463
479
|
@override
|
|
464
480
|
def parse_converters(self) -> dict[str, ParseConverter]:
|
|
@@ -420,7 +420,8 @@ class JiraConnectConfig(Config):
|
|
|
420
420
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
421
421
|
from_json_filename: Optional[PathOrStr] = None,
|
|
422
422
|
auto_ch_hook: Optional[ConfigAutoChangeHook] = None,
|
|
423
|
-
stderr_file: TextIO = sys.stderr
|
|
423
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
424
|
+
member_name: Optional[str] = None) -> None:
|
|
424
425
|
"""Create connection defaults, then read them from JSON."""
|
|
425
426
|
self.jira_type = JiraType.CLOUD
|
|
426
427
|
self.base_url = ''
|
|
@@ -431,7 +432,8 @@ class JiraConnectConfig(Config):
|
|
|
431
432
|
self._cached_token: Optional[str] = None
|
|
432
433
|
Config.__init__(self, from_json_data_text=from_json_data_text,
|
|
433
434
|
from_json_filename=from_json_filename,
|
|
434
|
-
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file
|
|
435
|
+
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file,
|
|
436
|
+
member_name=member_name)
|
|
435
437
|
|
|
436
438
|
@override
|
|
437
439
|
def _omit_none_from_json(self) -> list[str]:
|
|
@@ -600,7 +602,8 @@ class JiraPreset(Config):
|
|
|
600
602
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
601
603
|
from_json_filename: Optional[PathOrStr] = None,
|
|
602
604
|
auto_ch_hook: Optional[ConfigAutoChangeHook] = None,
|
|
603
|
-
stderr_file: TextIO = sys.stderr
|
|
605
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
606
|
+
member_name: Optional[str] = None) -> None:
|
|
604
607
|
"""Create preset defaults, then read them from JSON."""
|
|
605
608
|
self.connection_name = ''
|
|
606
609
|
self.backlog_column_map_name = ''
|
|
@@ -611,7 +614,8 @@ class JiraPreset(Config):
|
|
|
611
614
|
self.def_filter = ''
|
|
612
615
|
Config.__init__(self, from_json_data_text=from_json_data_text,
|
|
613
616
|
from_json_filename=from_json_filename,
|
|
614
|
-
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file
|
|
617
|
+
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file,
|
|
618
|
+
member_name=member_name)
|
|
615
619
|
|
|
616
620
|
@override
|
|
617
621
|
def _get_read_old_config(self) -> ReadOldConfiguration:
|
|
@@ -695,7 +699,8 @@ class JiraIOConfig(Config):
|
|
|
695
699
|
def __init__(self, from_json_data_text: Optional[str] = None,
|
|
696
700
|
from_json_filename: Optional[PathOrStr] = None,
|
|
697
701
|
auto_ch_hook: Optional[ConfigAutoChangeHook] = None,
|
|
698
|
-
stderr_file: TextIO = sys.stderr
|
|
702
|
+
stderr_file: TextIO = sys.stderr, *,
|
|
703
|
+
member_name: Optional[str] = None) -> None:
|
|
699
704
|
"""Create empty defaults, then read the jira configuration."""
|
|
700
705
|
self.connections: dict[str, JiraConnectConfig] = {}
|
|
701
706
|
self.backlog_column_maps: dict[str, JiraColumnMap] = {}
|
|
@@ -706,7 +711,8 @@ class JiraIOConfig(Config):
|
|
|
706
711
|
'issue_type_maps']
|
|
707
712
|
Config.__init__(self, from_json_data_text=from_json_data_text,
|
|
708
713
|
from_json_filename=from_json_filename,
|
|
709
|
-
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file
|
|
714
|
+
auto_ch_hook=auto_ch_hook, stderr_file=stderr_file,
|
|
715
|
+
member_name=member_name)
|
|
710
716
|
|
|
711
717
|
@override
|
|
712
718
|
def _get_read_old_config(self) -> ReadOldConfiguration:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|