backlogops-cli 1.2__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.
Files changed (46) hide show
  1. {backlogops_cli-1.2 → backlogops_cli-1.4}/PKG-INFO +48 -13
  2. {backlogops_cli-1.2 → backlogops_cli-1.4}/README_pypi.md +44 -9
  3. {backlogops_cli-1.2 → backlogops_cli-1.4}/backlogops_cli.egg-info/PKG-INFO +48 -13
  4. backlogops_cli-1.4/backlogops_cli.egg-info/requires.txt +5 -0
  5. {backlogops_cli-1.2 → backlogops_cli-1.4}/setup.py +4 -4
  6. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/_command_io.py +27 -6
  7. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/add_to_jira.py +4 -1
  8. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/encrypt_token_file.py +1 -1
  9. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/estimate_ready_date.py +39 -11
  10. backlogops_cli-1.4/src/backlogops_cli/jira_fields.py +135 -0
  11. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/preset_wizard.py +3 -2
  12. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/update_backlog_in_jira.py +43 -15
  13. backlogops_cli-1.2/backlogops_cli.egg-info/requires.txt +0 -5
  14. backlogops_cli-1.2/src/backlogops_cli/jira_fields.py +0 -83
  15. {backlogops_cli-1.2 → backlogops_cli-1.4}/LICENSE.txt +0 -0
  16. {backlogops_cli-1.2 → backlogops_cli-1.4}/backlogops_cli.egg-info/SOURCES.txt +0 -0
  17. {backlogops_cli-1.2 → backlogops_cli-1.4}/backlogops_cli.egg-info/dependency_links.txt +0 -0
  18. {backlogops_cli-1.2 → backlogops_cli-1.4}/backlogops_cli.egg-info/top_level.txt +0 -0
  19. {backlogops_cli-1.2 → backlogops_cli-1.4}/pyproject.toml +0 -0
  20. {backlogops_cli-1.2 → backlogops_cli-1.4}/setup.cfg +0 -0
  21. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/__init__.py +0 -0
  22. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/__main__.py +0 -0
  23. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/_migrate_warn.py +0 -0
  24. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/_wizard_io.py +0 -0
  25. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/add_releases_to_jira.py +0 -0
  26. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/adjust_release_content.py +0 -0
  27. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/bloc_version_reporter.py +0 -0
  28. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/config_edit.py +0 -0
  29. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/config_wizard.py +0 -0
  30. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/convert.py +0 -0
  31. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/demo_backlog.py +0 -0
  32. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/extract_keys.py +0 -0
  33. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/list.py +0 -0
  34. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/migrate_cfg.py +0 -0
  35. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/order_by_deps.py +0 -0
  36. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/order_by_keys.py +0 -0
  37. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/order_by_release.py +0 -0
  38. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/order_releases.py +0 -0
  39. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/order_releases_in_jira.py +0 -0
  40. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/plan_release_dates.py +0 -0
  41. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/py.typed +0 -0
  42. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/rank_in_jira.py +0 -0
  43. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/read_jira.py +0 -0
  44. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/rename_releases_in_jira.py +0 -0
  45. {backlogops_cli-1.2 → backlogops_cli-1.4}/src/backlogops_cli/update_releases_in_jira.py +0 -0
  46. {backlogops_cli-1.2 → 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.2
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.2
28
- Requires-Dist: wizard-ui-bridge[textual]>=1.3
29
- Requires-Dist: edit-cfg-json-textual>=0.2.0
27
+ Requires-Dist: backlogops>=1.4
28
+ Requires-Dist: wizard-ui-bridge[textual]>=1.4
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,14 @@ 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
+
83
+ - Configure what a backlog item that nobody has estimated counts as, per backlog
84
+ item level, so that an unestimated item does not make the estimated dates too
85
+ optimistic.
86
+
79
87
  - Extract backlog keys at given backlog item levels.
80
88
 
81
89
  - Reorder the backlog so that the dependencies are satisfied.
@@ -100,6 +108,9 @@ The following functionality is available in all 3 packages:
100
108
 
101
109
  - Rename columns when reading a file and when writing a file.
102
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
+
103
114
  - Map custom status names in input files to backlog item statuses.
104
115
 
105
116
  - Choose how backlog item levels are written: by number, by name, or both, and
@@ -110,7 +121,16 @@ The following functionality is available in all 3 packages:
110
121
  - Migrate an older configuration or preset file to the current file format.
111
122
 
112
123
  - A wizard to create a backlog-ops configuration file with the workforce, named
113
- input and output presets, level names, and status name mapping.
124
+ input and output presets, level names, status name mapping, the guess for
125
+ unestimated items, whether remaining time estimates are also used, how the
126
+ graphical user interface shows the data, and the Jira integration.
127
+
128
+ - An editor showing a whole configuration file or a stand-alone preset file at
129
+ once, folded where it is deep, to change a single value without stepping
130
+ through the wizard.
131
+
132
+ - Encrypt the Jira API token to a pass-phrase-protected file, so that no
133
+ configuration file holds the token in clear text.
114
134
 
115
135
  - Read a backlog and releases from Jira into a backlog and release table.
116
136
 
@@ -129,6 +149,14 @@ The following functionality is available in all 3 packages:
129
149
  - Move backlog items to a chosen anchor in the Jira rank order, following a key
130
150
  list.
131
151
 
152
+ - Report what a Jira operation did not do. Every Jira result opens with what
153
+ Jira refused or what was skipped, so a value that was not written is visible
154
+ without reading the whole listing.
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
+
132
160
  ## The operating model
133
161
 
134
162
  The operating model that most of the functionality is designed for is that the
@@ -137,8 +165,8 @@ are ordered by priority and dependencies to allow the teams to work in the
137
165
  backlog order. Each backlog item and each release may have a planned ready date,
138
166
  that records what has been communicated to the customer. Each backlog item and
139
167
  each release may have an estimated ready date, that is calculated from the
140
- current backlog state, the team velocity, and what we know about the
141
- 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.
142
170
 
143
171
  ## The backlog item fields
144
172
 
@@ -153,7 +181,10 @@ the library:
153
181
 
154
182
  - `title` The title of the backlog item. Required.
155
183
 
156
- - `story_points` The story points of the backlog item. Required.
184
+ - `story_points` The story points of the backlog item. Optional. An item with no
185
+ story points is one that nobody has estimated yet, and what such an item is
186
+ worked with when a completion date is estimated is configured. May be a
187
+ fraction, such as 0.5.
157
188
 
158
189
  - `status` The status of the backlog item. Required.
159
190
 
@@ -170,7 +201,11 @@ the library:
170
201
 
171
202
  - `team` The team responsible for the backlog item. Optional. Must not be empty
172
203
  string. Must be a valid team name. If None the item can be done by any team.
173
- If not None. the item can only be done by the specified team.
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.
174
209
 
175
210
  - `depends_on_f2s` The list of keys of the backlog items that must have been
176
211
  finished before the current item can start. May be empty.
@@ -210,7 +245,7 @@ pip install --upgrade backlogops-cli
210
245
 
211
246
  For a better understanding of how to use the library, CLI or GUI, see the user
212
247
  documentation:
213
- [Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc//using_backlogops/README.md)
248
+ [Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc/using_backlogops/README.md)
214
249
 
215
250
  ## API documentation
216
251
 
@@ -271,7 +306,7 @@ backlogops_cli serves 2 purposes:
271
306
  Extract backlog keys at the given levels to a key list
272
307
 
273
308
  python3 -m backlogops_cli.jira_fields
274
- Print Jira custom fields and an issue's editable fields
309
+ Print Jira custom fields, an issue's editable fields and JSON
275
310
 
276
311
  python3 -m backlogops_cli.list
277
312
  List all commands available in backlogops_cli
@@ -322,10 +357,10 @@ backlogops_cli serves 2 purposes:
322
357
 
323
358
  ## Test summary
324
359
 
325
- - Test result: 2309 passed, 1 deselected in 58s
360
+ - Test result: 2902 passed, 1 deselected in 68s (0:01:08)
326
361
  - No flake8 warnings.
327
362
  - No mypy errors found.
328
363
  - No pylint warnings.
329
364
  - No python layout warnings.
330
- - Built version(s): 1.2
365
+ - Built version(s): 1.4
331
366
  - Build and test using Python 3.14.7
@@ -41,6 +41,14 @@ 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
+
48
+ - Configure what a backlog item that nobody has estimated counts as, per backlog
49
+ item level, so that an unestimated item does not make the estimated dates too
50
+ optimistic.
51
+
44
52
  - Extract backlog keys at given backlog item levels.
45
53
 
46
54
  - Reorder the backlog so that the dependencies are satisfied.
@@ -65,6 +73,9 @@ The following functionality is available in all 3 packages:
65
73
 
66
74
  - Rename columns when reading a file and when writing a file.
67
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
+
68
79
  - Map custom status names in input files to backlog item statuses.
69
80
 
70
81
  - Choose how backlog item levels are written: by number, by name, or both, and
@@ -75,7 +86,16 @@ The following functionality is available in all 3 packages:
75
86
  - Migrate an older configuration or preset file to the current file format.
76
87
 
77
88
  - A wizard to create a backlog-ops configuration file with the workforce, named
78
- input and output presets, level names, and status name mapping.
89
+ input and output presets, level names, status name mapping, the guess for
90
+ unestimated items, whether remaining time estimates are also used, how the
91
+ graphical user interface shows the data, and the Jira integration.
92
+
93
+ - An editor showing a whole configuration file or a stand-alone preset file at
94
+ once, folded where it is deep, to change a single value without stepping
95
+ through the wizard.
96
+
97
+ - Encrypt the Jira API token to a pass-phrase-protected file, so that no
98
+ configuration file holds the token in clear text.
79
99
 
80
100
  - Read a backlog and releases from Jira into a backlog and release table.
81
101
 
@@ -94,6 +114,14 @@ The following functionality is available in all 3 packages:
94
114
  - Move backlog items to a chosen anchor in the Jira rank order, following a key
95
115
  list.
96
116
 
117
+ - Report what a Jira operation did not do. Every Jira result opens with what
118
+ Jira refused or what was skipped, so a value that was not written is visible
119
+ without reading the whole listing.
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
+
97
125
  ## The operating model
98
126
 
99
127
  The operating model that most of the functionality is designed for is that the
@@ -102,8 +130,8 @@ are ordered by priority and dependencies to allow the teams to work in the
102
130
  backlog order. Each backlog item and each release may have a planned ready date,
103
131
  that records what has been communicated to the customer. Each backlog item and
104
132
  each release may have an estimated ready date, that is calculated from the
105
- current backlog state, the team velocity, and what we know about the
106
- 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.
107
135
 
108
136
  ## The backlog item fields
109
137
 
@@ -118,7 +146,10 @@ the library:
118
146
 
119
147
  - `title` The title of the backlog item. Required.
120
148
 
121
- - `story_points` The story points of the backlog item. Required.
149
+ - `story_points` The story points of the backlog item. Optional. An item with no
150
+ story points is one that nobody has estimated yet, and what such an item is
151
+ worked with when a completion date is estimated is configured. May be a
152
+ fraction, such as 0.5.
122
153
 
123
154
  - `status` The status of the backlog item. Required.
124
155
 
@@ -135,7 +166,11 @@ the library:
135
166
 
136
167
  - `team` The team responsible for the backlog item. Optional. Must not be empty
137
168
  string. Must be a valid team name. If None the item can be done by any team.
138
- If not None. the item can only be done by the specified team.
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.
139
174
 
140
175
  - `depends_on_f2s` The list of keys of the backlog items that must have been
141
176
  finished before the current item can start. May be empty.
@@ -175,7 +210,7 @@ pip install --upgrade backlogops-cli
175
210
 
176
211
  For a better understanding of how to use the library, CLI or GUI, see the user
177
212
  documentation:
178
- [Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc//using_backlogops/README.md)
213
+ [Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc/using_backlogops/README.md)
179
214
 
180
215
  ## API documentation
181
216
 
@@ -236,7 +271,7 @@ backlogops_cli serves 2 purposes:
236
271
  Extract backlog keys at the given levels to a key list
237
272
 
238
273
  python3 -m backlogops_cli.jira_fields
239
- Print Jira custom fields and an issue's editable fields
274
+ Print Jira custom fields, an issue's editable fields and JSON
240
275
 
241
276
  python3 -m backlogops_cli.list
242
277
  List all commands available in backlogops_cli
@@ -287,10 +322,10 @@ backlogops_cli serves 2 purposes:
287
322
 
288
323
  ## Test summary
289
324
 
290
- - Test result: 2309 passed, 1 deselected in 58s
325
+ - Test result: 2902 passed, 1 deselected in 68s (0:01:08)
291
326
  - No flake8 warnings.
292
327
  - No mypy errors found.
293
328
  - No pylint warnings.
294
329
  - No python layout warnings.
295
- - Built version(s): 1.2
330
+ - Built version(s): 1.4
296
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.2
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.2
28
- Requires-Dist: wizard-ui-bridge[textual]>=1.3
29
- Requires-Dist: edit-cfg-json-textual>=0.2.0
27
+ Requires-Dist: backlogops>=1.4
28
+ Requires-Dist: wizard-ui-bridge[textual]>=1.4
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,14 @@ 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
+
83
+ - Configure what a backlog item that nobody has estimated counts as, per backlog
84
+ item level, so that an unestimated item does not make the estimated dates too
85
+ optimistic.
86
+
79
87
  - Extract backlog keys at given backlog item levels.
80
88
 
81
89
  - Reorder the backlog so that the dependencies are satisfied.
@@ -100,6 +108,9 @@ The following functionality is available in all 3 packages:
100
108
 
101
109
  - Rename columns when reading a file and when writing a file.
102
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
+
103
114
  - Map custom status names in input files to backlog item statuses.
104
115
 
105
116
  - Choose how backlog item levels are written: by number, by name, or both, and
@@ -110,7 +121,16 @@ The following functionality is available in all 3 packages:
110
121
  - Migrate an older configuration or preset file to the current file format.
111
122
 
112
123
  - A wizard to create a backlog-ops configuration file with the workforce, named
113
- input and output presets, level names, and status name mapping.
124
+ input and output presets, level names, status name mapping, the guess for
125
+ unestimated items, whether remaining time estimates are also used, how the
126
+ graphical user interface shows the data, and the Jira integration.
127
+
128
+ - An editor showing a whole configuration file or a stand-alone preset file at
129
+ once, folded where it is deep, to change a single value without stepping
130
+ through the wizard.
131
+
132
+ - Encrypt the Jira API token to a pass-phrase-protected file, so that no
133
+ configuration file holds the token in clear text.
114
134
 
115
135
  - Read a backlog and releases from Jira into a backlog and release table.
116
136
 
@@ -129,6 +149,14 @@ The following functionality is available in all 3 packages:
129
149
  - Move backlog items to a chosen anchor in the Jira rank order, following a key
130
150
  list.
131
151
 
152
+ - Report what a Jira operation did not do. Every Jira result opens with what
153
+ Jira refused or what was skipped, so a value that was not written is visible
154
+ without reading the whole listing.
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
+
132
160
  ## The operating model
133
161
 
134
162
  The operating model that most of the functionality is designed for is that the
@@ -137,8 +165,8 @@ are ordered by priority and dependencies to allow the teams to work in the
137
165
  backlog order. Each backlog item and each release may have a planned ready date,
138
166
  that records what has been communicated to the customer. Each backlog item and
139
167
  each release may have an estimated ready date, that is calculated from the
140
- current backlog state, the team velocity, and what we know about the
141
- 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.
142
170
 
143
171
  ## The backlog item fields
144
172
 
@@ -153,7 +181,10 @@ the library:
153
181
 
154
182
  - `title` The title of the backlog item. Required.
155
183
 
156
- - `story_points` The story points of the backlog item. Required.
184
+ - `story_points` The story points of the backlog item. Optional. An item with no
185
+ story points is one that nobody has estimated yet, and what such an item is
186
+ worked with when a completion date is estimated is configured. May be a
187
+ fraction, such as 0.5.
157
188
 
158
189
  - `status` The status of the backlog item. Required.
159
190
 
@@ -170,7 +201,11 @@ the library:
170
201
 
171
202
  - `team` The team responsible for the backlog item. Optional. Must not be empty
172
203
  string. Must be a valid team name. If None the item can be done by any team.
173
- If not None. the item can only be done by the specified team.
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.
174
209
 
175
210
  - `depends_on_f2s` The list of keys of the backlog items that must have been
176
211
  finished before the current item can start. May be empty.
@@ -210,7 +245,7 @@ pip install --upgrade backlogops-cli
210
245
 
211
246
  For a better understanding of how to use the library, CLI or GUI, see the user
212
247
  documentation:
213
- [Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc//using_backlogops/README.md)
248
+ [Using backlogops](https://github.com/tom-bjorkholm/backlog-ops/blob/master/doc/using_backlogops/README.md)
214
249
 
215
250
  ## API documentation
216
251
 
@@ -271,7 +306,7 @@ backlogops_cli serves 2 purposes:
271
306
  Extract backlog keys at the given levels to a key list
272
307
 
273
308
  python3 -m backlogops_cli.jira_fields
274
- Print Jira custom fields and an issue's editable fields
309
+ Print Jira custom fields, an issue's editable fields and JSON
275
310
 
276
311
  python3 -m backlogops_cli.list
277
312
  List all commands available in backlogops_cli
@@ -322,10 +357,10 @@ backlogops_cli serves 2 purposes:
322
357
 
323
358
  ## Test summary
324
359
 
325
- - Test result: 2309 passed, 1 deselected in 58s
360
+ - Test result: 2902 passed, 1 deselected in 68s (0:01:08)
326
361
  - No flake8 warnings.
327
362
  - No mypy errors found.
328
363
  - No pylint warnings.
329
364
  - No python layout warnings.
330
- - Built version(s): 1.2
365
+ - Built version(s): 1.4
331
366
  - Build and test using Python 3.14.7
@@ -0,0 +1,5 @@
1
+ argcomplete>=3.7.2
2
+ backlogops>=1.4
3
+ wizard-ui-bridge[textual]>=1.4
4
+ edit-cfg-json-textual>=0.4.0
5
+ versionreporter>=0.4
@@ -5,7 +5,7 @@ from setuptools import setup
5
5
 
6
6
  setup(
7
7
  name='backlogops-cli',
8
- version='1.2',
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.2',
19
- 'wizard-ui-bridge[textual] >= 1.3',
20
- 'edit-cfg-json-textual >= 0.2.0',
18
+ 'backlogops >= 1.4',
19
+ 'wizard-ui-bridge[textual] >= 1.4',
20
+ 'edit-cfg-json-textual >= 0.4.0',
21
21
  'versionreporter >= 0.4'
22
22
  ]
23
23
  )
@@ -145,12 +145,16 @@ def add_config_arg(parser: argparse.ArgumentParser) -> None:
145
145
  """Add the ``-c``/``--config`` backlog-ops configuration argument.
146
146
 
147
147
  The configuration file holds the workforce, the named input and output
148
- presets, the levels and the global status map. Without ``-c`` the file
149
- is discovered the same way as the GUI.
148
+ presets, the levels, the global status map, the guess for unestimated
149
+ items, the remaining time settings, the GUI display settings and the
150
+ Jira configuration. Without
151
+ ``-c`` the file is discovered the same way as the GUI.
150
152
  """
151
153
  parser.add_argument('-c', '--config', dest='config',
152
154
  help='Backlog-ops configuration file (workforce, '
153
- 'named presets, levels, status map). Without -c the '
155
+ 'named presets, levels, status map, default story '
156
+ 'points, remaining time settings, Jira '
157
+ 'configuration). Without -c the '
154
158
  'file is found from $BACKLOGOPS_CFG, else '
155
159
  'backlogops.cfg in $BACKLOGOPS_DIR, else '
156
160
  '$HOME/.backlogops.cfg.')
@@ -231,6 +235,20 @@ def io_levels(config: Optional[BacklogOpsConfig]) -> Optional[Levels]:
231
235
  return config.get_levels() if config is not None else None
232
236
 
233
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
+
234
252
  def read_input(parsed: argparse.Namespace,
235
253
  config: Optional[BacklogOpsConfig]) -> BacklogReleases:
236
254
  """Read and validate the backlog and releases from the input file.
@@ -343,7 +361,8 @@ def _write_output(parsed: argparse.Namespace,
343
361
  write_backlog_releases(data, parsed.output, out_config, rules,
344
362
  levels=io_levels(config),
345
363
  file_exists_callback=overwrite_callback(
346
- parsed.force))
364
+ parsed.force),
365
+ use_remaining_time=io_remaining_time(config))
347
366
 
348
367
 
349
368
  def run_write(parsed: argparse.Namespace,
@@ -405,7 +424,8 @@ def write_result_file(config: BacklogOpsConfig, path: str,
405
424
  auto_ch_hook=CliPresetMigrateWarnHook())
406
425
  write_backlog_releases(data, path, out_config, FormatRules(),
407
426
  levels=config.get_levels(),
408
- file_exists_callback=overwrite_callback(force))
427
+ file_exists_callback=overwrite_callback(force),
428
+ use_remaining_time=io_remaining_time(config))
409
429
  print(f'Wrote {path}')
410
430
 
411
431
 
@@ -418,7 +438,8 @@ def run_added_to_jira(
418
438
  """Read the input, add it to Jira, and print the added lists.
419
439
 
420
440
  ``add`` receives the parsed args, the config and the input data, adds
421
- to Jira, writes any ``--added``/``--existing`` files, and returns the
441
+ to Jira, writes any ``--added-file``/``--existing-file`` files, and
442
+ returns the
422
443
  result. ``report`` formats that result for stdout, printed unless
423
444
  ``-q``/``--quiet`` was given. ``exists_error`` is the key-exists error
424
445
  meaning nothing was added; other value or OS errors are reported with
@@ -66,10 +66,13 @@ 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; '
75
+ f'{len(result.failed_fields)} fields not set; '
73
76
  f'{len(result.failed_links)} links not written.', file=sys.stderr)
74
77
  return result
75
78
 
@@ -4,7 +4,7 @@
4
4
  Read a clear text API token from a file or from standard input, encrypt it
5
5
  with a pass phrase and write the encrypted token to a file. The pass phrase
6
6
  is requested on the terminal and is not echoed. The clear text token file
7
- is not modified, unless the the encrypted token file is the same as the
7
+ is not modified, unless the encrypted token file is the same as the
8
8
  clear text token file, in which case the clear text token file is overwritten
9
9
  with the encrypted token. The encrypted token file is written atomically,
10
10
  so that it is either the old version or the new version, and never a
@@ -6,10 +6,17 @@ estimates the ready date of each backlog item from the available teams,
6
6
  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
- configured backlog-ops file. The backlog with the estimated dates and the
10
- releases are written to the output file. The input and output formats are
11
- inferred from the file name extensions, but can be overridden by a
12
- configuration file or by a named preset.
9
+ configured backlog-ops file, and so is what a backlog item that nobody
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.
13
20
  """
14
21
 
15
22
  # PYTHON_ARGCOMPLETE_OK
@@ -35,6 +42,16 @@ def build_parser() -> argparse.ArgumentParser:
35
42
  parser.add_argument('-d', '--start-date', dest='start_date',
36
43
  metavar='ISO_DATE',
37
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.')
38
55
  parser.add_argument('--set-plan', dest='set_plan', action='store_true',
39
56
  help='Also copy each estimated date to the planned '
40
57
  'date.')
@@ -54,12 +71,20 @@ def _estimate(parsed: argparse.Namespace, config: Optional[BacklogOpsConfig],
54
71
  ) -> tuple[str, Optional[Callable[[str], None]]]:
55
72
  """Estimate the dates and return the release date change report.
56
73
 
57
- The configuration is required for this command, so ``config`` is never
58
- None here; the assertion makes that explicit for the type checker.
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.
59
78
  """
60
79
  assert isinstance(config, BacklogOpsConfig)
61
- changes = data.estimate_ready_date(config.available_teams,
62
- _start_date(parsed))
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)
63
88
  if parsed.set_plan:
64
89
  data.set_plan_from_estimate()
65
90
  return date_report(changes, overwrite_callback(parsed.force))
@@ -69,9 +94,12 @@ def main(args: Optional[list[str]] = None) -> int:
69
94
  """Estimate the ready dates and write the output file.
70
95
 
71
96
  The backlog with the estimated dates and the releases are written to
72
- the output file. The estimated release dates are updated as well, and
73
- the list of release date changes is printed to stdout, or also saved
74
- to a file when ``--changes-file`` is given.
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.
75
103
 
76
104
  Args:
77
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 for an output
6
- preset). Such a stand-alone file is used wherever an input or output
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. ``--links`` chooses how
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
@@ -20,8 +22,9 @@ selected fields are updated the same way under either value. With ``--rank``
20
22
  the items are also ranked in Jira to match the backlog order, at the chosen
21
23
  anchor.
22
24
 
23
- The updated, already-correct, ignored, added and failed items are printed
24
- to stdout as labelled lists, unless ``-q``/``--quiet`` is given. An
25
+ The updated, already-correct, ignored and added items, and what Jira
26
+ refused to add, to set or to link, are printed to stdout as labelled
27
+ lists, unless ``-q``/``--quiet`` is given. An
25
28
  encrypted Jira token is unlocked by a pass phrase asked on the terminal
26
29
  only when it is needed.
27
30
  """
@@ -48,6 +51,9 @@ _MISSING_MODES = {'raise': OnMissingKey.RAISE, 'ignore': OnMissingKey.IGNORE,
48
51
  _LINK_MODES = {'reconcile': LinkUpdate.RECONCILE,
49
52
  'add': LinkUpdate.ADD_MISSING}
50
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."""
51
57
 
52
58
 
53
59
  def build_parser() -> argparse.ArgumentParser:
@@ -75,40 +81,57 @@ def _add_column_flags(parser: argparse.ArgumentParser) -> None:
75
81
  group.add_argument('-s', '--store', dest='store', nargs='+',
76
82
  metavar='COLUMN',
77
83
  help='Columns to update, or the single word "all" for '
78
- 'every mapped writable column.')
84
+ 'every mapped writable column. ' + _RT_WRITABLE)
79
85
  group.add_argument('-e', '--exclude', dest='exclude', nargs='+',
80
86
  metavar='COLUMN',
81
- help='Update every mapped writable column but these.')
87
+ help='Update every mapped writable column but these. '
88
+ + _RT_WRITABLE)
82
89
 
