backlogops 0.9__tar.gz → 1.0__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-0.9 → backlogops-1.0}/PKG-INFO +7 -10
- {backlogops-0.9 → backlogops-1.0}/README_pypi.md +4 -7
- {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/PKG-INFO +7 -10
- {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/requires.txt +2 -2
- {backlogops-0.9 → backlogops-1.0}/setup.py +4 -4
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/__init__.py +3 -4
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/config_descriptions.py +23 -13
- backlogops-1.0/src/backlogops/config_editing.py +90 -0
- backlogops-0.9/src/backlogops/config_editing.py +0 -149
- {backlogops-0.9 → backlogops-1.0}/LICENSE.txt +0 -0
- {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/SOURCES.txt +0 -0
- {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/dependency_links.txt +0 -0
- {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/top_level.txt +0 -0
- {backlogops-0.9 → backlogops-1.0}/pyproject.toml +0 -0
- {backlogops-0.9 → backlogops-1.0}/setup.cfg +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/apply_format_rules.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/available_teams.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/available_teams_config.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_helpers.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_in_release_order.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_ops_config.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_ops_wizard.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_releases.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_releases_io.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/blo_version_reporter.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/config_file_io.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/date_ranges.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/demo_backlog.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/estimate_ready_date.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/format_rules.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/io_config.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/io_preset_wizard.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_connect.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_io_config.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_order_releases.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_rank_backlog.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_rank_by_keys.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_rank_move_keys.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_read.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_rename_releases.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_search.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_token.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_update_backlog.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_update_releases.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_wizard.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_write.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_write_fields.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_write_format.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_write_releases.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/key_list_io.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/levels.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/move_keys_first.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/name_list_io.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/no_text_io.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/order_by_dependencies.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/person.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/py.typed +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/release_backlog_updates.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/release_change_io.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/releases.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/rename_list_io.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/table_create.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/table_rows.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/team.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/wizard_forms.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/wizard_helpers.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/wizard_navigator.py +0 -0
- {backlogops-0.9 → backlogops-1.0}/src/backlogops/work_hours.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: backlogops
|
|
3
|
-
Version: 0
|
|
3
|
+
Version: 1.0
|
|
4
4
|
Summary: Library with backlog operations.
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -25,10 +25,10 @@ Requires-Dist: config-as-json>=1.5
|
|
|
25
25
|
Requires-Dist: cryptography>=50.0.0
|
|
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.3
|
|
29
29
|
Requires-Dist: wizard-ui-bridge[textual]>=1.3
|
|
30
|
-
Requires-Dist: edit-cfg-json==0.0.2
|
|
31
30
|
Requires-Dist: versionreporter>=0.4
|
|
31
|
+
Requires-Dist: edit-cfg-json==0.0.4
|
|
32
32
|
Dynamic: author
|
|
33
33
|
Dynamic: license-file
|
|
34
34
|
Dynamic: requires-dist
|
|
@@ -271,15 +271,12 @@ from backlogops import (
|
|
|
271
271
|
|
|
272
272
|
### Editing a configuration
|
|
273
273
|
|
|
274
|
-
- `edit_model_for`, `default_edit_config`: build the `edit-cfg-json` model
|
|
275
|
-
of an editing session over a `BacklogOpsConfig` or a stand-alone input
|
|
276
|
-
or output preset, for the Tkinter editor of the graphical interface and
|
|
277
|
-
the Textual editor of the command line.
|
|
278
|
-
|
|
279
274
|
- `CONFIG_DESCRIPTIONS`, `INPUT_DESCRIPTIONS`, `OUTPUT_DESCRIPTIONS`:
|
|
280
275
|
what each configuration member is for, shown below it in the editor.
|
|
281
276
|
A member has no docstring at runtime, so this is what the editor is
|
|
282
277
|
told; `descriptions_for` picks the mapping of one configuration class.
|
|
278
|
+
The nested TableIO endpoint is described by `tableio-cfg-json`, which
|
|
279
|
+
is asked for that text rather than having it repeated here.
|
|
283
280
|
|
|
284
281
|
- `EDIT_SETTINGS`: what the editor may do to a configuration file of this
|
|
285
282
|
application, including keeping what it writes over as a `.bak` file.
|
|
@@ -328,10 +325,10 @@ For the full set of public names see the API documentation linked above.
|
|
|
328
325
|
|
|
329
326
|
## Test summary
|
|
330
327
|
|
|
331
|
-
- Test result:
|
|
328
|
+
- Test result: 2234 passed, 1 deselected in 45s
|
|
332
329
|
- No flake8 warnings.
|
|
333
330
|
- No mypy errors found.
|
|
334
331
|
- No pylint warnings.
|
|
335
332
|
- No python layout warnings.
|
|
336
|
-
- Built version(s): 0
|
|
333
|
+
- Built version(s): 1.0
|
|
337
334
|
- Build and test using Python 3.14.7
|
|
@@ -235,15 +235,12 @@ from backlogops import (
|
|
|
235
235
|
|
|
236
236
|
### Editing a configuration
|
|
237
237
|
|
|
238
|
-
- `edit_model_for`, `default_edit_config`: build the `edit-cfg-json` model
|
|
239
|
-
of an editing session over a `BacklogOpsConfig` or a stand-alone input
|
|
240
|
-
or output preset, for the Tkinter editor of the graphical interface and
|
|
241
|
-
the Textual editor of the command line.
|
|
242
|
-
|
|
243
238
|
- `CONFIG_DESCRIPTIONS`, `INPUT_DESCRIPTIONS`, `OUTPUT_DESCRIPTIONS`:
|
|
244
239
|
what each configuration member is for, shown below it in the editor.
|
|
245
240
|
A member has no docstring at runtime, so this is what the editor is
|
|
246
241
|
told; `descriptions_for` picks the mapping of one configuration class.
|
|
242
|
+
The nested TableIO endpoint is described by `tableio-cfg-json`, which
|
|
243
|
+
is asked for that text rather than having it repeated here.
|
|
247
244
|
|
|
248
245
|
- `EDIT_SETTINGS`: what the editor may do to a configuration file of this
|
|
249
246
|
application, including keeping what it writes over as a `.bak` file.
|
|
@@ -292,10 +289,10 @@ For the full set of public names see the API documentation linked above.
|
|
|
292
289
|
|
|
293
290
|
## Test summary
|
|
294
291
|
|
|
295
|
-
- Test result:
|
|
292
|
+
- Test result: 2234 passed, 1 deselected in 45s
|
|
296
293
|
- No flake8 warnings.
|
|
297
294
|
- No mypy errors found.
|
|
298
295
|
- No pylint warnings.
|
|
299
296
|
- No python layout warnings.
|
|
300
|
-
- Built version(s): 0
|
|
297
|
+
- Built version(s): 1.0
|
|
301
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: 0
|
|
3
|
+
Version: 1.0
|
|
4
4
|
Summary: Library with backlog operations.
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -25,10 +25,10 @@ Requires-Dist: config-as-json>=1.5
|
|
|
25
25
|
Requires-Dist: cryptography>=50.0.0
|
|
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.3
|
|
29
29
|
Requires-Dist: wizard-ui-bridge[textual]>=1.3
|
|
30
|
-
Requires-Dist: edit-cfg-json==0.0.2
|
|
31
30
|
Requires-Dist: versionreporter>=0.4
|
|
31
|
+
Requires-Dist: edit-cfg-json==0.0.4
|
|
32
32
|
Dynamic: author
|
|
33
33
|
Dynamic: license-file
|
|
34
34
|
Dynamic: requires-dist
|
|
@@ -271,15 +271,12 @@ from backlogops import (
|
|
|
271
271
|
|
|
272
272
|
### Editing a configuration
|
|
273
273
|
|
|
274
|
-
- `edit_model_for`, `default_edit_config`: build the `edit-cfg-json` model
|
|
275
|
-
of an editing session over a `BacklogOpsConfig` or a stand-alone input
|
|
276
|
-
or output preset, for the Tkinter editor of the graphical interface and
|
|
277
|
-
the Textual editor of the command line.
|
|
278
|
-
|
|
279
274
|
- `CONFIG_DESCRIPTIONS`, `INPUT_DESCRIPTIONS`, `OUTPUT_DESCRIPTIONS`:
|
|
280
275
|
what each configuration member is for, shown below it in the editor.
|
|
281
276
|
A member has no docstring at runtime, so this is what the editor is
|
|
282
277
|
told; `descriptions_for` picks the mapping of one configuration class.
|
|
278
|
+
The nested TableIO endpoint is described by `tableio-cfg-json`, which
|
|
279
|
+
is asked for that text rather than having it repeated here.
|
|
283
280
|
|
|
284
281
|
- `EDIT_SETTINGS`: what the editor may do to a configuration file of this
|
|
285
282
|
application, including keeping what it writes over as a `.bak` file.
|
|
@@ -328,10 +325,10 @@ For the full set of public names see the API documentation linked above.
|
|
|
328
325
|
|
|
329
326
|
## Test summary
|
|
330
327
|
|
|
331
|
-
- Test result:
|
|
328
|
+
- Test result: 2234 passed, 1 deselected in 45s
|
|
332
329
|
- No flake8 warnings.
|
|
333
330
|
- No mypy errors found.
|
|
334
331
|
- No pylint warnings.
|
|
335
332
|
- No python layout warnings.
|
|
336
|
-
- Built version(s): 0
|
|
333
|
+
- Built version(s): 1.0
|
|
337
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='0
|
|
8
|
+
version='1.0',
|
|
9
9
|
description='Library with backlog operations.',
|
|
10
10
|
author='Tom Björkholm',
|
|
11
11
|
author_email='klausuler_linnet0q@icloud.com',
|
|
@@ -18,9 +18,9 @@ setup(
|
|
|
18
18
|
'cryptography >= 50.0.0',
|
|
19
19
|
'jira[cli,opt] >= 3.10.5',
|
|
20
20
|
'tableio >= 1.1',
|
|
21
|
-
'tableio-cfg-json >= 1.
|
|
21
|
+
'tableio-cfg-json >= 1.3',
|
|
22
22
|
'wizard-ui-bridge[textual] >= 1.3',
|
|
23
|
-
'
|
|
24
|
-
'
|
|
23
|
+
'versionreporter >= 0.4',
|
|
24
|
+
'edit-cfg-json == 0.0.4'
|
|
25
25
|
]
|
|
26
26
|
)
|
|
@@ -32,8 +32,7 @@ from backlogops.backlog_releases_io import (
|
|
|
32
32
|
from backlogops.config_descriptions import (
|
|
33
33
|
CONFIG_DESCRIPTIONS, GUI_DESCRIPTIONS, INPUT_DESCRIPTIONS,
|
|
34
34
|
JIRA_DESCRIPTIONS, OUTPUT_DESCRIPTIONS, WORKFORCE_DESCRIPTIONS)
|
|
35
|
-
from backlogops.config_editing import
|
|
36
|
-
EDIT_SETTINGS, default_edit_config, descriptions_for, edit_model_for)
|
|
35
|
+
from backlogops.config_editing import EDIT_SETTINGS, descriptions_for
|
|
37
36
|
from backlogops.config_file_io import (
|
|
38
37
|
CONFIG_EXTENSION, io_preset_class, read_io_preset, safe_write_config)
|
|
39
38
|
from backlogops.date_ranges import check_date_range, check_no_overlap
|
|
@@ -141,9 +140,9 @@ __all__ = [
|
|
|
141
140
|
'apply_jira_keys', 'available_teams_wizard', 'backlog_in_release_order',
|
|
142
141
|
'backlog_ops_wizard', 'build_backlog_releases', 'build_dependency_graph',
|
|
143
142
|
'check_backlog_consistency', 'check_date_range',
|
|
144
|
-
'check_levels_consistency', 'check_no_overlap',
|
|
143
|
+
'check_levels_consistency', 'check_no_overlap',
|
|
145
144
|
'default_jira_filter', 'descriptions_for',
|
|
146
|
-
'display_level_order', 'display_level_rows',
|
|
145
|
+
'display_level_order', 'display_level_rows',
|
|
147
146
|
'encrypt_token_file',
|
|
148
147
|
'encrypt_token_to_file', 'estimate_ready_date', 'estimate_release_dates',
|
|
149
148
|
'event_finish', 'event_start', 'find_cycle', 'fold_level_name',
|
|
@@ -40,11 +40,15 @@ more names than fit in one such line has the meaning of them said about the
|
|
|
40
40
|
member instead, which is why ``token_storage`` explains its modes here and
|
|
41
41
|
``level_display`` does not.
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
The nested TableIO endpoint is described by ``tableio_cfg_json``, which
|
|
44
|
+
owns those members: :func:`tio_json_descriptions` is asked for their text
|
|
45
|
+
under the path of the member holding the endpoint, so the TableIO
|
|
46
|
+
documentation is neither repeated here nor able to drift from it. What is
|
|
47
|
+
written here about that endpoint is the one line about the member itself.
|
|
48
|
+
|
|
49
|
+
One more thing is deliberately left out: a limit that lives inside a
|
|
50
|
+
validator is not read by the editor and is stated in words where it
|
|
51
|
+
matters.
|
|
48
52
|
"""
|
|
49
53
|
|
|
50
54
|
# Copyright (c) 2026, Tom Björkholm
|
|
@@ -52,6 +56,7 @@ words where it matters.
|
|
|
52
56
|
|
|
53
57
|
from config_as_json import ConfigPath
|
|
54
58
|
from edit_cfg_json import Descriptions
|
|
59
|
+
from tableio_cfg_json import tio_json_descriptions
|
|
55
60
|
|
|
56
61
|
EVERY = '['
|
|
57
62
|
"""The path step meaning every element of a list or every value of a dict.
|
|
@@ -169,12 +174,17 @@ WORKFORCE_DESCRIPTIONS: Descriptions = {
|
|
|
169
174
|
_FTE_EXCEPTION)}
|
|
170
175
|
"""What every member of an ``AvailableTeamsConfig`` is for."""
|
|
171
176
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
"""What the nested TableIO endpoint of one preset
|
|
177
|
+
_TABLEIO: Descriptions = {
|
|
178
|
+
('tableio',): 'How the file itself is read or written: its format, and '
|
|
179
|
+
'the settings of that format. A setting that is not in '
|
|
180
|
+
'the file keeps its default and has no line here.',
|
|
181
|
+
**tio_json_descriptions(('tableio',))}
|
|
182
|
+
"""What the nested TableIO endpoint of one preset and its settings are for.
|
|
183
|
+
|
|
184
|
+
Only the line about the member itself is written here. Everything below it
|
|
185
|
+
belongs to ``tableio_cfg_json``, which is asked for it, so the formats,
|
|
186
|
+
implementations and values named are the ones registered now.
|
|
187
|
+
"""
|
|
178
188
|
|
|
179
189
|
_MAPPED_COLUMN = ('Empty drops the column altogether. A column that is not '
|
|
180
190
|
'named here keeps its own name.')
|
|
@@ -198,7 +208,7 @@ INPUT_DESCRIPTIONS: Descriptions = {
|
|
|
198
208
|
'alone.',
|
|
199
209
|
('status_input_map', EVERY): 'Internal status this name is read as: one '
|
|
200
210
|
'of TODO, IN_PROGRESS, DONE or REJECTED.',
|
|
201
|
-
|
|
211
|
+
**_TABLEIO}
|
|
202
212
|
"""What every member of an ``InputFormatConfig`` is for."""
|
|
203
213
|
|
|
204
214
|
|
|
@@ -231,7 +241,7 @@ def _display_members(action: str) -> Descriptions:
|
|
|
231
241
|
|
|
232
242
|
|
|
233
243
|
OUTPUT_DESCRIPTIONS: Descriptions = {**_display_members('written'),
|
|
234
|
-
|
|
244
|
+
**_TABLEIO}
|
|
235
245
|
"""What every member of an ``OutputFormatConfig`` is for."""
|
|
236
246
|
|
|
237
247
|
GUI_DESCRIPTIONS: Descriptions = _display_members('shown')
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#! /usr/local/bin/python3
|
|
2
|
+
"""What a configuration editor of a UI is told about this application.
|
|
3
|
+
|
|
4
|
+
The wizard asks one question after another, which is what building a
|
|
5
|
+
configuration from nothing wants. Editing an existing one wants the other
|
|
6
|
+
shape: the whole configuration on the screen at once, folded where it is
|
|
7
|
+
deep, so a single value can be changed without walking past everything
|
|
8
|
+
else. ``edit_cfg_json`` is that editor, and this module is the two answers
|
|
9
|
+
it asks the application for, so that the command line and the graphical
|
|
10
|
+
interface show one configuration the same way.
|
|
11
|
+
|
|
12
|
+
The first answer is :func:`descriptions_for`, which is what each editable
|
|
13
|
+
class says about its own members. Which class is edited is the class of the
|
|
14
|
+
configuration object the editor is given, so the caller decides whether a
|
|
15
|
+
complete backlog-ops configuration or a stand-alone preset is being edited,
|
|
16
|
+
and the descriptions of that class follow from it. A caller that has a
|
|
17
|
+
class rather than an object, such as a command line naming the kind of file
|
|
18
|
+
it edits, gets the object from ``edit_cfg_json.default_config``.
|
|
19
|
+
|
|
20
|
+
The second answer is :data:`EDIT_SETTINGS`, which is what the editor may do
|
|
21
|
+
to a file. Saving is the editor's own: it validates the whole configuration
|
|
22
|
+
through the configuration class and only then writes, keeping what it wrote
|
|
23
|
+
over as a ``.bak`` file, because an editor overwrites the file it read.
|
|
24
|
+
|
|
25
|
+
Nothing here opens an editor. A user interface that already runs its own
|
|
26
|
+
toolkit mounts the editor itself — ``edit_cfg_json_tk.TkEditorPanel`` in a
|
|
27
|
+
window of the application, ``edit_cfg_json_textual.edit`` in a terminal of
|
|
28
|
+
its own — and hands those two answers to it.
|
|
29
|
+
|
|
30
|
+
Three things the editor cannot do are worth knowing before it is offered
|
|
31
|
+
instead of the wizard, and all three are of ``edit_cfg_json`` itself rather
|
|
32
|
+
than of this configuration:
|
|
33
|
+
|
|
34
|
+
* A dict whose keys the application validates for itself cannot gain or
|
|
35
|
+
lose a key. That is the status map, the column-name maps of a preset and
|
|
36
|
+
of the display, and the Jira column and issue-type maps: their values are
|
|
37
|
+
editable, and a new entry is the wizard's to create.
|
|
38
|
+
* A member left out of the file has no row, so the levels can be edited
|
|
39
|
+
only in a configuration that already states them.
|
|
40
|
+
* A person is keyed by their own name in lower case, so renaming one means
|
|
41
|
+
adding an entry under the new key and removing the old one; editing the
|
|
42
|
+
name alone leaves a configuration the class refuses.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
# Copyright (c) 2026, Tom Björkholm
|
|
46
|
+
# MIT License
|
|
47
|
+
|
|
48
|
+
from typing import Optional
|
|
49
|
+
from config_as_json import Config
|
|
50
|
+
from edit_cfg_json import Descriptions, Settings
|
|
51
|
+
from backlogops.backlog_ops_config import BacklogOpsConfig
|
|
52
|
+
from backlogops.config_descriptions import CONFIG_DESCRIPTIONS, \
|
|
53
|
+
INPUT_DESCRIPTIONS, OUTPUT_DESCRIPTIONS
|
|
54
|
+
from backlogops.config_file_io import CONFIG_EXTENSION
|
|
55
|
+
from backlogops.io_config import InputFormatConfig, OutputFormatConfig
|
|
56
|
+
|
|
57
|
+
EDIT_SETTINGS = Settings(file_extension=CONFIG_EXTENSION,
|
|
58
|
+
extension_enforced=False, backup_suffix='.bak',
|
|
59
|
+
backup_count=1, confirm_overwrite=True)
|
|
60
|
+
"""What the editor may do to a configuration file of this application.
|
|
61
|
+
|
|
62
|
+
The extension is added to a destination that has none, and a name with
|
|
63
|
+
another extension is accepted, which is how the wizard commands complete a
|
|
64
|
+
file name too. Overwriting a file this session has not written is confirmed
|
|
65
|
+
first and the previous content is then kept as that name plus ``.bak``,
|
|
66
|
+
because the editor writes over the file it read and that file may hold a
|
|
67
|
+
configuration somebody else wrote.
|
|
68
|
+
|
|
69
|
+
The default key combinations apply: none of them is taken by the
|
|
70
|
+
application around the editor.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
CLASS_DESCRIPTIONS: dict[type[Config], Descriptions] = {
|
|
74
|
+
BacklogOpsConfig: CONFIG_DESCRIPTIONS,
|
|
75
|
+
InputFormatConfig: INPUT_DESCRIPTIONS,
|
|
76
|
+
OutputFormatConfig: OUTPUT_DESCRIPTIONS}
|
|
77
|
+
"""What each editable configuration class says about its own members."""
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def descriptions_for(config: Config) -> Optional[Descriptions]:
|
|
81
|
+
"""Return what the class of one configuration says about its members.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
config: The configuration object that is to be edited.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
The descriptions of that class, or None for a class this library
|
|
88
|
+
says nothing about, which the editor shows without descriptions.
|
|
89
|
+
"""
|
|
90
|
+
return CLASS_DESCRIPTIONS.get(type(config))
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
#! /usr/local/bin/python3
|
|
2
|
-
"""Build the edit model that a configuration editor of a UI shows.
|
|
3
|
-
|
|
4
|
-
The wizard asks one question after another, which is what building a
|
|
5
|
-
configuration from nothing wants. Editing an existing one wants the other
|
|
6
|
-
shape: the whole configuration on the screen at once, folded where it is
|
|
7
|
-
deep, so a single value can be changed without walking past everything
|
|
8
|
-
else. ``edit_cfg_json`` is that editor, and this module is everything of it
|
|
9
|
-
that is not a widget, so that the command line and the graphical interface
|
|
10
|
-
show one configuration the same way.
|
|
11
|
-
|
|
12
|
-
:func:`edit_model_for` reads the input file and returns the model to show.
|
|
13
|
-
Which class is edited is the class of the configuration object it is given,
|
|
14
|
-
so the caller decides whether a complete backlog-ops configuration or a
|
|
15
|
-
stand-alone preset is being edited, and the descriptions of that class
|
|
16
|
-
follow from it. A caller that has a class rather than an object, such as a
|
|
17
|
-
command line naming the kind of file it edits, gets the object from
|
|
18
|
-
:func:`default_edit_config`.
|
|
19
|
-
|
|
20
|
-
What the editor may do to a file is :data:`EDIT_SETTINGS`. Saving is the
|
|
21
|
-
editor's own: it validates the whole configuration through the
|
|
22
|
-
configuration class and only then writes, keeping what it wrote over as a
|
|
23
|
-
``.bak`` file, because an editor overwrites the file it read.
|
|
24
|
-
|
|
25
|
-
Three things the editor cannot do are worth knowing before it is offered
|
|
26
|
-
instead of the wizard, and all three are of ``edit_cfg_json`` itself rather
|
|
27
|
-
than of this configuration:
|
|
28
|
-
|
|
29
|
-
* A dict whose keys the application validates for itself cannot gain or
|
|
30
|
-
lose a key. That is the status map, the column-name maps of a preset and
|
|
31
|
-
of the display, and the Jira column and issue-type maps: their values are
|
|
32
|
-
editable, and a new entry is the wizard's to create.
|
|
33
|
-
* A member left out of the file has no row, so the levels can be edited
|
|
34
|
-
only in a configuration that already states them.
|
|
35
|
-
* A person is keyed by their own name in lower case, so renaming one means
|
|
36
|
-
adding an entry under the new key and removing the old one; editing the
|
|
37
|
-
name alone leaves a configuration the class refuses.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
# Copyright (c) 2026, Tom Björkholm
|
|
41
|
-
# MIT License
|
|
42
|
-
|
|
43
|
-
import sys
|
|
44
|
-
from typing import Optional, TextIO
|
|
45
|
-
from config_as_json import Config, PathOrStr
|
|
46
|
-
from edit_cfg_json import ConfigLoadError, Descriptions, EditModel, Settings, \
|
|
47
|
-
default_config, load_config
|
|
48
|
-
from backlogops.backlog_ops_config import BacklogOpsConfig
|
|
49
|
-
from backlogops.config_descriptions import CONFIG_DESCRIPTIONS, \
|
|
50
|
-
INPUT_DESCRIPTIONS, OUTPUT_DESCRIPTIONS
|
|
51
|
-
from backlogops.config_file_io import CONFIG_EXTENSION
|
|
52
|
-
from backlogops.io_config import InputFormatConfig, OutputFormatConfig
|
|
53
|
-
|
|
54
|
-
EDIT_SETTINGS = Settings(file_extension=CONFIG_EXTENSION,
|
|
55
|
-
extension_enforced=False, backup_suffix='.bak',
|
|
56
|
-
backup_count=1, confirm_overwrite=True)
|
|
57
|
-
"""What the editor may do to a configuration file of this application.
|
|
58
|
-
|
|
59
|
-
The extension is added to a destination that has none, and a name with
|
|
60
|
-
another extension is accepted, which is how the wizard commands complete a
|
|
61
|
-
file name too. Overwriting a file this session has not written is confirmed
|
|
62
|
-
first and the previous content is then kept as that name plus ``.bak``,
|
|
63
|
-
because the editor writes over the file it read and that file may hold a
|
|
64
|
-
configuration somebody else wrote.
|
|
65
|
-
|
|
66
|
-
The default key combinations apply: none of them is taken by the
|
|
67
|
-
application around the editor.
|
|
68
|
-
"""
|
|
69
|
-
|
|
70
|
-
CLASS_DESCRIPTIONS: dict[type[Config], Descriptions] = {
|
|
71
|
-
BacklogOpsConfig: CONFIG_DESCRIPTIONS,
|
|
72
|
-
InputFormatConfig: INPUT_DESCRIPTIONS,
|
|
73
|
-
OutputFormatConfig: OUTPUT_DESCRIPTIONS}
|
|
74
|
-
"""What each editable configuration class says about its own members."""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def descriptions_for(config: Config) -> Optional[Descriptions]:
|
|
78
|
-
"""Return what the class of one configuration says about its members.
|
|
79
|
-
|
|
80
|
-
Args:
|
|
81
|
-
config: The configuration object that is to be edited.
|
|
82
|
-
|
|
83
|
-
Returns:
|
|
84
|
-
The descriptions of that class, or None for a class this library
|
|
85
|
-
says nothing about, which the editor shows without descriptions.
|
|
86
|
-
"""
|
|
87
|
-
return CLASS_DESCRIPTIONS.get(type(config))
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def default_edit_config(config_type: type[Config]) -> Config:
|
|
91
|
-
"""Return a configuration holding the declared defaults of one class.
|
|
92
|
-
|
|
93
|
-
It is the door for a caller that has a class rather than an object,
|
|
94
|
-
which is what a command line naming the kind of file it edits has.
|
|
95
|
-
|
|
96
|
-
Args:
|
|
97
|
-
config_type: The configuration class to be edited.
|
|
98
|
-
|
|
99
|
-
Returns:
|
|
100
|
-
A configuration object holding only what that class declares.
|
|
101
|
-
|
|
102
|
-
Raises:
|
|
103
|
-
ValueError: The editor cannot construct that class on its own.
|
|
104
|
-
"""
|
|
105
|
-
try:
|
|
106
|
-
return default_config(config_type)
|
|
107
|
-
except ConfigLoadError as error:
|
|
108
|
-
raise ValueError(str(error)) from error
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def edit_model_for(config: Config, *, in_file: Optional[PathOrStr] = None,
|
|
112
|
-
out_file: Optional[PathOrStr] = None,
|
|
113
|
-
stderr_file: TextIO = sys.stderr) -> EditModel:
|
|
114
|
-
"""Read the configuration to edit and return the model of a session.
|
|
115
|
-
|
|
116
|
-
The class of ``config`` decides which class is edited and supplies the
|
|
117
|
-
values a member the input file leaves out falls back to. The object
|
|
118
|
-
itself is never modified: what a save wrote is
|
|
119
|
-
``EditModel.saved_config``.
|
|
120
|
-
|
|
121
|
-
Args:
|
|
122
|
-
config: Configuration object of the class to edit, holding the
|
|
123
|
-
values to start from when there is no input file.
|
|
124
|
-
in_file: Configuration file to read, or None to edit the values
|
|
125
|
-
that ``config`` holds.
|
|
126
|
-
out_file: Configuration file a save writes, or None to write the
|
|
127
|
-
input file. With neither, the editor asks the user for one
|
|
128
|
-
before it can save. A destination named here is one this
|
|
129
|
-
session chose, so it is given the configuration extension when
|
|
130
|
-
it has none; the input file is inherited and taken as it is.
|
|
131
|
-
stderr_file: Stream used for user-facing diagnostics.
|
|
132
|
-
|
|
133
|
-
Returns:
|
|
134
|
-
The model of one editing session, for a UI backend to show.
|
|
135
|
-
|
|
136
|
-
Raises:
|
|
137
|
-
ValueError: The input file cannot be opened for editing. The
|
|
138
|
-
message holds what the configuration class said about it.
|
|
139
|
-
"""
|
|
140
|
-
try:
|
|
141
|
-
loaded = load_config(config, in_file=in_file, settings=EDIT_SETTINGS)
|
|
142
|
-
except ConfigLoadError as error:
|
|
143
|
-
raise ValueError(str(error)) from error
|
|
144
|
-
model = EditModel(loaded.config, loaded.report,
|
|
145
|
-
descriptions=descriptions_for(config), out_file=in_file,
|
|
146
|
-
settings=EDIT_SETTINGS, stderr_file=stderr_file)
|
|
147
|
-
if out_file is not None:
|
|
148
|
-
model.set_out_file(out_file)
|
|
149
|
-
return model
|
|
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
|
|
File without changes
|