backlogops-cli 1.3__tar.gz → 1.4__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_cli-1.3 → backlogops_cli-1.4}/PKG-INFO +27 -12
- {backlogops_cli-1.3 → backlogops_cli-1.4}/README_pypi.md +24 -9
- {backlogops_cli-1.3 → backlogops_cli-1.4}/backlogops_cli.egg-info/PKG-INFO +27 -12
- {backlogops_cli-1.3 → backlogops_cli-1.4}/backlogops_cli.egg-info/requires.txt +2 -2
- {backlogops_cli-1.3 → backlogops_cli-1.4}/setup.py +3 -3
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/_command_io.py +24 -5
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/add_to_jira.py +3 -1
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/estimate_ready_date.py +38 -12
- backlogops_cli-1.4/src/backlogops_cli/jira_fields.py +135 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/preset_wizard.py +3 -2
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/update_backlog_in_jira.py +35 -12
- backlogops_cli-1.3/src/backlogops_cli/jira_fields.py +0 -83
- {backlogops_cli-1.3 → backlogops_cli-1.4}/LICENSE.txt +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/backlogops_cli.egg-info/SOURCES.txt +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/backlogops_cli.egg-info/dependency_links.txt +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/backlogops_cli.egg-info/top_level.txt +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/pyproject.toml +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/setup.cfg +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/__init__.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/__main__.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/_migrate_warn.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/_wizard_io.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/add_releases_to_jira.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/adjust_release_content.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/bloc_version_reporter.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/config_edit.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/config_wizard.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/convert.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/demo_backlog.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/encrypt_token_file.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/extract_keys.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/list.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/migrate_cfg.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/order_by_deps.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/order_by_keys.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/order_by_release.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/order_releases.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/order_releases_in_jira.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/plan_release_dates.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/py.typed +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/rank_in_jira.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/read_jira.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/rename_releases_in_jira.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/update_releases_in_jira.py +0 -0
- {backlogops_cli-1.3 → backlogops_cli-1.4}/src/backlogops_cli/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: backlogops-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4
|
|
4
4
|
Summary: Command line interface for backlog operations.
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -24,9 +24,9 @@ Requires-Python: >=3.12
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE.txt
|
|
26
26
|
Requires-Dist: argcomplete>=3.7.2
|
|
27
|
-
Requires-Dist: backlogops>=1.
|
|
27
|
+
Requires-Dist: backlogops>=1.4
|
|
28
28
|
Requires-Dist: wizard-ui-bridge[textual]>=1.4
|
|
29
|
-
Requires-Dist: edit-cfg-json-textual>=0.
|
|
29
|
+
Requires-Dist: edit-cfg-json-textual>=0.4.0
|
|
30
30
|
Requires-Dist: versionreporter>=0.4
|
|
31
31
|
Dynamic: author
|
|
32
32
|
Dynamic: license-file
|
|
@@ -76,6 +76,10 @@ The following functionality is available in all 3 packages:
|
|
|
76
76
|
- Estimate ready date for the backlog items based on available teams, team
|
|
77
77
|
velocity, vacation dates, periods with half time work, etc.
|
|
78
78
|
|
|
79
|
+
- Alternatively, where remaining time estimates are required, estimate ready
|
|
80
|
+
dates from the remaining time of the backlog items and the focus factor of the
|
|
81
|
+
teams (story points are recommended).
|
|
82
|
+
|
|
79
83
|
- Configure what a backlog item that nobody has estimated counts as, per backlog
|
|
80
84
|
item level, so that an unestimated item does not make the estimated dates too
|
|
81
85
|
optimistic.
|
|
@@ -104,6 +108,9 @@ The following functionality is available in all 3 packages:
|
|
|
104
108
|
|
|
105
109
|
- Rename columns when reading a file and when writing a file.
|
|
106
110
|
|
|
111
|
+
- Leave out columns that are empty on every row, when writing a file and when
|
|
112
|
+
showing the backlog in the GUI.
|
|
113
|
+
|
|
107
114
|
- Map custom status names in input files to backlog item statuses.
|
|
108
115
|
|
|
109
116
|
- Choose how backlog item levels are written: by number, by name, or both, and
|
|
@@ -115,8 +122,8 @@ The following functionality is available in all 3 packages:
|
|
|
115
122
|
|
|
116
123
|
- A wizard to create a backlog-ops configuration file with the workforce, named
|
|
117
124
|
input and output presets, level names, status name mapping, the guess for
|
|
118
|
-
unestimated items,
|
|
119
|
-
Jira integration.
|
|
125
|
+
unestimated items, whether remaining time estimates are also used, how the
|
|
126
|
+
graphical user interface shows the data, and the Jira integration.
|
|
120
127
|
|
|
121
128
|
- An editor showing a whole configuration file or a stand-alone preset file at
|
|
122
129
|
once, folded where it is deep, to change a single value without stepping
|
|
@@ -146,6 +153,10 @@ The following functionality is available in all 3 packages:
|
|
|
146
153
|
Jira refused or what was skipped, so a value that was not written is visible
|
|
147
154
|
without reading the whole listing.
|
|
148
155
|
|
|
156
|
+
- Show the custom fields of Jira, the fields an issue's edit screen accepts, and
|
|
157
|
+
the raw JSON Jira holds for a field of an issue, to find the correct Jira
|
|
158
|
+
field mapping.
|
|
159
|
+
|
|
149
160
|
## The operating model
|
|
150
161
|
|
|
151
162
|
The operating model that most of the functionality is designed for is that the
|
|
@@ -154,8 +165,8 @@ are ordered by priority and dependencies to allow the teams to work in the
|
|
|
154
165
|
backlog order. Each backlog item and each release may have a planned ready date,
|
|
155
166
|
that records what has been communicated to the customer. Each backlog item and
|
|
156
167
|
each release may have an estimated ready date, that is calculated from the
|
|
157
|
-
current backlog state, the team velocity,
|
|
158
|
-
availability of the team members.
|
|
168
|
+
current backlog state, the team velocity (or, for remaining time estimates, the
|
|
169
|
+
focus factor), and what we know about the availability of the team members.
|
|
159
170
|
|
|
160
171
|
## The backlog item fields
|
|
161
172
|
|
|
@@ -190,7 +201,11 @@ the library:
|
|
|
190
201
|
|
|
191
202
|
- `team` The team responsible for the backlog item. Optional. Must not be empty
|
|
192
203
|
string. Must be a valid team name. If None the item can be done by any team.
|
|
193
|
-
If not None
|
|
204
|
+
If not None, the item can only be done by the specified team.
|
|
205
|
+
|
|
206
|
+
- `remaining_time` The remaining time of the backlog item, as ideal focused work
|
|
207
|
+
time of one person. Optional. Only used by the alternative remaining time
|
|
208
|
+
estimates; story points are recommended. Must not be negative.
|
|
194
209
|
|
|
195
210
|
- `depends_on_f2s` The list of keys of the backlog items that must have been
|
|
196
211
|
finished before the current item can start. May be empty.
|
|
@@ -230,7 +245,7 @@ pip install --upgrade backlogops-cli
|
|
|
230
245
|
|
|
231
246
|
For a better understanding of how to use the library, CLI or GUI, see the user
|
|
232
247
|
documentation:
|
|
233
|
-
[Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc
|
|
248
|
+
[Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc/using_backlogops/README.md)
|
|
234
249
|
|
|
235
250
|
## API documentation
|
|
236
251
|
|
|
@@ -291,7 +306,7 @@ backlogops_cli serves 2 purposes:
|
|
|
291
306
|
Extract backlog keys at the given levels to a key list
|
|
292
307
|
|
|
293
308
|
python3 -m backlogops_cli.jira_fields
|
|
294
|
-
Print Jira custom fields
|
|
309
|
+
Print Jira custom fields, an issue's editable fields and JSON
|
|
295
310
|
|
|
296
311
|
python3 -m backlogops_cli.list
|
|
297
312
|
List all commands available in backlogops_cli
|
|
@@ -342,10 +357,10 @@ backlogops_cli serves 2 purposes:
|
|
|
342
357
|
|
|
343
358
|
## Test summary
|
|
344
359
|
|
|
345
|
-
- Test result:
|
|
360
|
+
- Test result: 2902 passed, 1 deselected in 68s (0:01:08)
|
|
346
361
|
- No flake8 warnings.
|
|
347
362
|
- No mypy errors found.
|
|
348
363
|
- No pylint warnings.
|
|
349
364
|
- No python layout warnings.
|
|
350
|
-
- Built version(s): 1.
|
|
365
|
+
- Built version(s): 1.4
|
|
351
366
|
- Build and test using Python 3.14.7
|
|
@@ -41,6 +41,10 @@ The following functionality is available in all 3 packages:
|
|
|
41
41
|
- Estimate ready date for the backlog items based on available teams, team
|
|
42
42
|
velocity, vacation dates, periods with half time work, etc.
|
|
43
43
|
|
|
44
|
+
- Alternatively, where remaining time estimates are required, estimate ready
|
|
45
|
+
dates from the remaining time of the backlog items and the focus factor of the
|
|
46
|
+
teams (story points are recommended).
|
|
47
|
+
|
|
44
48
|
- Configure what a backlog item that nobody has estimated counts as, per backlog
|
|
45
49
|
item level, so that an unestimated item does not make the estimated dates too
|
|
46
50
|
optimistic.
|
|
@@ -69,6 +73,9 @@ The following functionality is available in all 3 packages:
|
|
|
69
73
|
|
|
70
74
|
- Rename columns when reading a file and when writing a file.
|
|
71
75
|
|
|
76
|
+
- Leave out columns that are empty on every row, when writing a file and when
|
|
77
|
+
showing the backlog in the GUI.
|
|
78
|
+
|
|
72
79
|
- Map custom status names in input files to backlog item statuses.
|
|
73
80
|
|
|
74
81
|
- Choose how backlog item levels are written: by number, by name, or both, and
|
|
@@ -80,8 +87,8 @@ The following functionality is available in all 3 packages:
|
|
|
80
87
|
|
|
81
88
|
- A wizard to create a backlog-ops configuration file with the workforce, named
|
|
82
89
|
input and output presets, level names, status name mapping, the guess for
|
|
83
|
-
unestimated items,
|
|
84
|
-
Jira integration.
|
|
90
|
+
unestimated items, whether remaining time estimates are also used, how the
|
|
91
|
+
graphical user interface shows the data, and the Jira integration.
|
|
85
92
|
|
|
86
93
|
- An editor showing a whole configuration file or a stand-alone preset file at
|
|
87
94
|
once, folded where it is deep, to change a single value without stepping
|
|
@@ -111,6 +118,10 @@ The following functionality is available in all 3 packages:
|
|
|
111
118
|
Jira refused or what was skipped, so a value that was not written is visible
|
|
112
119
|
without reading the whole listing.
|
|
113
120
|
|
|
121
|
+
- Show the custom fields of Jira, the fields an issue's edit screen accepts, and
|
|
122
|
+
the raw JSON Jira holds for a field of an issue, to find the correct Jira
|
|
123
|
+
field mapping.
|
|
124
|
+
|
|
114
125
|
## The operating model
|
|
115
126
|
|
|
116
127
|
The operating model that most of the functionality is designed for is that the
|
|
@@ -119,8 +130,8 @@ are ordered by priority and dependencies to allow the teams to work in the
|
|
|
119
130
|
backlog order. Each backlog item and each release may have a planned ready date,
|
|
120
131
|
that records what has been communicated to the customer. Each backlog item and
|
|
121
132
|
each release may have an estimated ready date, that is calculated from the
|
|
122
|
-
current backlog state, the team velocity,
|
|
123
|
-
availability of the team members.
|
|
133
|
+
current backlog state, the team velocity (or, for remaining time estimates, the
|
|
134
|
+
focus factor), and what we know about the availability of the team members.
|
|
124
135
|
|
|
125
136
|
## The backlog item fields
|
|
126
137
|
|
|
@@ -155,7 +166,11 @@ the library:
|
|
|
155
166
|
|
|
156
167
|
- `team` The team responsible for the backlog item. Optional. Must not be empty
|
|
157
168
|
string. Must be a valid team name. If None the item can be done by any team.
|
|
158
|
-
If not None
|
|
169
|
+
If not None, the item can only be done by the specified team.
|
|
170
|
+
|
|
171
|
+
- `remaining_time` The remaining time of the backlog item, as ideal focused work
|
|
172
|
+
time of one person. Optional. Only used by the alternative remaining time
|
|
173
|
+
estimates; story points are recommended. Must not be negative.
|
|
159
174
|
|
|
160
175
|
- `depends_on_f2s` The list of keys of the backlog items that must have been
|
|
161
176
|
finished before the current item can start. May be empty.
|
|
@@ -195,7 +210,7 @@ pip install --upgrade backlogops-cli
|
|
|
195
210
|
|
|
196
211
|
For a better understanding of how to use the library, CLI or GUI, see the user
|
|
197
212
|
documentation:
|
|
198
|
-
[Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc
|
|
213
|
+
[Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc/using_backlogops/README.md)
|
|
199
214
|
|
|
200
215
|
## API documentation
|
|
201
216
|
|
|
@@ -256,7 +271,7 @@ backlogops_cli serves 2 purposes:
|
|
|
256
271
|
Extract backlog keys at the given levels to a key list
|
|
257
272
|
|
|
258
273
|
python3 -m backlogops_cli.jira_fields
|
|
259
|
-
Print Jira custom fields
|
|
274
|
+
Print Jira custom fields, an issue's editable fields and JSON
|
|
260
275
|
|
|
261
276
|
python3 -m backlogops_cli.list
|
|
262
277
|
List all commands available in backlogops_cli
|
|
@@ -307,10 +322,10 @@ backlogops_cli serves 2 purposes:
|
|
|
307
322
|
|
|
308
323
|
## Test summary
|
|
309
324
|
|
|
310
|
-
- Test result:
|
|
325
|
+
- Test result: 2902 passed, 1 deselected in 68s (0:01:08)
|
|
311
326
|
- No flake8 warnings.
|
|
312
327
|
- No mypy errors found.
|
|
313
328
|
- No pylint warnings.
|
|
314
329
|
- No python layout warnings.
|
|
315
|
-
- Built version(s): 1.
|
|
330
|
+
- Built version(s): 1.4
|
|
316
331
|
- Build and test using Python 3.14.7
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: backlogops-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4
|
|
4
4
|
Summary: Command line interface for backlog operations.
|
|
5
5
|
Author: Tom Björkholm
|
|
6
6
|
Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
|
|
@@ -24,9 +24,9 @@ Requires-Python: >=3.12
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE.txt
|
|
26
26
|
Requires-Dist: argcomplete>=3.7.2
|
|
27
|
-
Requires-Dist: backlogops>=1.
|
|
27
|
+
Requires-Dist: backlogops>=1.4
|
|
28
28
|
Requires-Dist: wizard-ui-bridge[textual]>=1.4
|
|
29
|
-
Requires-Dist: edit-cfg-json-textual>=0.
|
|
29
|
+
Requires-Dist: edit-cfg-json-textual>=0.4.0
|
|
30
30
|
Requires-Dist: versionreporter>=0.4
|
|
31
31
|
Dynamic: author
|
|
32
32
|
Dynamic: license-file
|
|
@@ -76,6 +76,10 @@ The following functionality is available in all 3 packages:
|
|
|
76
76
|
- Estimate ready date for the backlog items based on available teams, team
|
|
77
77
|
velocity, vacation dates, periods with half time work, etc.
|
|
78
78
|
|
|
79
|
+
- Alternatively, where remaining time estimates are required, estimate ready
|
|
80
|
+
dates from the remaining time of the backlog items and the focus factor of the
|
|
81
|
+
teams (story points are recommended).
|
|
82
|
+
|
|
79
83
|
- Configure what a backlog item that nobody has estimated counts as, per backlog
|
|
80
84
|
item level, so that an unestimated item does not make the estimated dates too
|
|
81
85
|
optimistic.
|
|
@@ -104,6 +108,9 @@ The following functionality is available in all 3 packages:
|
|
|
104
108
|
|
|
105
109
|
- Rename columns when reading a file and when writing a file.
|
|
106
110
|
|
|
111
|
+
- Leave out columns that are empty on every row, when writing a file and when
|
|
112
|
+
showing the backlog in the GUI.
|
|
113
|
+
|
|
107
114
|
- Map custom status names in input files to backlog item statuses.
|
|
108
115
|
|
|
109
116
|
- Choose how backlog item levels are written: by number, by name, or both, and
|
|
@@ -115,8 +122,8 @@ The following functionality is available in all 3 packages:
|
|
|
115
122
|
|
|
116
123
|
- A wizard to create a backlog-ops configuration file with the workforce, named
|
|
117
124
|
input and output presets, level names, status name mapping, the guess for
|
|
118
|
-
unestimated items,
|
|
119
|
-
Jira integration.
|
|
125
|
+
unestimated items, whether remaining time estimates are also used, how the
|
|
126
|
+
graphical user interface shows the data, and the Jira integration.
|
|
120
127
|
|
|
121
128
|
- An editor showing a whole configuration file or a stand-alone preset file at
|
|
122
129
|
once, folded where it is deep, to change a single value without stepping
|
|
@@ -146,6 +153,10 @@ The following functionality is available in all 3 packages:
|
|
|
146
153
|
Jira refused or what was skipped, so a value that was not written is visible
|
|
147
154
|
without reading the whole listing.
|
|
148
155
|
|
|
156
|
+
- Show the custom fields of Jira, the fields an issue's edit screen accepts, and
|
|
157
|
+
the raw JSON Jira holds for a field of an issue, to find the correct Jira
|
|
158
|
+
field mapping.
|
|
159
|
+
|
|
149
160
|
## The operating model
|
|
150
161
|
|
|
151
162
|
The operating model that most of the functionality is designed for is that the
|
|
@@ -154,8 +165,8 @@ are ordered by priority and dependencies to allow the teams to work in the
|
|
|
154
165
|
backlog order. Each backlog item and each release may have a planned ready date,
|
|
155
166
|
that records what has been communicated to the customer. Each backlog item and
|
|
156
167
|
each release may have an estimated ready date, that is calculated from the
|
|
157
|
-
current backlog state, the team velocity,
|
|
158
|
-
availability of the team members.
|
|
168
|
+
current backlog state, the team velocity (or, for remaining time estimates, the
|
|
169
|
+
focus factor), and what we know about the availability of the team members.
|
|
159
170
|
|
|
160
171
|
## The backlog item fields
|
|
161
172
|
|
|
@@ -190,7 +201,11 @@ the library:
|
|
|
190
201
|
|
|
191
202
|
- `team` The team responsible for the backlog item. Optional. Must not be empty
|
|
192
203
|
string. Must be a valid team name. If None the item can be done by any team.
|
|
193
|
-
If not None
|
|
204
|
+
If not None, the item can only be done by the specified team.
|
|
205
|
+
|
|
206
|
+
- `remaining_time` The remaining time of the backlog item, as ideal focused work
|
|
207
|
+
time of one person. Optional. Only used by the alternative remaining time
|
|
208
|
+
estimates; story points are recommended. Must not be negative.
|
|
194
209
|
|
|
195
210
|
- `depends_on_f2s` The list of keys of the backlog items that must have been
|
|
196
211
|
finished before the current item can start. May be empty.
|
|
@@ -230,7 +245,7 @@ pip install --upgrade backlogops-cli
|
|
|
230
245
|
|
|
231
246
|
For a better understanding of how to use the library, CLI or GUI, see the user
|
|
232
247
|
documentation:
|
|
233
|
-
[Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc
|
|
248
|
+
[Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc/using_backlogops/README.md)
|
|
234
249
|
|
|
235
250
|
## API documentation
|
|
236
251
|
|
|
@@ -291,7 +306,7 @@ backlogops_cli serves 2 purposes:
|
|
|
291
306
|
Extract backlog keys at the given levels to a key list
|
|
292
307
|
|
|
293
308
|
python3 -m backlogops_cli.jira_fields
|
|
294
|
-
Print Jira custom fields
|
|
309
|
+
Print Jira custom fields, an issue's editable fields and JSON
|
|
295
310
|
|
|
296
311
|
python3 -m backlogops_cli.list
|
|
297
312
|
List all commands available in backlogops_cli
|
|
@@ -342,10 +357,10 @@ backlogops_cli serves 2 purposes:
|
|
|
342
357
|
|
|
343
358
|
## Test summary
|
|
344
359
|
|
|
345
|
-
- Test result:
|
|
360
|
+
- Test result: 2902 passed, 1 deselected in 68s (0:01:08)
|
|
346
361
|
- No flake8 warnings.
|
|
347
362
|
- No mypy errors found.
|
|
348
363
|
- No pylint warnings.
|
|
349
364
|
- No python layout warnings.
|
|
350
|
-
- Built version(s): 1.
|
|
365
|
+
- Built version(s): 1.4
|
|
351
366
|
- 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-cli',
|
|
8
|
-
version='1.
|
|
8
|
+
version='1.4',
|
|
9
9
|
description='Command line interface for backlog operations.',
|
|
10
10
|
author='Tom Björkholm',
|
|
11
11
|
author_email='klausuler_linnet0q@icloud.com',
|
|
@@ -15,9 +15,9 @@ setup(
|
|
|
15
15
|
package_data={'backlogops_cli': ['src/py.typed']},
|
|
16
16
|
install_requires=[
|
|
17
17
|
'argcomplete >= 3.7.2',
|
|
18
|
-
'backlogops >= 1.
|
|
18
|
+
'backlogops >= 1.4',
|
|
19
19
|
'wizard-ui-bridge[textual] >= 1.4',
|
|
20
|
-
'edit-cfg-json-textual >= 0.
|
|
20
|
+
'edit-cfg-json-textual >= 0.4.0',
|
|
21
21
|
'versionreporter >= 0.4'
|
|
22
22
|
]
|
|
23
23
|
)
|
|
@@ -146,13 +146,15 @@ def add_config_arg(parser: argparse.ArgumentParser) -> None:
|
|
|
146
146
|
|
|
147
147
|
The configuration file holds the workforce, the named input and output
|
|
148
148
|
presets, the levels, the global status map, the guess for unestimated
|
|
149
|
-
items, the GUI display settings and the
|
|
149
|
+
items, the remaining time settings, the GUI display settings and the
|
|
150
|
+
Jira configuration. Without
|
|
150
151
|
``-c`` the file is discovered the same way as the GUI.
|
|
151
152
|
"""
|
|
152
153
|
parser.add_argument('-c', '--config', dest='config',
|
|
153
154
|
help='Backlog-ops configuration file (workforce, '
|
|
154
155
|
'named presets, levels, status map, default story '
|
|
155
|
-
'points,
|
|
156
|
+
'points, remaining time settings, Jira '
|
|
157
|
+
'configuration). Without -c the '
|
|
156
158
|
'file is found from $BACKLOGOPS_CFG, else '
|
|
157
159
|
'backlogops.cfg in $BACKLOGOPS_DIR, else '
|
|
158
160
|
'$HOME/.backlogops.cfg.')
|
|
@@ -233,6 +235,20 @@ def io_levels(config: Optional[BacklogOpsConfig]) -> Optional[Levels]:
|
|
|
233
235
|
return config.get_levels() if config is not None else None
|
|
234
236
|
|
|
235
237
|
|
|
238
|
+
def io_remaining_time(config: Optional[BacklogOpsConfig]) -> bool:
|
|
239
|
+
"""Return whether ``config`` enables remaining time estimates.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
config: The resolved backlog-ops configuration, or None for the
|
|
243
|
+
defaults, which do not use remaining time estimates.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
True when remaining time estimates are enabled in ``config``.
|
|
247
|
+
"""
|
|
248
|
+
return config is not None and \
|
|
249
|
+
config.remaining_time.enable_remaining_time
|
|
250
|
+
|
|
251
|
+
|
|
236
252
|
def read_input(parsed: argparse.Namespace,
|
|
237
253
|
config: Optional[BacklogOpsConfig]) -> BacklogReleases:
|
|
238
254
|
"""Read and validate the backlog and releases from the input file.
|
|
@@ -345,7 +361,8 @@ def _write_output(parsed: argparse.Namespace,
|
|
|
345
361
|
write_backlog_releases(data, parsed.output, out_config, rules,
|
|
346
362
|
levels=io_levels(config),
|
|
347
363
|
file_exists_callback=overwrite_callback(
|
|
348
|
-
parsed.force)
|
|
364
|
+
parsed.force),
|
|
365
|
+
use_remaining_time=io_remaining_time(config))
|
|
349
366
|
|
|
350
367
|
|
|
351
368
|
def run_write(parsed: argparse.Namespace,
|
|
@@ -407,7 +424,8 @@ def write_result_file(config: BacklogOpsConfig, path: str,
|
|
|
407
424
|
auto_ch_hook=CliPresetMigrateWarnHook())
|
|
408
425
|
write_backlog_releases(data, path, out_config, FormatRules(),
|
|
409
426
|
levels=config.get_levels(),
|
|
410
|
-
file_exists_callback=overwrite_callback(force)
|
|
427
|
+
file_exists_callback=overwrite_callback(force),
|
|
428
|
+
use_remaining_time=io_remaining_time(config))
|
|
411
429
|
print(f'Wrote {path}')
|
|
412
430
|
|
|
413
431
|
|
|
@@ -420,7 +438,8 @@ def run_added_to_jira(
|
|
|
420
438
|
"""Read the input, add it to Jira, and print the added lists.
|
|
421
439
|
|
|
422
440
|
``add`` receives the parsed args, the config and the input data, adds
|
|
423
|
-
to Jira, writes any ``--added``/``--existing`` files, and
|
|
441
|
+
to Jira, writes any ``--added-file``/``--existing-file`` files, and
|
|
442
|
+
returns the
|
|
424
443
|
result. ``report`` formats that result for stdout, printed unless
|
|
425
444
|
``-q``/``--quiet`` was given. ``exists_error`` is the key-exists error
|
|
426
445
|
meaning nothing was added; other value or OS errors are reported with
|
|
@@ -66,7 +66,9 @@ def _add(parsed: argparse.Namespace, config: BacklogOpsConfig,
|
|
|
66
66
|
on_existing_key=mode,
|
|
67
67
|
rank_anchor=rank_anchor(parsed.rank),
|
|
68
68
|
levels=config.get_levels(),
|
|
69
|
-
status_map=config.get_status_input_map()
|
|
69
|
+
status_map=config.get_status_input_map(),
|
|
70
|
+
use_remaining_time=config.remaining_time
|
|
71
|
+
.enable_remaining_time)
|
|
70
72
|
print(f'Added {len(result.stored)} items to Jira; '
|
|
71
73
|
f'{len(result.already_present)} already present; '
|
|
72
74
|
f'{len(result.failed)} failed; '
|
|
@@ -7,10 +7,16 @@ as documented for :func:`backlogops.estimate_ready_date`. The teams
|
|
|
7
7
|
configuration (velocity, work hours, vacations and so on) is taken from
|
|
8
8
|
the file given by ``--config`` or, when that is absent, from the
|
|
9
9
|
configured backlog-ops file, and so is what a backlog item that nobody
|
|
10
|
-
has estimated is worked with.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
has estimated is worked with. With ``--remaining-time`` the ready dates
|
|
11
|
+
are instead estimated from the remaining time of the items and the focus
|
|
12
|
+
factor of the teams (or the configured default focus factor), as
|
|
13
|
+
documented for
|
|
14
|
+
:func:`backlogops.estimate_rt_ready_date`; that needs remaining time
|
|
15
|
+
estimates to be enabled in the configuration, and the command fails
|
|
16
|
+
without writing anything when they are not. The backlog with the
|
|
17
|
+
estimated dates and the releases are written to the output file. The
|
|
18
|
+
input and output formats are inferred from the file name extensions, but
|
|
19
|
+
can be overridden by a configuration file or by a named preset.
|
|
14
20
|
"""
|
|
15
21
|
|
|
16
22
|
# PYTHON_ARGCOMPLETE_OK
|
|
@@ -36,6 +42,16 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
36
42
|
parser.add_argument('-d', '--start-date', dest='start_date',
|
|
37
43
|
metavar='ISO_DATE',
|
|
38
44
|
help='Day the teams start working (default today).')
|
|
45
|
+
parser.add_argument('--remaining-time', dest='remaining_time',
|
|
46
|
+
action='store_true',
|
|
47
|
+
help='Estimate from the remaining time of the items '
|
|
48
|
+
'and the focus factor of the teams (or the default '
|
|
49
|
+
'focus factor) instead of from '
|
|
50
|
+
'story points and velocity. Requires remaining time '
|
|
51
|
+
'estimates to be enabled in the configuration '
|
|
52
|
+
'("enable_remaining_time": true in its '
|
|
53
|
+
'"remaining_time" section); otherwise the command '
|
|
54
|
+
'fails and writes nothing.')
|
|
39
55
|
parser.add_argument('--set-plan', dest='set_plan', action='store_true',
|
|
40
56
|
help='Also copy each estimated date to the planned '
|
|
41
57
|
'date.')
|
|
@@ -55,13 +71,20 @@ def _estimate(parsed: argparse.Namespace, config: Optional[BacklogOpsConfig],
|
|
|
55
71
|
) -> tuple[str, Optional[Callable[[str], None]]]:
|
|
56
72
|
"""Estimate the dates and return the release date change report.
|
|
57
73
|
|
|
58
|
-
The
|
|
59
|
-
|
|
74
|
+
The dates are estimated from remaining time when ``--remaining-time``
|
|
75
|
+
is given, and from story points otherwise. The configuration is
|
|
76
|
+
required for this command, so ``config`` is never None here; the
|
|
77
|
+
assertion makes that explicit for the type checker.
|
|
60
78
|
"""
|
|
61
79
|
assert isinstance(config, BacklogOpsConfig)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
start = _start_date(parsed)
|
|
81
|
+
if parsed.remaining_time:
|
|
82
|
+
changes = data.estimate_rt_ready_date(config.available_teams,
|
|
83
|
+
config.remaining_time, start)
|
|
84
|
+
else:
|
|
85
|
+
changes = data.estimate_ready_date(
|
|
86
|
+
config.available_teams, start,
|
|
87
|
+
default_story_points=config.default_story_points)
|
|
65
88
|
if parsed.set_plan:
|
|
66
89
|
data.set_plan_from_estimate()
|
|
67
90
|
return date_report(changes, overwrite_callback(parsed.force))
|
|
@@ -71,9 +94,12 @@ def main(args: Optional[list[str]] = None) -> int:
|
|
|
71
94
|
"""Estimate the ready dates and write the output file.
|
|
72
95
|
|
|
73
96
|
The backlog with the estimated dates and the releases are written to
|
|
74
|
-
the output file. The
|
|
75
|
-
|
|
76
|
-
|
|
97
|
+
the output file. The ready dates are estimated from story points, or
|
|
98
|
+
from remaining time with ``--remaining-time``, which fails when the
|
|
99
|
+
configuration does not enable remaining time estimates. The estimated
|
|
100
|
+
release dates are updated as well, and the list of release date
|
|
101
|
+
changes is printed to stdout, or also saved to a file when
|
|
102
|
+
``--changes-file`` is given.
|
|
77
103
|
|
|
78
104
|
Args:
|
|
79
105
|
args: Optional replacement for ``sys.argv[1:]``, mainly for tests.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#! /usr/local/bin/python3
|
|
2
|
+
"""Print Jira field information for a preset, to diagnose write mappings.
|
|
3
|
+
|
|
4
|
+
The command prints the custom field id to display name map that the reader
|
|
5
|
+
fetches from Jira, so a column-map name such as 'Story point estimate' can
|
|
6
|
+
be matched to its field id. With ``--issue`` it also prints the fields the
|
|
7
|
+
given issue's edit screen offers, which explains why a mapped field cannot
|
|
8
|
+
be set on that issue's type: a field missing from the edit screen cannot be
|
|
9
|
+
set through the issue edit REST endpoint. Each ``--field`` (which needs
|
|
10
|
+
``--issue``) prints the raw Jira JSON at a field id or dotted path of that
|
|
11
|
+
issue, such as ``timetracking.remainingEstimateSeconds``, and the field's
|
|
12
|
+
edit screen entry, so a column map path can be checked against what Jira
|
|
13
|
+
really holds and how it may be written.
|
|
14
|
+
|
|
15
|
+
An encrypted Jira token is unlocked by a pass phrase asked on the terminal
|
|
16
|
+
only when it is needed.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# PYTHON_ARGCOMPLETE_OK
|
|
20
|
+
# Copyright (c) 2026, Tom Björkholm
|
|
21
|
+
# MIT License
|
|
22
|
+
|
|
23
|
+
import argparse
|
|
24
|
+
import json
|
|
25
|
+
import sys
|
|
26
|
+
from typing import Optional
|
|
27
|
+
from jira import JIRAError
|
|
28
|
+
from backlogops import (
|
|
29
|
+
JiraConnections, JiraFieldJson, jira_custom_fields, jira_editable_fields,
|
|
30
|
+
jira_field_json)
|
|
31
|
+
from backlogops_cli._command_io import (
|
|
32
|
+
build_jira_parser, jira_passphrase, parsed_args, required_config)
|
|
33
|
+
|
|
34
|
+
DESCRIPTION = "Print Jira custom fields, an issue's editable fields and JSON"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
38
|
+
"""Build the command line parser for the field diagnostic command."""
|
|
39
|
+
parser = build_jira_parser(DESCRIPTION, with_input=False)
|
|
40
|
+
parser.add_argument('--issue', dest='issue', metavar='KEY',
|
|
41
|
+
help="Also print the fields this issue's edit "
|
|
42
|
+
'screen offers (for example SCRUM-15).')
|
|
43
|
+
parser.add_argument('--field', dest='fields', action='append',
|
|
44
|
+
metavar='NAME', default=[],
|
|
45
|
+
help='Also print the raw Jira JSON of this field of '
|
|
46
|
+
'the --issue: a field id, a custom field name, or a '
|
|
47
|
+
'dotted path such as '
|
|
48
|
+
'timetracking.remainingEstimateSeconds. Repeatable.')
|
|
49
|
+
return parser
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _print_pairs(heading: str, pairs: list[tuple[str, str]]) -> None:
|
|
53
|
+
"""Print a heading and each field id and display name pair."""
|
|
54
|
+
print(heading)
|
|
55
|
+
if not pairs:
|
|
56
|
+
print(' (none)')
|
|
57
|
+
for field_id, name in pairs:
|
|
58
|
+
print(f' {field_id} {name}')
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _print_json(heading: str, value: object) -> None:
|
|
62
|
+
"""Print a heading and the value as indented JSON below it."""
|
|
63
|
+
print(f' {heading}:')
|
|
64
|
+
text = json.dumps(value, indent=2, ensure_ascii=False, default=str)
|
|
65
|
+
for line in text.splitlines():
|
|
66
|
+
print(f' {line}')
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _print_field(issue: str, found: JiraFieldJson) -> None:
|
|
70
|
+
"""Print one field path's JSON value and its edit screen entry."""
|
|
71
|
+
print(f"\nField '{found.name}' of {issue} (Jira field {found.field_id}):")
|
|
72
|
+
if found.present:
|
|
73
|
+
_print_json('value', found.value)
|
|
74
|
+
else:
|
|
75
|
+
print(' value: (no such path in the issue)')
|
|
76
|
+
if found.edit_entry is None:
|
|
77
|
+
print(' edit screen: (not offered, so not writable by an edit)')
|
|
78
|
+
else:
|
|
79
|
+
_print_json('edit screen', found.edit_entry)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _read_all(parsed: argparse.Namespace) -> tuple[
|
|
83
|
+
list[tuple[str, str]], Optional[list[tuple[str, str]]],
|
|
84
|
+
list[JiraFieldJson]]:
|
|
85
|
+
"""Read the custom fields, edit screen fields and field JSON asked for."""
|
|
86
|
+
config = required_config(parsed)
|
|
87
|
+
connections = JiraConnections(config.get_jira_config(), jira_passphrase)
|
|
88
|
+
customs = jira_custom_fields(connections, parsed.preset)
|
|
89
|
+
if parsed.issue is None:
|
|
90
|
+
return customs, None, []
|
|
91
|
+
editable = jira_editable_fields(connections, parsed.preset, parsed.issue)
|
|
92
|
+
found = jira_field_json(connections, parsed.preset, parsed.issue,
|
|
93
|
+
parsed.fields)
|
|
94
|
+
return customs, editable, found
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _run(parsed: argparse.Namespace) -> int:
|
|
98
|
+
"""Print the custom field map and, optionally, the issue's fields."""
|
|
99
|
+
try:
|
|
100
|
+
customs, editable, found = _read_all(parsed)
|
|
101
|
+
except (ValueError, TypeError, KeyError, OSError, JIRAError) as error:
|
|
102
|
+
print(f'Could not read Jira fields: {error}', file=sys.stderr)
|
|
103
|
+
return 1
|
|
104
|
+
_print_pairs(f"Custom fields for preset '{parsed.preset}':", customs)
|
|
105
|
+
if editable is not None:
|
|
106
|
+
print()
|
|
107
|
+
_print_pairs(f'Fields settable on the edit screen of '
|
|
108
|
+
f'{parsed.issue}:', editable)
|
|
109
|
+
for field in found:
|
|
110
|
+
_print_field(parsed.issue, field)
|
|
111
|
+
return 0
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def main(args: Optional[list[str]] = None) -> int:
|
|
115
|
+
"""Print Jira field information for a preset.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
args: Optional replacement for ``sys.argv[1:]``, mainly for tests.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
``0`` on success, ``1`` when the fields cannot be read.
|
|
122
|
+
|
|
123
|
+
Raises:
|
|
124
|
+
SystemExit: With status ``2`` when ``--field`` is given without
|
|
125
|
+
``--issue``, as for any other command line usage error.
|
|
126
|
+
"""
|
|
127
|
+
parser = build_parser()
|
|
128
|
+
parsed = parsed_args(parser, args)
|
|
129
|
+
if parsed.fields and parsed.issue is None:
|
|
130
|
+
parser.error('--field needs --issue')
|
|
131
|
+
return _run(parsed)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
if __name__ == '__main__': # pragma: no cover
|
|
135
|
+
sys.exit(main())
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
"""Run the IO preset wizard and store the created preset file.
|
|
3
3
|
|
|
4
4
|
The created file holds a single input or output TableIO preset (a format
|
|
5
|
-
configuration with its column-name maps, and a level display
|
|
6
|
-
preset). Such a stand-alone file
|
|
5
|
+
configuration with its column-name maps, and a level display and whether
|
|
6
|
+
to leave out empty columns for an output preset). Such a stand-alone file
|
|
7
|
+
is used wherever an input or output
|
|
7
8
|
configuration is taken, by giving its file name.
|
|
8
9
|
"""
|
|
9
10
|
|
|
@@ -7,11 +7,13 @@ backlog-ops configuration, changing only a chosen subset of the mapped
|
|
|
7
7
|
fields. The subset is chosen with exactly one of two flags: ``-s``/``--store``
|
|
8
8
|
lists the columns to update (or the single word ``all`` for every mapped
|
|
9
9
|
writable column), while ``-e``/``--exclude`` updates every mapped writable
|
|
10
|
-
column except the listed ones.
|
|
10
|
+
column except the listed ones. The remaining time is a writable column only
|
|
11
|
+
while remaining time estimates are enabled.
|
|
11
12
|
|
|
12
13
|
``--on-missing`` chooses what to do with an item whose key is not present in
|
|
13
14
|
Jira: ``raise`` (the default) stops with an error, ``ignore`` leaves it
|
|
14
|
-
alone, and ``add`` creates it with all of its fields
|
|
15
|
+
alone, and ``add`` creates it with all of its mapped fields (the remaining
|
|
16
|
+
time only while remaining time estimates are enabled). ``--links`` chooses how
|
|
15
17
|
the parent and dependency links are updated: ``reconcile`` (the default) makes
|
|
16
18
|
the Jira links match the backlog exactly, removing a Jira link the backlog no
|
|
17
19
|
longer has and clearing a dropped parent, while ``add`` only adds the missing
|
|
@@ -49,6 +51,9 @@ _MISSING_MODES = {'raise': OnMissingKey.RAISE, 'ignore': OnMissingKey.IGNORE,
|
|
|
49
51
|
_LINK_MODES = {'reconcile': LinkUpdate.RECONCILE,
|
|
50
52
|
'add': LinkUpdate.ADD_MISSING}
|
|
51
53
|
_STORE_ALL = 'all'
|
|
54
|
+
_RT_WRITABLE = ('remaining_time is writable only while '
|
|
55
|
+
'"enable_remaining_time" is true in the configuration.')
|
|
56
|
+
"""Help and message note on when the remaining time can be updated."""
|
|
52
57
|
|
|
53
58
|
|
|
54
59
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -76,40 +81,57 @@ def _add_column_flags(parser: argparse.ArgumentParser) -> None:
|
|
|
76
81
|
group.add_argument('-s', '--store', dest='store', nargs='+',
|
|
77
82
|
metavar='COLUMN',
|
|
78
83
|
help='Columns to update, or the single word "all" for '
|
|
79
|
-
'every mapped writable column.')
|
|
84
|
+
'every mapped writable column. ' + _RT_WRITABLE)
|
|
80
85
|
group.add_argument('-e', '--exclude', dest='exclude', nargs='+',
|
|
81
86
|
metavar='COLUMN',
|
|
82
|
-
help='Update every mapped writable column but these.'
|
|
87
|
+
help='Update every mapped writable column but these. '
|
|
88
|
+
+ _RT_WRITABLE)
|
|
83
89
|
|
|
84
90
|
|
|
85
|
-
def _resolve_fields(parsed: argparse.Namespace,
|
|
86
|
-
|
|
91
|
+
def _resolve_fields(parsed: argparse.Namespace, connections: JiraConnections,
|
|
92
|
+
use_remaining_time: bool) -> list[str]:
|
|
87
93
|
"""Return the internal field names to update from the -s/-e flags.
|
|
88
94
|
|
|
89
95
|
``-s all`` and ``-e`` are resolved against the preset's updatable
|
|
90
96
|
columns. A ``-s`` name that is not an updatable column is reported and
|
|
91
|
-
dropped, so a typo does not silently update nothing.
|
|
97
|
+
dropped, so a typo does not silently update nothing. The remaining
|
|
98
|
+
time is an updatable column only while remaining time estimates are
|
|
99
|
+
used.
|
|
92
100
|
"""
|
|
93
|
-
updatable = updatable_backlog_fields(connections, parsed.preset
|
|
101
|
+
updatable = updatable_backlog_fields(connections, parsed.preset,
|
|
102
|
+
use_remaining_time=use_remaining_time)
|
|
94
103
|
if parsed.store is not None:
|
|
95
104
|
if parsed.store == [_STORE_ALL]:
|
|
96
105
|
return updatable
|
|
97
106
|
unknown = [name for name in parsed.store if name not in updatable]
|
|
98
107
|
if unknown:
|
|
99
|
-
|
|
100
|
-
+ ', '.join(unknown), file=sys.stderr)
|
|
108
|
+
_report_ignored(unknown, use_remaining_time)
|
|
101
109
|
return [name for name in parsed.store if name in updatable]
|
|
102
110
|
excluded = set(parsed.exclude)
|
|
103
111
|
return [name for name in updatable if name not in excluded]
|
|
104
112
|
|
|
105
113
|
|
|
114
|
+
def _report_ignored(unknown: list[str], use_remaining_time: bool) -> None:
|
|
115
|
+
"""Report the ``-s`` columns dropped as not updatable to stderr.
|
|
116
|
+
|
|
117
|
+
A dropped ``remaining_time`` while remaining time estimates are not
|
|
118
|
+
used gets a note saying why, because the preset may well map it.
|
|
119
|
+
"""
|
|
120
|
+
note = ''
|
|
121
|
+
if 'remaining_time' in unknown and not use_remaining_time:
|
|
122
|
+
note = ' (' + _RT_WRITABLE + ')'
|
|
123
|
+
print('Ignoring columns not updatable in this preset: '
|
|
124
|
+
+ ', '.join(unknown) + note, file=sys.stderr)
|
|
125
|
+
|
|
126
|
+
|
|
106
127
|
def _update(parsed: argparse.Namespace, config: BacklogOpsConfig,
|
|
107
128
|
data_backlog: object) -> UpdatedBacklogInJira:
|
|
108
129
|
"""Update the input backlog in Jira using the named preset."""
|
|
109
130
|
print(f"Updating backlog in Jira using preset '{parsed.preset}'...",
|
|
110
131
|
file=sys.stderr)
|
|
111
132
|
connections = JiraConnections(config.get_jira_config(), jira_passphrase)
|
|
112
|
-
|
|
133
|
+
use_rt = config.remaining_time.enable_remaining_time
|
|
134
|
+
fields = _resolve_fields(parsed, connections, use_rt)
|
|
113
135
|
mode = _MISSING_MODES[parsed.on_missing]
|
|
114
136
|
link_update = _LINK_MODES[parsed.links]
|
|
115
137
|
assert isinstance(data_backlog, list)
|
|
@@ -119,7 +141,8 @@ def _update(parsed: argparse.Namespace, config: BacklogOpsConfig,
|
|
|
119
141
|
link_update=link_update,
|
|
120
142
|
rank_anchor=rank_anchor(parsed.rank),
|
|
121
143
|
levels=config.get_levels(),
|
|
122
|
-
status_map=config.get_status_input_map()
|
|
144
|
+
status_map=config.get_status_input_map(),
|
|
145
|
+
use_remaining_time=use_rt)
|
|
123
146
|
_report_summary(result)
|
|
124
147
|
return result
|
|
125
148
|
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
#! /usr/local/bin/python3
|
|
2
|
-
"""Print Jira field information for a preset, to diagnose write mappings.
|
|
3
|
-
|
|
4
|
-
The command prints the custom field id to display name map that the reader
|
|
5
|
-
fetches from Jira, so a column-map name such as 'Story point estimate' can
|
|
6
|
-
be matched to its field id. With ``--issue`` it also prints the fields the
|
|
7
|
-
given issue's edit screen offers, which explains why a mapped field cannot
|
|
8
|
-
be set on that issue's type: a field missing from the edit screen cannot be
|
|
9
|
-
set through the issue edit REST endpoint.
|
|
10
|
-
|
|
11
|
-
An encrypted Jira token is unlocked by a pass phrase asked on the terminal
|
|
12
|
-
only when it is needed.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
# PYTHON_ARGCOMPLETE_OK
|
|
16
|
-
# Copyright (c) 2026, Tom Björkholm
|
|
17
|
-
# MIT License
|
|
18
|
-
|
|
19
|
-
import argparse
|
|
20
|
-
import sys
|
|
21
|
-
from typing import Optional
|
|
22
|
-
from jira import JIRAError
|
|
23
|
-
from backlogops import (
|
|
24
|
-
JiraConnections, jira_custom_fields, jira_editable_fields)
|
|
25
|
-
from backlogops_cli._command_io import (
|
|
26
|
-
build_jira_parser, jira_passphrase, parsed_args, required_config)
|
|
27
|
-
|
|
28
|
-
DESCRIPTION = "Print Jira custom fields and an issue's editable fields"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def build_parser() -> argparse.ArgumentParser:
|
|
32
|
-
"""Build the command line parser for the field diagnostic command."""
|
|
33
|
-
parser = build_jira_parser(DESCRIPTION, with_input=False)
|
|
34
|
-
parser.add_argument('--issue', dest='issue', metavar='KEY',
|
|
35
|
-
help="Also print the fields this issue's edit "
|
|
36
|
-
'screen offers (for example SCRUM-15).')
|
|
37
|
-
return parser
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _print_pairs(heading: str, pairs: list[tuple[str, str]]) -> None:
|
|
41
|
-
"""Print a heading and each field id and display name pair."""
|
|
42
|
-
print(heading)
|
|
43
|
-
if not pairs:
|
|
44
|
-
print(' (none)')
|
|
45
|
-
for field_id, name in pairs:
|
|
46
|
-
print(f' {field_id} {name}')
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def _run(parsed: argparse.Namespace) -> int:
|
|
50
|
-
"""Print the custom field map and, optionally, editable fields."""
|
|
51
|
-
try:
|
|
52
|
-
config = required_config(parsed)
|
|
53
|
-
connections = JiraConnections(config.get_jira_config(),
|
|
54
|
-
jira_passphrase)
|
|
55
|
-
customs = jira_custom_fields(connections, parsed.preset)
|
|
56
|
-
editable = (jira_editable_fields(connections, parsed.preset,
|
|
57
|
-
parsed.issue)
|
|
58
|
-
if parsed.issue is not None else None)
|
|
59
|
-
except (ValueError, TypeError, KeyError, OSError, JIRAError) as error:
|
|
60
|
-
print(f'Could not read Jira fields: {error}', file=sys.stderr)
|
|
61
|
-
return 1
|
|
62
|
-
_print_pairs(f"Custom fields for preset '{parsed.preset}':", customs)
|
|
63
|
-
if editable is not None:
|
|
64
|
-
print()
|
|
65
|
-
_print_pairs(f'Fields settable on the edit screen of '
|
|
66
|
-
f'{parsed.issue}:', editable)
|
|
67
|
-
return 0
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def main(args: Optional[list[str]] = None) -> int:
|
|
71
|
-
"""Print Jira field information for a preset.
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
args: Optional replacement for ``sys.argv[1:]``, mainly for tests.
|
|
75
|
-
|
|
76
|
-
Returns:
|
|
77
|
-
``0`` on success, ``1`` when the fields cannot be read.
|
|
78
|
-
"""
|
|
79
|
-
return _run(parsed_args(build_parser(), args))
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if __name__ == '__main__': # pragma: no cover
|
|
83
|
-
sys.exit(main())
|
|
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
|