83
90
 
84
- def _resolve_fields(parsed: argparse.Namespace,
85
- connections: JiraConnections) -> list[str]:
91
+ def _resolve_fields(parsed: argparse.Namespace, connections: JiraConnections,
92
+ use_remaining_time: bool) -> list[str]:
86
93
  """Return the internal field names to update from the -s/-e flags.
87
94
 
88
95
  ``-s all`` and ``-e`` are resolved against the preset's updatable
89
96
  columns. A ``-s`` name that is not an updatable column is reported and
90
- 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.
91
100
  """
92
- updatable = updatable_backlog_fields(connections, parsed.preset)
101
+ updatable = updatable_backlog_fields(connections, parsed.preset,
102
+ use_remaining_time=use_remaining_time)
93
103
  if parsed.store is not None:
94
104
  if parsed.store == [_STORE_ALL]:
95
105
  return updatable
96
106
  unknown = [name for name in parsed.store if name not in updatable]
97
107
  if unknown:
98
- print('Ignoring columns not updatable in this preset: '
99
- + ', '.join(unknown), file=sys.stderr)
108
+ _report_ignored(unknown, use_remaining_time)
100
109
  return [name for name in parsed.store if name in updatable]
101
110
  excluded = set(parsed.exclude)
102
111
  return [name for name in updatable if name not in excluded]
103
112
 
104
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
+
105
127
  def _update(parsed: argparse.Namespace, config: BacklogOpsConfig,
106
128
  data_backlog: object) -> UpdatedBacklogInJira:
107
129
  """Update the input backlog in Jira using the named preset."""
108
130
  print(f"Updating backlog in Jira using preset '{parsed.preset}'...",
109
131
  file=sys.stderr)
110
132
  connections = JiraConnections(config.get_jira_config(), jira_passphrase)
111
- fields = _resolve_fields(parsed, connections)
133
+ use_rt = config.remaining_time.enable_remaining_time
134
+ fields = _resolve_fields(parsed, connections, use_rt)
112
135
  mode = _MISSING_MODES[parsed.on_missing]
113
136
  link_update = _LINK_MODES[parsed.links]
114
137
  assert isinstance(data_backlog, list)
@@ -118,7 +141,8 @@ def _update(parsed: argparse.Namespace, config: BacklogOpsConfig,
118
141
  link_update=link_update,
119
142
  rank_anchor=rank_anchor(parsed.rank),
120
143
  levels=config.get_levels(),
121
- status_map=config.get_status_input_map())
144
+ status_map=config.get_status_input_map(),
145
+ use_remaining_time=use_rt)
122
146
  _report_summary(result)
123
147
  return result
124
148
 
@@ -128,7 +152,11 @@ def _report_summary(result: UpdatedBacklogInJira) -> None:
128
152
  print(f'Updated {len(result.updated)} items in Jira; '
129
153
  f'{len(result.already_correct)} already correct; '
130
154
  f'{len(result.ignored)} ignored; {len(result.added.stored)} added; '
131
- f'{len(result.failed)} failed.', file=sys.stderr)
155
+ f'{len(result.added.failed)} not added; '
156
+ f'{len(result.failed_fields) + len(result.added.failed_fields)} '
157
+ 'fields not set; '
158
+ f'{len(result.failed_links) + len(result.added.failed_links)} '
159
+ 'links not written.', file=sys.stderr)
132
160
 
133
161
 
134
162
  def _run(parsed: argparse.Namespace) -> int:
@@ -1,5 +0,0 @@
1
- argcomplete>=3.7.2
2
- backlogops>=1.2
3
- wizard-ui-bridge[textual]>=1.3
4
- edit-cfg-json-textual>=0.2.0
5
- versionreporter>=0.4
@@ -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