Status: | Merged |
---|---|
Approved by: | David Britton |
Approved revision: | 178 |
Merged at revision: | 146 |
Proposed branch: | lp:~dpb/lp2kanban/lint-1 |
Merge into: | lp:lp2kanban |
Prerequisite: | lp:~dpb/lp2kanban/feature-hoover-just-the-facts |
Diff against target: |
1106 lines (+194/-165) 8 files modified
Makefile (+4/-1) src/lp2kanban/bugs2cards.py (+23/-20) src/lp2kanban/cards2workitems.py (+6/-6) src/lp2kanban/kanban.py (+38/-41) src/lp2kanban/tests/test_bugs2cards.py (+94/-72) src/lp2kanban/tests/test_cards2workitems.py (+13/-10) src/lp2kanban/tests/test_description_annotations.py (+6/-5) src/lp2kanban/workitems2cards.py (+10/-10) |
To merge this branch: | bzr merge lp:~dpb/lp2kanban/lint-1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alberto Donato (community) | Approve | ||
🤖 Landscape Builder | test results | Approve | |
Review via email: mp+305280@code.launchpad.net |
Commit message
Add lint target (flake8), remove lint.
Description of the change
Add lint target (flake8), remove lint.
make lint should pass
make ci-test includes lint check
To post a comment you must log in.
Revision history for this message
🤖 Landscape Builder (landscape-builder) : | # |
review:
Abstain
(executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote : | # |
review:
Approve
(test results)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Makefile' | |||
2 | --- Makefile 2016-08-29 20:19:31 +0000 | |||
3 | +++ Makefile 2016-09-08 22:02:56 +0000 | |||
4 | @@ -61,4 +61,7 @@ | |||
5 | 61 | . venv/bin/activate; \ | 61 | . venv/bin/activate; \ |
6 | 62 | $(MAKE) check | 62 | $(MAKE) check |
7 | 63 | 63 | ||
9 | 64 | .PHONY: check default configs needs-xdg-utils clean ci-test | 64 | lint: |
10 | 65 | flake8 src/lp2kanban | ||
11 | 66 | |||
12 | 67 | .PHONY: check default configs needs-xdg-utils clean ci-test lint | ||
13 | 65 | 68 | ||
14 | === modified file 'src/lp2kanban/bugs2cards.py' | |||
15 | --- src/lp2kanban/bugs2cards.py 2016-09-08 22:02:56 +0000 | |||
16 | +++ src/lp2kanban/bugs2cards.py 2016-09-08 22:02:56 +0000 | |||
17 | @@ -141,7 +141,7 @@ | |||
18 | 141 | self.lp_to_group.update(parse_groups_config(groups_config)) | 141 | self.lp_to_group.update(parse_groups_config(groups_config)) |
19 | 142 | 142 | ||
20 | 143 | self.feature_lanes = {} | 143 | self.feature_lanes = {} |
22 | 144 | new_lane = config.get("new_lanes") | 144 | new_lane = config.get("new_lanes") |
23 | 145 | if not new_lane: | 145 | if not new_lane: |
24 | 146 | return | 146 | return |
25 | 147 | if "${group}" in new_lane: | 147 | if "${group}" in new_lane: |
26 | @@ -149,7 +149,7 @@ | |||
27 | 149 | for group in set(self.lp_to_group.values()): | 149 | for group in set(self.lp_to_group.values()): |
28 | 150 | lane_path = new_lane.replace("${group}", group) | 150 | lane_path = new_lane.replace("${group}", group) |
29 | 151 | lane = board.getLaneByPath(lane_path) | 151 | lane = board.getLaneByPath(lane_path) |
31 | 152 | group_tag = "squad-%s" % group.lower().replace(" ","-") | 152 | group_tag = "squad-%s" % group.lower().replace(" ", "-") |
32 | 153 | self.feature_lanes[group_tag] = lane | 153 | self.feature_lanes[group_tag] = lane |
33 | 154 | for card in board.getFeatureCards(): | 154 | for card in board.getFeatureCards(): |
34 | 155 | if not card.tags: | 155 | if not card.tags: |
35 | @@ -192,6 +192,7 @@ | |||
36 | 192 | IGNORE_STATUSES = ('Superseded',) | 192 | IGNORE_STATUSES = ('Superseded',) |
37 | 193 | FINAL_STATUSES = ('Approved', 'Merged') | 193 | FINAL_STATUSES = ('Approved', 'Merged') |
38 | 194 | 194 | ||
39 | 195 | |||
40 | 195 | def get_lp_bug(card, launchpad): | 196 | def get_lp_bug(card, launchpad): |
41 | 196 | """Return the launchpad bug resource for a card if that bug exists. | 197 | """Return the launchpad bug resource for a card if that bug exists. |
42 | 197 | 198 | ||
43 | @@ -273,7 +274,7 @@ | |||
44 | 273 | 274 | ||
45 | 274 | for card in feature_card.lane.board.cards: | 275 | for card in feature_card.lane.board.cards: |
46 | 275 | if card_type and card.type.name != card_type: | 276 | if card_type and card.type.name != card_type: |
48 | 276 | continue | 277 | continue |
49 | 277 | if card.tags: | 278 | if card.tags: |
50 | 278 | card_tags = set(card.tags.split(",")) | 279 | card_tags = set(card.tags.split(",")) |
51 | 279 | if feature_tags.intersection(card_tags): | 280 | if feature_tags.intersection(card_tags): |
52 | @@ -304,7 +305,7 @@ | |||
53 | 304 | mps = [] | 305 | mps = [] |
54 | 305 | has_inprogress_branches = False | 306 | has_inprogress_branches = False |
55 | 306 | for branch in branches: | 307 | for branch in branches: |
57 | 307 | if hasattr(branch, 'branch') : | 308 | if hasattr(branch, 'branch'): |
58 | 308 | branch = branch.branch | 309 | branch = branch.branch |
59 | 309 | for mp in branch.landing_targets: | 310 | for mp in branch.landing_targets: |
60 | 310 | mp_info = Record(rank=None, status=None, mp=None) | 311 | mp_info = Record(rank=None, status=None, mp=None) |
61 | @@ -319,7 +320,7 @@ | |||
62 | 319 | mps.append(mp_info) | 320 | mps.append(mp_info) |
63 | 320 | if len(mps) == 0: | 321 | if len(mps) == 0: |
64 | 321 | return None | 322 | return None |
66 | 322 | mps.sort(key=lambda x:(x.rank, x.mp.date_created), reverse=True) | 323 | mps.sort(key=lambda x: (x.rank, x.mp.date_created), reverse=True) |
67 | 323 | if not has_inprogress_branches: | 324 | if not has_inprogress_branches: |
68 | 324 | return mps[0] # Return highest-ranked and most-recent MP | 325 | return mps[0] # Return highest-ranked and most-recent MP |
69 | 325 | for mp in mps: | 326 | for mp in mps: |
70 | @@ -395,7 +396,7 @@ | |||
71 | 395 | return False | 396 | return False |
72 | 396 | if card.tags and "no-sync" in set(card.tags.split(",")): | 397 | if card.tags and "no-sync" in set(card.tags.split(",")): |
73 | 397 | return False | 398 | return False |
75 | 398 | 399 | ||
76 | 399 | enabled = conf.get('move_cards', 'off') == 'on' | 400 | enabled = conf.get('move_cards', 'off') == 'on' |
77 | 400 | active = not card.lane.board.is_archived | 401 | active = not card.lane.board.is_archived |
78 | 401 | return enabled and active | 402 | return enabled and active |
79 | @@ -498,7 +499,7 @@ | |||
80 | 498 | status = CardStatus.LANDING | 499 | status = CardStatus.LANDING |
81 | 499 | elif branch_card_status: | 500 | elif branch_card_status: |
82 | 500 | return branch_card_status | 501 | return branch_card_status |
84 | 501 | 502 | ||
85 | 502 | elif bug_status in DONE_FIX_BUG_STATUSES: | 503 | elif bug_status in DONE_FIX_BUG_STATUSES: |
86 | 503 | status = CardStatus.DONE_FIX | 504 | status = CardStatus.DONE_FIX |
87 | 504 | elif bug_status in DONE_NOFIX_BUG_STATUSES: | 505 | elif bug_status in DONE_NOFIX_BUG_STATUSES: |
88 | @@ -512,7 +513,7 @@ | |||
89 | 512 | if task.target in all_projects: | 513 | if task.target in all_projects: |
90 | 513 | return True | 514 | return True |
91 | 514 | if (hasattr(task.target, "project") and | 515 | if (hasattr(task.target, "project") and |
93 | 515 | task.target.project in all_projects): | 516 | task.target.project in all_projects): |
94 | 516 | return True | 517 | return True |
95 | 517 | return False | 518 | return False |
96 | 518 | 519 | ||
97 | @@ -525,13 +526,13 @@ | |||
98 | 525 | for task in tasks: | 526 | for task in tasks: |
99 | 526 | # Ignore bug task status for series targets | 527 | # Ignore bug task status for series targets |
100 | 527 | if (task.target.resource_type_link == RESOURCE_TYPE_LINK_SERIES and | 528 | if (task.target.resource_type_link == RESOURCE_TYPE_LINK_SERIES and |
102 | 528 | task.target.project in all_projects): | 529 | task.target.project in all_projects): |
103 | 529 | continue # Skip series task status for our projects | 530 | continue # Skip series task status for our projects |
104 | 530 | if (task.assignee is not None and | 531 | if (task.assignee is not None and |
106 | 531 | task.assignee.name not in all_users): | 532 | task.assignee.name not in all_users): |
107 | 532 | continue # Skip non-users tasks | 533 | continue # Skip non-users tasks |
108 | 533 | if (task.target in all_projects or | 534 | if (task.target in all_projects or |
110 | 534 | task.assignee is not None): | 535 | task.assignee is not None): |
111 | 535 | matches = True | 536 | matches = True |
112 | 536 | if (lowest_status is None or | 537 | if (lowest_status is None or |
113 | 537 | (ORDERED_BUG_STATUSES.index(task.status) < | 538 | (ORDERED_BUG_STATUSES.index(task.status) < |
114 | @@ -555,7 +556,6 @@ | |||
115 | 555 | return possible_target_lane | 556 | return possible_target_lane |
116 | 556 | 557 | ||
117 | 557 | 558 | ||
118 | 558 | |||
119 | 559 | def find_card_target_lane_next(lane, path): | 559 | def find_card_target_lane_next(lane, path): |
120 | 560 | """Find the target lane for the path using the "next" algorithm. | 560 | """Find the target lane for the path using the "next" algorithm. |
121 | 561 | 561 | ||
122 | @@ -723,7 +723,7 @@ | |||
123 | 723 | assignee = Record(name=owner_name) | 723 | assignee = Record(name=owner_name) |
124 | 724 | try: | 724 | try: |
125 | 725 | move_card(card, card_status, bconf, [assignee], lp_users) | 725 | move_card(card, card_status, bconf, [assignee], lp_users) |
127 | 726 | except IOError as e: | 726 | except IOError: |
128 | 727 | log.exception("Error moving card: %s (in %s)" % ( | 727 | log.exception("Error moving card: %s (in %s)" % ( |
129 | 728 | card.title, card_path)) | 728 | card.title, card_path)) |
130 | 729 | continue | 729 | continue |
131 | @@ -751,7 +751,8 @@ | |||
132 | 751 | continue | 751 | continue |
133 | 752 | ignore_external_projects = ( | 752 | ignore_external_projects = ( |
134 | 753 | bconf.get("autosync") == "active-projects") | 753 | bconf.get("autosync") == "active-projects") |
136 | 754 | if ignore_external_projects and not is_bug_in_projects(lp_bug, lp_projects): | 754 | if ignore_external_projects and not is_bug_in_projects( |
137 | 755 | lp_bug, lp_projects): | ||
138 | 755 | continue # Skip bugs in external projects | 756 | continue # Skip bugs in external projects |
139 | 756 | synced, card_status, assignees, mp_url, mp_type = get_bug_status( | 757 | synced, card_status, assignees, mp_url, mp_type = get_bug_status( |
140 | 757 | lp_bug, lp_projects, lp_users.lp_to_kanban.keys()) | 758 | lp_bug, lp_projects, lp_users.lp_to_kanban.keys()) |
141 | @@ -766,7 +767,7 @@ | |||
142 | 766 | if card_status is not None and should_move_card(card, bconf): | 767 | if card_status is not None and should_move_card(card, bconf): |
143 | 767 | try: | 768 | try: |
144 | 768 | move_card(card, card_status, bconf, assignees, lp_users) | 769 | move_card(card, card_status, bconf, assignees, lp_users) |
146 | 769 | except IOError as e: | 770 | except IOError: |
147 | 770 | log.exception("Error Moving: lp:%s: %s (in %s)" % ( | 771 | log.exception("Error Moving: lp:%s: %s (in %s)" % ( |
148 | 771 | lp_bug.id, card.title, card_path)) | 772 | lp_bug.id, card.title, card_path)) |
149 | 772 | continue | 773 | continue |
150 | @@ -878,7 +879,8 @@ | |||
151 | 878 | return | 879 | return |
152 | 879 | 880 | ||
153 | 880 | log.info( | 881 | log.info( |
155 | 881 | u" * Moving card#{}: {} -> {}".format(card.id, card.lane.path, target_lane_path)) | 882 | u" * Moving card#{}: {} -> {}".format( |
156 | 883 | card.id, card.lane.path, target_lane_path)) | ||
157 | 882 | card.move(target_lane) | 884 | card.move(target_lane) |
158 | 883 | 885 | ||
159 | 884 | 886 | ||
160 | @@ -896,8 +898,8 @@ | |||
161 | 896 | # happens. | 898 | # happens. |
162 | 897 | try: | 899 | try: |
163 | 898 | card.save() | 900 | card.save() |
166 | 899 | except IOError as e: | 901 | except IOError: |
167 | 900 | log.exception(u"Exception caught. Skipping board.\n" | 902 | log.exception("Exception caught. Skipping board.\n" |
168 | 901 | " desc:{}\n" | 903 | " desc:{}\n" |
169 | 902 | " kanban id:{}\n" | 904 | " kanban id:{}\n" |
170 | 903 | " LP bug id:{}\n".format( | 905 | " LP bug id:{}\n".format( |
171 | @@ -937,7 +939,8 @@ | |||
172 | 937 | 939 | ||
173 | 938 | if args.debug: | 940 | if args.debug: |
174 | 939 | logging.basicConfig( | 941 | logging.basicConfig( |
176 | 940 | level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s') | 942 | level=logging.DEBUG, |
177 | 943 | format='%(asctime)s %(levelname)s %(message)s') | ||
178 | 941 | import httplib | 944 | import httplib |
179 | 942 | httplib.HTTPConnection.debuglevel = 1 | 945 | httplib.HTTPConnection.debuglevel = 1 |
180 | 943 | else: | 946 | else: |
181 | @@ -975,7 +978,7 @@ | |||
182 | 975 | continue | 978 | continue |
183 | 976 | bconf = boards_config[board_name] | 979 | bconf = boards_config[board_name] |
184 | 977 | process_board(board, bconf, args.bug, args.card) | 980 | process_board(board, bconf, args.bug, args.card) |
186 | 978 | except (KeyError, AssertionError) as e: | 981 | except (KeyError, AssertionError): |
187 | 979 | log.exception("Exception caught. Skipping board.") | 982 | log.exception("Exception caught. Skipping board.") |
188 | 980 | continue | 983 | continue |
189 | 981 | 984 | ||
190 | 982 | 985 | ||
191 | === modified file 'src/lp2kanban/cards2workitems.py' | |||
192 | --- src/lp2kanban/cards2workitems.py 2013-06-19 19:44:20 +0000 | |||
193 | +++ src/lp2kanban/cards2workitems.py 2016-09-08 22:02:56 +0000 | |||
194 | @@ -13,9 +13,9 @@ | |||
195 | 13 | # support POSTPONED work items in this mapping, since the Kanban board | 13 | # support POSTPONED work items in this mapping, since the Kanban board |
196 | 14 | # has no such concept. | 14 | # has no such concept. |
197 | 15 | LANE_TYPE_TO_WORK_ITEM_STATUS_MAP = { | 15 | LANE_TYPE_TO_WORK_ITEM_STATUS_MAP = { |
201 | 16 | 1: u"TODO", # Kanban type "Ready" | 16 | 1: u"TODO", # Kanban type "Ready" |
202 | 17 | 2: u"INPROGRESS", # Kanban type "In process" | 17 | 2: u"INPROGRESS", # Kanban type "In process" |
203 | 18 | 3: u"DONE", # Kanban type "Complete" | 18 | 3: u"DONE", # Kanban type "Complete" |
204 | 19 | } | 19 | } |
205 | 20 | 20 | ||
206 | 21 | 21 | ||
207 | @@ -34,7 +34,7 @@ | |||
208 | 34 | card.description_annotations.get('blueprint_url') or | 34 | card.description_annotations.get('blueprint_url') or |
209 | 35 | card.external_system_url) | 35 | card.external_system_url) |
210 | 36 | if (blueprint_url is None or not | 36 | if (blueprint_url is None or not |
212 | 37 | blueprint_url_pattern.match(blueprint_url)): | 37 | blueprint_url_pattern.match(blueprint_url)): |
213 | 38 | # Return early if the card isn't linked to a blueprint, since | 38 | # Return early if the card isn't linked to a blueprint, since |
214 | 39 | # there's no point doing the conversion if we're not going to | 39 | # there's no point doing the conversion if we're not going to |
215 | 40 | # sync it. | 40 | # sync it. |
216 | @@ -118,8 +118,8 @@ | |||
217 | 118 | work_items_text = "\n".join(work_items) | 118 | work_items_text = "\n".join(work_items) |
218 | 119 | blueprint = fetch_blueprint(lp, blueprint_url) | 119 | blueprint = fetch_blueprint(lp, blueprint_url) |
219 | 120 | if blueprint is not None: | 120 | if blueprint is not None: |
222 | 121 | print "Adding %s work items to %s" % ( | 121 | print("Adding %s work items to %s" % ( |
223 | 122 | len(work_items), blueprint.name) | 122 | len(work_items), blueprint.name)) |
224 | 123 | blueprint.workitems_text = work_items_text | 123 | blueprint.workitems_text = work_items_text |
225 | 124 | blueprint.lp_save() | 124 | blueprint.lp_save() |
226 | 125 | 125 | ||
227 | 126 | 126 | ||
228 | === modified file 'src/lp2kanban/kanban.py' | |||
229 | --- src/lp2kanban/kanban.py 2016-09-08 22:02:56 +0000 | |||
230 | +++ src/lp2kanban/kanban.py 2016-09-08 22:02:56 +0000 | |||
231 | @@ -3,7 +3,6 @@ | |||
232 | 3 | 3 | ||
233 | 4 | import requests | 4 | import requests |
234 | 5 | import json | 5 | import json |
235 | 6 | import logging | ||
236 | 7 | import operator | 6 | import operator |
237 | 8 | from pprint import pprint | 7 | from pprint import pprint |
238 | 9 | import re | 8 | import re |
239 | @@ -112,14 +111,14 @@ | |||
240 | 112 | 111 | ||
241 | 113 | resp = request.response | 112 | resp = request.response |
242 | 114 | if (not sent or | 113 | if (not sent or |
245 | 115 | resp.status_code not in LeankitResponseCodes.SUCCESS_CODES): | 114 | resp.status_code not in LeankitResponseCodes.SUCCESS_CODES): |
246 | 116 | print "Error from kanban" | 115 | print("Error from kanban") |
247 | 117 | pprint(resp) | 116 | pprint(resp) |
248 | 118 | raise IOError('kanban error %d' % (resp.status_code)) | 117 | raise IOError('kanban error %d' % (resp.status_code)) |
249 | 119 | response = Record(json.loads(resp.content)) | 118 | response = Record(json.loads(resp.content)) |
250 | 120 | 119 | ||
251 | 121 | if (handle_errors and | 120 | if (handle_errors and |
253 | 122 | response.ReplyCode not in LeankitResponseCodes.SUCCESS_CODES): | 121 | response.ReplyCode not in LeankitResponseCodes.SUCCESS_CODES): |
254 | 123 | raise IOError('kanban error %d: %s' % ( | 122 | raise IOError('kanban error %d: %s' % ( |
255 | 124 | response.ReplyCode, response.ReplyText)) | 123 | response.ReplyCode, response.ReplyText)) |
256 | 125 | return response | 124 | return response |
257 | @@ -166,7 +165,7 @@ | |||
258 | 166 | def _prettifyName(self, camelcase): | 165 | def _prettifyName(self, camelcase): |
259 | 167 | camelcase = camelcase.replace('ID', '_id') | 166 | camelcase = camelcase.replace('ID', '_id') |
260 | 168 | if len(camelcase) > 1: | 167 | if len(camelcase) > 1: |
262 | 169 | repl_func = lambda match: '_' + match.group(1).lower() | 168 | def repl_func(match): return '_' + match.group(1).lower() |
263 | 170 | camelcase = camelcase[0].lower() + camelcase[1:] | 169 | camelcase = camelcase[0].lower() + camelcase[1:] |
264 | 171 | return re.sub('([A-Z])', repl_func, camelcase) | 170 | return re.sub('([A-Z])', repl_func, camelcase) |
265 | 172 | else: | 171 | else: |
266 | @@ -174,7 +173,7 @@ | |||
267 | 174 | 173 | ||
268 | 175 | def _toCamelCase(self, name): | 174 | def _toCamelCase(self, name): |
269 | 176 | if len(name) > 1: | 175 | if len(name) > 1: |
271 | 177 | repl_func = lambda match: match.group(1)[1:].upper() | 176 | def repl_func(match): return match.group(1)[1:].upper() |
272 | 178 | name = name[0].upper() + name[1:] | 177 | name = name[0].upper() + name[1:] |
273 | 179 | return re.sub('(_[a-z])', repl_func, name) | 178 | return re.sub('(_[a-z])', repl_func, name) |
274 | 180 | else: | 179 | else: |
275 | @@ -236,9 +235,9 @@ | |||
276 | 236 | # no-op. | 235 | # no-op. |
277 | 237 | return | 236 | return |
278 | 238 | data = self._raw_data | 237 | data = self._raw_data |
280 | 239 | data["UserWipOverrideComment"] = None; | 238 | data["UserWipOverrideComment"] = None |
281 | 240 | if ("AssignedUsers" in data and | 239 | if ("AssignedUsers" in data and |
283 | 241 | "assigned_user_id" not in self.dirty_attrs): | 240 | "assigned_user_id" not in self.dirty_attrs): |
284 | 242 | if 'AssignedUserId' in data.keys(): | 241 | if 'AssignedUserId' in data.keys(): |
285 | 243 | del data['AssignedUserId'] | 242 | del data['AssignedUserId'] |
286 | 244 | if 'AssignedUserName' in data.keys(): | 243 | if 'AssignedUserName' in data.keys(): |
287 | @@ -247,7 +246,6 @@ | |||
288 | 247 | lambda X: X['AssignedUserId'], data['AssignedUsers']) | 246 | lambda X: X['AssignedUserId'], data['AssignedUsers']) |
289 | 248 | 247 | ||
290 | 249 | for attr in self.dirty_attrs: | 248 | for attr in self.dirty_attrs: |
291 | 250 | #print "Storing %s in %s..." % (attr, self._toCamelCase(attr)) | ||
292 | 251 | data[self._toCamelCase(attr)] = getattr(self, attr) | 249 | data[self._toCamelCase(attr)] = getattr(self, attr) |
293 | 252 | 250 | ||
294 | 253 | board_id = str(self.lane.board.id) | 251 | board_id = str(self.lane.board.id) |
295 | @@ -380,7 +378,6 @@ | |||
296 | 380 | self.external_card_id = src.external_card_id | 378 | self.external_card_id = src.external_card_id |
297 | 381 | self.assigned_user_id = src.assigned_user_id | 379 | self.assigned_user_id = src.assigned_user_id |
298 | 382 | 380 | ||
299 | 383 | |||
300 | 384 | @property | 381 | @property |
301 | 385 | def parsed_description(self): | 382 | def parsed_description(self): |
302 | 386 | """Parse the card description to find key=value pairs. | 383 | """Parse the card description to find key=value pairs. |
303 | @@ -397,8 +394,8 @@ | |||
304 | 397 | end = match.end() | 394 | end = match.end() |
305 | 398 | try: | 395 | try: |
306 | 399 | annotations = Record(json.loads(self.description[start:end])) | 396 | annotations = Record(json.loads(self.description[start:end])) |
309 | 400 | except ValueError, ex: | 397 | except ValueError as ex: |
310 | 401 | print "Unable to parse card %s: %s" % (self.id, ex.message) | 398 | print("Unable to parse card %s: %s" % (self.id, ex.message)) |
311 | 402 | annotations = Record() | 399 | annotations = Record() |
312 | 403 | return ( | 400 | return ( |
313 | 404 | annotations, | 401 | annotations, |
314 | @@ -580,7 +577,7 @@ | |||
315 | 580 | archive_lanes = [lane_dict['Lane'] for lane_dict in self._archive] | 577 | archive_lanes = [lane_dict['Lane'] for lane_dict in self._archive] |
316 | 581 | archive_lanes.extend( | 578 | archive_lanes.extend( |
317 | 582 | [lane_dict['Lane'] for | 579 | [lane_dict['Lane'] for |
319 | 583 | lane_dict in self._archive[0]['ChildLanes']]) | 580 | lane_dict in self._archive[0]['ChildLanes']]) |
320 | 584 | lanes += archive_lanes | 581 | lanes += archive_lanes |
321 | 585 | lanes += self.connector.get( | 582 | lanes += self.connector.get( |
322 | 586 | "/Kanban/Api/Board/" + str(self.id) + "/Backlog").ReplyData[0] | 583 | "/Kanban/Api/Board/" + str(self.id) + "/Backlog").ReplyData[0] |
323 | @@ -594,7 +591,7 @@ | |||
324 | 594 | 591 | ||
325 | 595 | def _classifyCards(self): | 592 | def _classifyCards(self): |
326 | 596 | """Classify the cards into buckets for lookups later.""" | 593 | """Classify the cards into buckets for lookups later.""" |
328 | 597 | print " Classifying cards %s cards." % len(self.cards) | 594 | print(" Classifying cards %s cards." % len(self.cards)) |
329 | 598 | for card in self.cards: | 595 | for card in self.cards: |
330 | 599 | if card.type.name == "Feature": | 596 | if card.type.name == "Feature": |
331 | 600 | self._feature_cards.add(card) | 597 | self._feature_cards.add(card) |
332 | @@ -608,18 +605,18 @@ | |||
333 | 608 | if BRANCH_MP_REGEX.match(card.external_system_url): | 605 | if BRANCH_MP_REGEX.match(card.external_system_url): |
334 | 609 | self._cards_with_branches.add(card) | 606 | self._cards_with_branches.add(card) |
335 | 610 | else: | 607 | else: |
348 | 611 | print ("WARNING: Card won't sync. Non-branch link ({}) " | 608 | print("WARNING: Card won't sync. Non-branch link ({}) " |
349 | 612 | "for {}.".format(url, card.title)) | 609 | "for {}.".format(url, card.title)) |
350 | 613 | print " - %s feature cards" % len( | 610 | print(" - %s feature cards" % len( |
351 | 614 | self._feature_cards) | 611 | self._feature_cards)) |
352 | 615 | print " - %s cards with external ids" % len( | 612 | print(" - %s cards with external ids" % len( |
353 | 616 | self._cards_with_external_ids) | 613 | self._cards_with_external_ids)) |
354 | 617 | print " - %s cards with external links" % len( | 614 | print(" - %s cards with external links" % len( |
355 | 618 | self._cards_with_external_links) | 615 | self._cards_with_external_links)) |
356 | 619 | print " - %s cards with description annotations" % len( | 616 | print(" - %s cards with description annotations" % len( |
357 | 620 | self._cards_with_description_annotations) | 617 | self._cards_with_description_annotations)) |
358 | 621 | print " - %s cards with branches" % len( | 618 | print(" - %s cards with branches" % len( |
359 | 622 | self._cards_with_branches) | 619 | self._cards_with_branches)) |
360 | 623 | 620 | ||
361 | 624 | def _populateFeatureTagPaths(self): | 621 | def _populateFeatureTagPaths(self): |
362 | 625 | """Create a map from feature tag to the group owning the feature.""" | 622 | """Create a map from feature tag to the group owning the feature.""" |
363 | @@ -672,12 +669,13 @@ | |||
364 | 672 | 669 | ||
365 | 673 | def getLane(self, lane_id): | 670 | def getLane(self, lane_id): |
366 | 674 | flat_lanes = {} | 671 | flat_lanes = {} |
367 | 672 | |||
368 | 675 | def flatten_lane(lane): | 673 | def flatten_lane(lane): |
369 | 676 | flat_lanes[lane.id] = lane | 674 | flat_lanes[lane.id] = lane |
370 | 677 | for child in lane.child_lanes: | 675 | for child in lane.child_lanes: |
371 | 678 | flatten_lane(child) | 676 | flatten_lane(child) |
372 | 679 | map(flatten_lane, self.root_lane.child_lanes) | 677 | map(flatten_lane, self.root_lane.child_lanes) |
374 | 680 | return flat_lanes[lane_id]; | 678 | return flat_lanes[lane_id] |
375 | 681 | 679 | ||
376 | 682 | def getLaneByTitle(self, title): | 680 | def getLaneByTitle(self, title): |
377 | 683 | if len(self.root_lane.child_lanes) > 0: | 681 | if len(self.root_lane.child_lanes) > 0: |
378 | @@ -689,7 +687,7 @@ | |||
379 | 689 | else: | 687 | else: |
380 | 690 | for child in lane.child_lanes: | 688 | for child in lane.child_lanes: |
381 | 691 | result = self._getLaneByTitle(child, title) | 689 | result = self._getLaneByTitle(child, title) |
383 | 692 | if result != None: | 690 | if result is not None: |
384 | 693 | return result | 691 | return result |
385 | 694 | return None | 692 | return None |
386 | 695 | 693 | ||
387 | @@ -698,7 +696,7 @@ | |||
388 | 698 | return self._getLaneByPath(self.root_lane, path, ignorecase) | 696 | return self._getLaneByPath(self.root_lane, path, ignorecase) |
389 | 699 | 697 | ||
390 | 700 | def _getLaneByPath(self, lane, path, ignorecase): | 698 | def _getLaneByPath(self, lane, path, ignorecase): |
392 | 701 | if ignorecase == True: | 699 | if ignorecase is True: |
393 | 702 | if lane.path.lower() == path.lower(): | 700 | if lane.path.lower() == path.lower(): |
394 | 703 | return lane | 701 | return lane |
395 | 704 | else: | 702 | else: |
396 | @@ -707,24 +705,23 @@ | |||
397 | 707 | 705 | ||
398 | 708 | for child in lane.child_lanes: | 706 | for child in lane.child_lanes: |
399 | 709 | result = self._getLaneByPath(child, path, ignorecase) | 707 | result = self._getLaneByPath(child, path, ignorecase) |
401 | 710 | if result != None: | 708 | if result is not None: |
402 | 711 | return result | 709 | return result |
403 | 712 | return None | 710 | return None |
404 | 713 | 711 | ||
405 | 714 | |||
406 | 715 | def _printLanes(self, lane, indent, include_cards=False): | 712 | def _printLanes(self, lane, indent, include_cards=False): |
407 | 716 | next_lane = lane.getNextLanes() | 713 | next_lane = lane.getNextLanes() |
408 | 717 | if next_lane is None: | 714 | if next_lane is None: |
409 | 718 | next_lane = '' | 715 | next_lane = '' |
410 | 719 | else: | 716 | else: |
411 | 720 | next_lane = (' (next: any of [' + | 717 | next_lane = (' (next: any of [' + |
414 | 721 | ', '.join([my_lane.path for my_lane in next_lane]) | 718 | ', '.join([my_lane.path for my_lane in next_lane]) + |
415 | 722 | + '])') | 719 | '])') |
416 | 723 | next_lane += ' - %d cards' % len(lane.cards) | 720 | next_lane += ' - %d cards' % len(lane.cards) |
418 | 724 | print " " * indent + "* " + lane.title + next_lane | 721 | print(" " * indent + "* " + lane.title + next_lane) |
419 | 725 | for card in lane.cards: | 722 | for card in lane.cards: |
422 | 726 | print (" " * (indent + 1) + "- #" + card.external_card_id + | 723 | print(" " * (indent + 1) + "- #" + card.external_card_id + |
423 | 727 | ': ' + card.title) | 724 | ': ' + card.title) |
424 | 728 | for child in lane.child_lanes: | 725 | for child in lane.child_lanes: |
425 | 729 | self._printLanes(child, indent + 1) | 726 | self._printLanes(child, indent + 1) |
426 | 730 | 727 | ||
427 | @@ -732,7 +729,7 @@ | |||
428 | 732 | """Recursively prints all the lanes in the board with indentation.""" | 729 | """Recursively prints all the lanes in the board with indentation.""" |
429 | 733 | if len(self.root_lane.child_lanes) == 0: | 730 | if len(self.root_lane.child_lanes) == 0: |
430 | 734 | return | 731 | return |
432 | 735 | print "Board lanes:" | 732 | print("Board lanes:") |
433 | 736 | indent = 1 | 733 | indent = 1 |
434 | 737 | for lane in self.root_lane.child_lanes: | 734 | for lane in self.root_lane.child_lanes: |
435 | 738 | self._printLanes(lane, indent, include_cards) | 735 | self._printLanes(lane, indent, include_cards) |
436 | @@ -818,16 +815,16 @@ | |||
437 | 818 | kanban = LeankitKanban('launchpad.leankitkanban.com', | 815 | kanban = LeankitKanban('launchpad.leankitkanban.com', |
438 | 819 | 'user@email', 'password') | 816 | 'user@email', 'password') |
439 | 820 | 817 | ||
441 | 821 | print "Active boards:" | 818 | print("Active boards:") |
442 | 822 | boards = kanban.getBoards() | 819 | boards = kanban.getBoards() |
443 | 823 | for board in boards: | 820 | for board in boards: |
445 | 824 | print " * %s (%d)" % (board.title, board.id) | 821 | print(" * %s (%d)" % (board.title, board.id)) |
446 | 825 | 822 | ||
447 | 826 | # Get a board by the title. | 823 | # Get a board by the title. |
448 | 827 | board_name = 'Landscape 2016' | 824 | board_name = 'Landscape 2016' |
450 | 828 | print "Getting board '%s'..." % board_name | 825 | print("Getting board '%s'..." % board_name) |
451 | 829 | board = kanban.getBoard(title=board_name) | 826 | board = kanban.getBoard(title=board_name) |
452 | 830 | board.printLanes() | 827 | board.printLanes() |
453 | 831 | 828 | ||
454 | 832 | # Print all users. | 829 | # Print all users. |
456 | 833 | print board.users | 830 | print(board.users) |
457 | 834 | 831 | ||
458 | === modified file 'src/lp2kanban/tests/test_bugs2cards.py' | |||
459 | --- src/lp2kanban/tests/test_bugs2cards.py 2016-09-08 22:02:56 +0000 | |||
460 | +++ src/lp2kanban/tests/test_bugs2cards.py 2016-09-08 22:02:56 +0000 | |||
461 | @@ -1,8 +1,6 @@ | |||
462 | 1 | # Copyright 2011-2012 Canonical Ltd | 1 | # Copyright 2011-2012 Canonical Ltd |
463 | 2 | """Tests for the bugs2cards.py script.""" | 2 | """Tests for the bugs2cards.py script.""" |
464 | 3 | 3 | ||
465 | 4 | __metaclass__ = type | ||
466 | 5 | |||
467 | 6 | from ConfigParser import ( | 4 | from ConfigParser import ( |
468 | 7 | ConfigParser, | 5 | ConfigParser, |
469 | 8 | NoSectionError, | 6 | NoSectionError, |
470 | @@ -38,12 +36,9 @@ | |||
471 | 38 | ) | 36 | ) |
472 | 39 | from lp2kanban.kanban import ( | 37 | from lp2kanban.kanban import ( |
473 | 40 | RESOURCE_TYPE_LINK_SERIES, | 38 | RESOURCE_TYPE_LINK_SERIES, |
474 | 41 | LeankitCard, | ||
475 | 42 | Record, | 39 | Record, |
476 | 43 | ) | 40 | ) |
477 | 44 | from lp2kanban.tests.common import ( | 41 | from lp2kanban.tests.common import ( |
478 | 45 | FauxLeankitUser, | ||
479 | 46 | FauxLaunchpadUser, | ||
480 | 47 | FauxLaunchpadUsersForBoard, | 42 | FauxLaunchpadUsersForBoard, |
481 | 48 | FauxBoard, | 43 | FauxBoard, |
482 | 49 | FauxCard, | 44 | FauxCard, |
483 | @@ -51,6 +46,8 @@ | |||
484 | 51 | FauxLane, | 46 | FauxLane, |
485 | 52 | ) | 47 | ) |
486 | 53 | 48 | ||
487 | 49 | __metaclass__ = type | ||
488 | 50 | |||
489 | 54 | 51 | ||
490 | 55 | def parse_config_text(text): | 52 | def parse_config_text(text): |
491 | 56 | config = ConfigParser() | 53 | config = ConfigParser() |
492 | @@ -90,8 +87,7 @@ | |||
493 | 90 | globals, boards = parse_config(config) | 87 | globals, boards = parse_config(config) |
494 | 91 | 88 | ||
495 | 92 | self.assertEqual({'var1': 'value1', | 89 | self.assertEqual({'var1': 'value1', |
498 | 93 | 'var2': 'value2', | 90 | 'var2': 'value2'}, globals) |
497 | 94 | }, globals) | ||
499 | 95 | self.assertEqual({'Board 1': {'var1': 'board value 1'}}, boards) | 91 | self.assertEqual({'Board 1': {'var1': 'board value 1'}}, boards) |
500 | 96 | 92 | ||
501 | 97 | def test_parse_config_defaults(self): | 93 | def test_parse_config_defaults(self): |
502 | @@ -131,7 +127,9 @@ | |||
503 | 131 | 127 | ||
504 | 132 | 128 | ||
505 | 133 | class FauxMP: | 129 | class FauxMP: |
507 | 134 | def __init__(self, queue_status, target_name, link=None, description=None, date_created=None): | 130 | def __init__( |
508 | 131 | self, queue_status, target_name, link=None, description=None, | ||
509 | 132 | date_created=None): | ||
510 | 135 | self.queue_status = queue_status | 133 | self.queue_status = queue_status |
511 | 136 | self.target_branch = Record(name=target_name) | 134 | self.target_branch = Record(name=target_name) |
512 | 137 | self.web_link = link | 135 | self.web_link = link |
513 | @@ -145,7 +143,7 @@ | |||
514 | 145 | self.projects = data.get("projects", {}) | 143 | self.projects = data.get("projects", {}) |
515 | 146 | self.project_group = data.get("project_groups", {}) | 144 | self.project_group = data.get("project_groups", {}) |
516 | 147 | self.bugs = data.get("bugs", {}) | 145 | self.bugs = data.get("bugs", {}) |
518 | 148 | 146 | ||
519 | 149 | 147 | ||
520 | 150 | class BranchInfoTests(unittest.TestCase): | 148 | class BranchInfoTests(unittest.TestCase): |
521 | 151 | 149 | ||
522 | @@ -296,7 +294,7 @@ | |||
523 | 296 | def __init__(self, resource_type_link="http://api/project", project=None): | 294 | def __init__(self, resource_type_link="http://api/project", project=None): |
524 | 297 | self.resource_type_link = resource_type_link | 295 | self.resource_type_link = resource_type_link |
525 | 298 | self.project = project | 296 | self.project = project |
527 | 299 | 297 | ||
528 | 300 | 298 | ||
529 | 301 | class FauxBugTask: | 299 | class FauxBugTask: |
530 | 302 | 300 | ||
531 | @@ -312,23 +310,25 @@ | |||
532 | 312 | class IsBugInProjectsTest(unittest.TestCase): | 310 | class IsBugInProjectsTest(unittest.TestCase): |
533 | 313 | 311 | ||
534 | 314 | def test_false_when_part_of_external_project(self): | 312 | def test_false_when_part_of_external_project(self): |
536 | 315 | # Return False when the bug is part of an unconfigured (external) project | 313 | # Return False when the bug is part of an unconfigured (external) |
537 | 314 | # project | ||
538 | 316 | bug = FauxBug() | 315 | bug = FauxBug() |
540 | 317 | project = FauxTarget(resource_type_link="project", project="otherproject") | 316 | project = FauxTarget( |
541 | 317 | resource_type_link="project", project="otherproject") | ||
542 | 318 | bug.addTask(project) | 318 | bug.addTask(project) |
543 | 319 | self.assertFalse(is_bug_in_projects(bug, ["myproject1", "myproject2"])) | 319 | self.assertFalse(is_bug_in_projects(bug, ["myproject1", "myproject2"])) |
544 | 320 | 320 | ||
545 | 321 | def test_true_when_part_of_configured_projects(self): | 321 | def test_true_when_part_of_configured_projects(self): |
546 | 322 | # Return True when the bug is one of the configured (internal) projects | 322 | # Return True when the bug is one of the configured (internal) projects |
547 | 323 | bug = FauxBug() | 323 | bug = FauxBug() |
549 | 324 | project = FauxTarget(resource_type_link="project", project="myproject2") | 324 | project = FauxTarget( |
550 | 325 | resource_type_link="project", project="myproject2") | ||
551 | 325 | bug.addTask(project) | 326 | bug.addTask(project) |
552 | 326 | self.assertTrue(is_bug_in_projects(bug, ["myproject1", "myproject2"])) | 327 | self.assertTrue(is_bug_in_projects(bug, ["myproject1", "myproject2"])) |
553 | 327 | 328 | ||
554 | 328 | |||
555 | 329 | def test_true_when_series_within_a_configured_project(self): | 329 | def test_true_when_series_within_a_configured_project(self): |
558 | 330 | # Return True when the bug has a series task that is related to one of the | 330 | # Return True when the bug has a series task that is related to one |
559 | 331 | # configured projects. | 331 | # of the configured projects. |
560 | 332 | bug = FauxBug() | 332 | bug = FauxBug() |
561 | 333 | series = FauxTarget( | 333 | series = FauxTarget( |
562 | 334 | resource_type_link=RESOURCE_TYPE_LINK_SERIES, project="myproject1") | 334 | resource_type_link=RESOURCE_TYPE_LINK_SERIES, project="myproject1") |
563 | @@ -407,7 +407,8 @@ | |||
564 | 407 | # than 'Fix Committed'). | 407 | # than 'Fix Committed'). |
565 | 408 | bug = FauxBug() | 408 | bug = FauxBug() |
566 | 409 | project1 = FauxTarget(resource_type_link="project", project="lazr") | 409 | project1 = FauxTarget(resource_type_link="project", project="lazr") |
568 | 410 | project2 = FauxTarget(resource_type_link="project", project="launchpad") | 410 | project2 = FauxTarget( |
569 | 411 | resource_type_link="project", project="launchpad") | ||
570 | 411 | bug.addTask(project1, status='Fix Released') | 412 | bug.addTask(project1, status='Fix Released') |
571 | 412 | bug.addTask(project2, status='In Progress') | 413 | bug.addTask(project2, status='In Progress') |
572 | 413 | matches, status, assignees, mp_url, mp_type = get_bug_status( | 414 | matches, status, assignees, mp_url, mp_type = get_bug_status( |
573 | @@ -583,8 +584,9 @@ | |||
574 | 583 | self.assertTrue(should_sync_card(card, {'autosync': 'on', | 584 | self.assertTrue(should_sync_card(card, {'autosync': 'on', |
575 | 584 | 'sync_cards': 'on'})) | 585 | 'sync_cards': 'on'})) |
576 | 585 | 586 | ||
579 | 586 | def test_should_sync_card_autosync_synced_with_external_card_id(self): | 587 | def test_should_sync_card_autosync_active_projects_external_card_id(self): |
580 | 587 | # When autosync is 'active-projects', cards with external card IDs are synced. | 588 | # When autosync is 'active-projects', cards with external card |
581 | 589 | # IDs are synced. | ||
582 | 588 | card = Record(title=u'no sync', description=u'', | 590 | card = Record(title=u'no sync', description=u'', |
583 | 589 | external_card_id=u'11', external_system_url=u'') | 591 | external_card_id=u'11', external_system_url=u'') |
584 | 590 | self.assertTrue(should_sync_card(card, {'autosync': 'active-projects', | 592 | self.assertTrue(should_sync_card(card, {'autosync': 'active-projects', |
585 | @@ -663,8 +665,8 @@ | |||
586 | 663 | self.assertFalse(should_sync_card(card2, conf)) | 665 | self.assertFalse(should_sync_card(card2, conf)) |
587 | 664 | 666 | ||
588 | 665 | def test_get_card_status_new_no_branch(self): | 667 | def test_get_card_status_new_no_branch(self): |
591 | 666 | # For a bug in 'New', 'Triaged' or 'Confirmed' or 'Incomplete' statuses, | 668 | # For a bug in 'New', 'Triaged' or 'Confirmed' or |
592 | 667 | # the branch status is new state. | 669 | # 'Incomplete' statuses the branch status is new state. |
593 | 668 | no_branch_info = Record(status=None) | 670 | no_branch_info = Record(status=None) |
594 | 669 | self.assertEqual( | 671 | self.assertEqual( |
595 | 670 | CardStatus.NEW, | 672 | CardStatus.NEW, |
596 | @@ -681,29 +683,29 @@ | |||
597 | 681 | 683 | ||
598 | 682 | def test_get_card_status_coding_no_bug(self): | 684 | def test_get_card_status_coding_no_bug(self): |
599 | 683 | # For a card with no associated bug, an 'In Progress' branch status | 685 | # For a card with no associated bug, an 'In Progress' branch status |
601 | 684 | # will be in the coding state. | 686 | # will be in the coding state. |
602 | 685 | branch_info = Record(status='In Progress', target=None) | 687 | branch_info = Record(status='In Progress', target=None) |
603 | 686 | self.assertEqual( | 688 | self.assertEqual( |
605 | 687 | CardStatus.CODING, | 689 | CardStatus.CODING, |
606 | 688 | get_card_status(None, [], branch_info)) | 690 | get_card_status(None, [], branch_info)) |
607 | 689 | 691 | ||
608 | 690 | def test_get_card_status_review_no_bug(self): | 692 | def test_get_card_status_review_no_bug(self): |
609 | 691 | # For a card with no associated bug, an 'Approved' or 'In Review' | 693 | # For a card with no associated bug, an 'Approved' or 'In Review' |
611 | 692 | # branch status will be in the review state. | 694 | # branch status will be in the review state. |
612 | 693 | branch_infos = [ | 695 | branch_infos = [ |
613 | 694 | Record(status='In Review', target=None), | 696 | Record(status='In Review', target=None), |
614 | 695 | Record(status='Approved', target=None)] | 697 | Record(status='Approved', target=None)] |
615 | 696 | for branch_info in branch_infos: | 698 | for branch_info in branch_infos: |
616 | 697 | self.assertEqual( | 699 | self.assertEqual( |
618 | 698 | CardStatus.REVIEW, | 700 | CardStatus.REVIEW, |
619 | 699 | get_card_status(None, [], branch_info)) | 701 | get_card_status(None, [], branch_info)) |
620 | 700 | 702 | ||
621 | 701 | def test_get_card_status_landing_no_bug(self): | 703 | def test_get_card_status_landing_no_bug(self): |
622 | 702 | # For a card with no associated bug, a merged branch status will be in | 704 | # For a card with no associated bug, a merged branch status will be in |
624 | 703 | # the landing state. | 705 | # the landing state. |
625 | 704 | branch_info = Record(status='Merged', target=None) | 706 | branch_info = Record(status='Merged', target=None) |
626 | 705 | self.assertEqual( | 707 | self.assertEqual( |
628 | 706 | CardStatus.LANDING, | 708 | CardStatus.LANDING, |
629 | 707 | get_card_status(None, [], branch_info)) | 709 | get_card_status(None, [], branch_info)) |
630 | 708 | 710 | ||
631 | 709 | def test_get_card_status_coding_in_progress_no_branch(self): | 711 | def test_get_card_status_coding_in_progress_no_branch(self): |
632 | @@ -888,7 +890,7 @@ | |||
633 | 888 | cards = get_cards_for_feature( | 890 | cards = get_cards_for_feature( |
634 | 889 | self.feature_card, self.feature_bug, card_type='Branch') | 891 | self.feature_card, self.feature_bug, card_type='Branch') |
635 | 890 | self.assertItemsEqual([branch_card], cards) | 892 | self.assertItemsEqual([branch_card], cards) |
637 | 891 | 893 | ||
638 | 892 | 894 | ||
639 | 893 | class FindLaneNextTest(unittest.TestCase): | 895 | class FindLaneNextTest(unittest.TestCase): |
640 | 894 | 896 | ||
641 | @@ -980,7 +982,7 @@ | |||
642 | 980 | def test_no_bugs(self): | 982 | def test_no_bugs(self): |
643 | 981 | # If no bugs are found, no cards are created. | 983 | # If no bugs are found, no cards are created. |
644 | 982 | board = FauxBoard() | 984 | board = FauxBoard() |
646 | 983 | bug_lane = board.addLane('bugs') | 985 | board.addLane('bugs') |
647 | 984 | project = FauxLaunchpadProject(bug_tasks=[]) | 986 | project = FauxLaunchpadProject(bug_tasks=[]) |
648 | 985 | create_cards_in_project(board, project, 'kanban') | 987 | create_cards_in_project(board, project, 'kanban') |
649 | 986 | self.assertEqual([], board.cards) | 988 | self.assertEqual([], board.cards) |
650 | @@ -989,7 +991,7 @@ | |||
651 | 989 | # If a new bug is found, a card is created for it, having the | 991 | # If a new bug is found, a card is created for it, having the |
652 | 990 | # bug id, title and description saved. | 992 | # bug id, title and description saved. |
653 | 991 | board = FauxBoard() | 993 | board = FauxBoard() |
655 | 992 | bug_lane = board.addLane('bugs') | 994 | board.addLane('bugs') |
656 | 993 | project = FauxLaunchpadProject( | 995 | project = FauxLaunchpadProject( |
657 | 994 | bug_tasks=[FauxBugTask( | 996 | bug_tasks=[FauxBugTask( |
658 | 995 | bug_id=42, title='Test bug', | 997 | bug_id=42, title='Test bug', |
659 | @@ -1007,7 +1009,7 @@ | |||
660 | 1007 | # After a card is created from a bug, the bug tag is removed, so | 1009 | # After a card is created from a bug, the bug tag is removed, so |
661 | 1008 | # that having a lot of open bugs won't slow down the process. | 1010 | # that having a lot of open bugs won't slow down the process. |
662 | 1009 | board = FauxBoard() | 1011 | board = FauxBoard() |
664 | 1010 | bug_lane = board.addLane('bugs') | 1012 | board.addLane('bugs') |
665 | 1011 | project = FauxLaunchpadProject( | 1013 | project = FauxLaunchpadProject( |
666 | 1012 | bug_tasks=[FauxBugTask( | 1014 | bug_tasks=[FauxBugTask( |
667 | 1013 | bug_id=42, title='Test bug', | 1015 | bug_id=42, title='Test bug', |
668 | @@ -1038,7 +1040,7 @@ | |||
669 | 1038 | # Cards are created only for bugs having the bug tag that is | 1040 | # Cards are created only for bugs having the bug tag that is |
670 | 1039 | # passed in to create_cards_in_project(). | 1041 | # passed in to create_cards_in_project(). |
671 | 1040 | board = FauxBoard() | 1042 | board = FauxBoard() |
673 | 1041 | bug_lane = board.addLane('bugs') | 1043 | board.addLane('bugs') |
674 | 1042 | project = FauxLaunchpadProject( | 1044 | project = FauxLaunchpadProject( |
675 | 1043 | bug_tasks=[FauxBugTask( | 1045 | bug_tasks=[FauxBugTask( |
676 | 1044 | bug_id=21, title='Tagged with other tag', | 1046 | bug_id=21, title='Tagged with other tag', |
677 | @@ -1058,7 +1060,7 @@ | |||
678 | 1058 | # It's possible to specify which card type that the created card | 1060 | # It's possible to specify which card type that the created card |
679 | 1059 | # should have, instead of the default one. | 1061 | # should have, instead of the default one. |
680 | 1060 | board = FauxBoard() | 1062 | board = FauxBoard() |
682 | 1061 | bug_lane = board.addLane('bugs') | 1063 | board.addLane('bugs') |
683 | 1062 | project = FauxLaunchpadProject( | 1064 | project = FauxLaunchpadProject( |
684 | 1063 | bug_tasks=[FauxBugTask( | 1065 | bug_tasks=[FauxBugTask( |
685 | 1064 | bug_id=42, title='Test bug', | 1066 | bug_id=42, title='Test bug', |
686 | @@ -1066,7 +1068,8 @@ | |||
687 | 1066 | tags=['kanban'])]) | 1068 | tags=['kanban'])]) |
688 | 1067 | feature_type = FauxCardType(id=2, name='Feature', is_default=False) | 1069 | feature_type = FauxCardType(id=2, name='Feature', is_default=False) |
689 | 1068 | board.cardtypes[feature_type.id] = feature_type | 1070 | board.cardtypes[feature_type.id] = feature_type |
691 | 1069 | create_cards_in_project(board, project, 'kanban', cardtype_name='Feature') | 1071 | create_cards_in_project( |
692 | 1072 | board, project, 'kanban', cardtype_name='Feature') | ||
693 | 1070 | [card] = board.cards | 1073 | [card] = board.cards |
694 | 1071 | self.assertEqual(feature_type.id, card.type_id) | 1074 | self.assertEqual(feature_type.id, card.type_id) |
695 | 1072 | self.assertTrue(card.saved) | 1075 | self.assertTrue(card.saved) |
696 | @@ -1075,8 +1078,8 @@ | |||
697 | 1075 | # It's possible to specify to which lane the card should be | 1078 | # It's possible to specify to which lane the card should be |
698 | 1076 | # added. | 1079 | # added. |
699 | 1077 | board = FauxBoard() | 1080 | board = FauxBoard() |
702 | 1078 | bug_lane = board.addLane('bugs') | 1081 | board.addLane('bugs') |
703 | 1079 | bug_lane = board.addLane('incoming') | 1082 | board.addLane('incoming') |
704 | 1080 | project = FauxLaunchpadProject( | 1083 | project = FauxLaunchpadProject( |
705 | 1081 | bug_tasks=[FauxBugTask( | 1084 | bug_tasks=[FauxBugTask( |
706 | 1082 | bug_id=42, title='Test bug', | 1085 | bug_id=42, title='Test bug', |
707 | @@ -1094,7 +1097,7 @@ | |||
708 | 1094 | # Bug descriptions that exceed the 19997 character limit of a | 1097 | # Bug descriptions that exceed the 19997 character limit of a |
709 | 1095 | # kanban card are truncated to 19500 bytes. | 1098 | # kanban card are truncated to 19500 bytes. |
710 | 1096 | board = FauxBoard() | 1099 | board = FauxBoard() |
712 | 1097 | bug_lane = board.addLane('bugs') | 1100 | board.addLane('bugs') |
713 | 1098 | project = FauxLaunchpadProject( | 1101 | project = FauxLaunchpadProject( |
714 | 1099 | bug_tasks=[FauxBugTask( | 1102 | bug_tasks=[FauxBugTask( |
715 | 1100 | bug_id=42, title='Test bug', | 1103 | bug_id=42, title='Test bug', |
716 | @@ -1185,7 +1188,6 @@ | |||
717 | 1185 | parse_groups_config(config)) | 1188 | parse_groups_config(config)) |
718 | 1186 | 1189 | ||
719 | 1187 | 1190 | ||
720 | 1188 | |||
721 | 1189 | class LaunchpadUsersForBoardTest(unittest.TestCase): | 1191 | class LaunchpadUsersForBoardTest(unittest.TestCase): |
722 | 1190 | 1192 | ||
723 | 1191 | def setUp(self): | 1193 | def setUp(self): |
724 | @@ -1266,12 +1268,12 @@ | |||
725 | 1266 | lp_users = LaunchpadUsersForBoard() | 1268 | lp_users = LaunchpadUsersForBoard() |
726 | 1267 | lp_users.set_up_users(lp, board, config) | 1269 | lp_users.set_up_users(lp, board, config) |
727 | 1268 | self.assertEqual( | 1270 | self.assertEqual( |
729 | 1269 | {"sometag": coding_lane, "anothertag": coding_lane}, | 1271 | {"sometag": coding_lane, "anothertag": coding_lane}, |
730 | 1270 | lp_users.feature_lanes) | 1272 | lp_users.feature_lanes) |
731 | 1271 | 1273 | ||
732 | 1272 | def test_feature_lanes_unset_with_group_new_lanes_and_no_assignee(self): | 1274 | def test_feature_lanes_unset_with_group_new_lanes_and_no_assignee(self): |
733 | 1273 | # When a feature card is present and tagged and the new_lanes | 1275 | # When a feature card is present and tagged and the new_lanes |
735 | 1274 | # in the config specifies the ${group} variable, | 1276 | # in the config specifies the ${group} variable, |
736 | 1275 | # feature_lanes will be unset when there are no assigned users on the | 1277 | # feature_lanes will be unset when there are no assigned users on the |
737 | 1276 | # card. | 1278 | # card. |
738 | 1277 | feature_card = FauxCard(tags="sometag") | 1279 | feature_card = FauxCard(tags="sometag") |
739 | @@ -1305,7 +1307,8 @@ | |||
740 | 1305 | lp = None | 1307 | lp = None |
741 | 1306 | config = {"groups_config_file": groups_file.name, | 1308 | config = {"groups_config_file": groups_file.name, |
742 | 1307 | "new_lanes": "dev::${group}::coding"} | 1309 | "new_lanes": "dev::${group}::coding"} |
744 | 1308 | lp_users = LaunchpadUsersForBoard(kanban_to_lp={1:FauxPerson("User1")}) | 1310 | lp_users = LaunchpadUsersForBoard( |
745 | 1311 | kanban_to_lp={1: FauxPerson("User1")}) | ||
746 | 1309 | lp_users.set_up_users(lp, board, config) | 1312 | lp_users.set_up_users(lp, board, config) |
747 | 1310 | self.assertEqual( | 1313 | self.assertEqual( |
748 | 1311 | {"squad-group-1": coding_lane, "squad-group-2": coding_lane2}, | 1314 | {"squad-group-1": coding_lane, "squad-group-2": coding_lane2}, |
749 | @@ -1313,7 +1316,7 @@ | |||
750 | 1313 | 1316 | ||
751 | 1314 | def test_feature_lanes_set_with_group_new_lanes_and_assignee(self): | 1317 | def test_feature_lanes_set_with_group_new_lanes_and_assignee(self): |
752 | 1315 | # When a feature card is present and tagged and the new_lanes | 1318 | # When a feature card is present and tagged and the new_lanes |
754 | 1316 | # in the config specifies the ${group} variable, | 1319 | # in the config specifies the ${group} variable, |
755 | 1317 | # feature_lanes will be set when there are is an assigned user on the | 1320 | # feature_lanes will be set when there are is an assigned user on the |
756 | 1318 | # card. | 1321 | # card. |
757 | 1319 | feature_card = FauxCard(tags="sometag,anothertag") | 1322 | feature_card = FauxCard(tags="sometag,anothertag") |
758 | @@ -1336,17 +1339,17 @@ | |||
759 | 1336 | lp = None | 1339 | lp = None |
760 | 1337 | config = {"groups_config_file": groups_file.name, | 1340 | config = {"groups_config_file": groups_file.name, |
761 | 1338 | "new_lanes": "dev::${group}::coding"} | 1341 | "new_lanes": "dev::${group}::coding"} |
763 | 1339 | lp_users = LaunchpadUsersForBoard(kanban_to_lp={1:FauxPerson("User1")}) | 1342 | lp_users = LaunchpadUsersForBoard( |
764 | 1343 | kanban_to_lp={1: FauxPerson("User1")}) | ||
765 | 1340 | lp_users.set_up_users(lp, board, config) | 1344 | lp_users.set_up_users(lp, board, config) |
766 | 1341 | self.assertEqual( | 1345 | self.assertEqual( |
769 | 1342 | {"sometag": coding_lane, "anothertag": coding_lane, | 1346 | {"sometag": coding_lane, "anothertag": coding_lane, |
770 | 1343 | "squad-group-1": coding_lane, "squad-group-2": coding_lane2}, | 1347 | "squad-group-1": coding_lane, "squad-group-2": coding_lane2}, |
771 | 1344 | lp_users.feature_lanes) | 1348 | lp_users.feature_lanes) |
772 | 1345 | 1349 | ||
773 | 1346 | 1350 | ||
774 | 1347 | class FauxPerson: | 1351 | class FauxPerson: |
775 | 1348 | 1352 | ||
776 | 1349 | |||
777 | 1350 | def __init__(self, name): | 1353 | def __init__(self, name): |
778 | 1351 | self.name = name | 1354 | self.name = name |
779 | 1352 | 1355 | ||
780 | @@ -1654,7 +1657,7 @@ | |||
781 | 1654 | self.lp_users = FauxLaunchpadUsersForBoard() | 1657 | self.lp_users = FauxLaunchpadUsersForBoard() |
782 | 1655 | 1658 | ||
783 | 1656 | def test_sync_cards_moves_landing_cards_to_taskboard_in_landing_lane(self): | 1659 | def test_sync_cards_moves_landing_cards_to_taskboard_in_landing_lane(self): |
785 | 1657 | """ | 1660 | """ |
786 | 1658 | Feature-linked cards in landing lanes will move to taskboard when the | 1661 | Feature-linked cards in landing lanes will move to taskboard when the |
787 | 1659 | feature card is in landing_lanes. | 1662 | feature card is in landing_lanes. |
788 | 1660 | """ | 1663 | """ |
789 | @@ -1735,16 +1738,20 @@ | |||
790 | 1735 | self.assertEqual((self.feature_card, 'Done'), landed_card2.moved_to) | 1738 | self.assertEqual((self.feature_card, 'Done'), landed_card2.moved_to) |
791 | 1736 | 1739 | ||
792 | 1737 | def test_sync_cards_external_ids_filter_card(self): | 1740 | def test_sync_cards_external_ids_filter_card(self): |
795 | 1738 | """filter_card skips and selects correctly.""" | 1741 | """filter_card skips and selects correctly.""" |
796 | 1739 | # Configure bugs2cards to sync only active-projects myproject1 and myproject2 | 1742 | # Configure bugs2cards to sync only active-projects myproject1 |
797 | 1743 | # and myproject2 | ||
798 | 1740 | self.bconf.update({ | 1744 | self.bconf.update({ |
799 | 1741 | "autosync": "active-projects", | 1745 | "autosync": "active-projects", |
800 | 1742 | "projects": "myproject", | 1746 | "projects": "myproject", |
801 | 1743 | "sync_cards": "on", "move_cards": "on", | 1747 | "sync_cards": "on", "move_cards": "on", |
802 | 1744 | "new_lanes": "new"}) | 1748 | "new_lanes": "new"}) |
806 | 1745 | # Setup bug1 and bug2 in separate projects which both need to move coding -> new | 1749 | # Setup bug1 and bug2 in separate projects which both need to |
807 | 1746 | project1 = FauxTarget(resource_type_link="project", project="myproject") | 1750 | # move coding -> new |
808 | 1747 | lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"}) | 1751 | project1 = FauxTarget( |
809 | 1752 | resource_type_link="project", project="myproject") | ||
810 | 1753 | lp_users = FauxLaunchpadUsersForBoard( | ||
811 | 1754 | lp_to_kanban={"person1": "Person One"}) | ||
812 | 1748 | lp_projects = [project1] | 1755 | lp_projects = [project1] |
813 | 1749 | bug_task1 = FauxBugTask( | 1756 | bug_task1 = FauxBugTask( |
814 | 1750 | bug_id=123, target=project1, title='Test bug in myproject', | 1757 | bug_id=123, target=project1, title='Test bug in myproject', |
815 | @@ -1766,25 +1773,31 @@ | |||
816 | 1766 | bug_card1.type = bug_type | 1773 | bug_card1.type = bug_type |
817 | 1767 | bug_card2.type = bug_type | 1774 | bug_card2.type = bug_type |
818 | 1768 | self.board.cards.extend([bug_card1, bug_card2]) | 1775 | self.board.cards.extend([bug_card1, bug_card2]) |
821 | 1769 | lp = FauxLP({"projects": {"myproject": project1}, | 1776 | lp = FauxLP({ |
822 | 1770 | "bugs": {123: bug_task1.bug, 456: bug_task2.bug}}) | 1777 | "projects": {"myproject": project1}, |
823 | 1778 | "bugs": {123: bug_task1.bug, 456: bug_task2.bug}}) | ||
824 | 1771 | sync_cards_external_ids( | 1779 | sync_cards_external_ids( |
826 | 1772 | board, self.bconf, lp, lp_users, lp_projects, filter_card=bug_card2.id) | 1780 | board, self.bconf, lp, lp_users, |
827 | 1781 | lp_projects, filter_card=bug_card2.id) | ||
828 | 1773 | coding_lane = FauxLane(board=board, path='coding') | 1782 | coding_lane = FauxLane(board=board, path='coding') |
829 | 1774 | self.assertEqual(new_lane, bug_card2.moved_to) | 1783 | self.assertEqual(new_lane, bug_card2.moved_to) |
830 | 1775 | self.assertIsNone(bug_card1.moved_to) | 1784 | self.assertIsNone(bug_card1.moved_to) |
831 | 1776 | 1785 | ||
832 | 1777 | def test_sync_cards_skips_bugs_not_in_filter_bug(self): | 1786 | def test_sync_cards_skips_bugs_not_in_filter_bug(self): |
835 | 1778 | """filter_bug skips and selects bugs in sync_cards_external_ids.""" | 1787 | """filter_bug skips and selects bugs in sync_cards_external_ids.""" |
836 | 1779 | # Configure bugs2cards to sync only active-projects myproject1 and myproject2 | 1788 | # Configure bugs2cards to sync only active-projects myproject1 |
837 | 1789 | # and myproject2 | ||
838 | 1780 | self.bconf.update({ | 1790 | self.bconf.update({ |
839 | 1781 | "autosync": "active-projects", | 1791 | "autosync": "active-projects", |
840 | 1782 | "projects": "myproject", | 1792 | "projects": "myproject", |
841 | 1783 | "sync_cards": "on", "move_cards": "on", | 1793 | "sync_cards": "on", "move_cards": "on", |
842 | 1784 | "new_lanes": "new"}) | 1794 | "new_lanes": "new"}) |
846 | 1785 | # Setup bug1 and bug2 in separate projects which both need to move coding -> new | 1795 | # Setup bug1 and bug2 in separate projects which both need |
847 | 1786 | project1 = FauxTarget(resource_type_link="project", project="myproject") | 1796 | # to move coding -> new |
848 | 1787 | lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"}) | 1797 | project1 = FauxTarget( |
849 | 1798 | resource_type_link="project", project="myproject") | ||
850 | 1799 | lp_users = FauxLaunchpadUsersForBoard( | ||
851 | 1800 | lp_to_kanban={"person1": "Person One"}) | ||
852 | 1788 | lp_projects = [project1] | 1801 | lp_projects = [project1] |
853 | 1789 | bug_task1 = FauxBugTask( | 1802 | bug_task1 = FauxBugTask( |
854 | 1790 | bug_id=123, target=project1, title='Test bug in myproject', | 1803 | bug_id=123, target=project1, title='Test bug in myproject', |
855 | @@ -1806,26 +1819,34 @@ | |||
856 | 1806 | bug_card1.type = bug_type | 1819 | bug_card1.type = bug_type |
857 | 1807 | bug_card2.type = bug_type | 1820 | bug_card2.type = bug_type |
858 | 1808 | self.board.cards.extend([bug_card1, bug_card2]) | 1821 | self.board.cards.extend([bug_card1, bug_card2]) |
861 | 1809 | lp = FauxLP({"projects": {"myproject": project1}, | 1822 | lp = FauxLP({ |
862 | 1810 | "bugs": {123: bug_task1.bug, 456: bug_task2.bug}}) | 1823 | "projects": {"myproject": project1}, |
863 | 1824 | "bugs": {123: bug_task1.bug, 456: bug_task2.bug}}) | ||
864 | 1811 | sync_cards_external_ids( | 1825 | sync_cards_external_ids( |
865 | 1812 | board, self.bconf, lp, lp_users, lp_projects, filter_bug=u"456") | 1826 | board, self.bconf, lp, lp_users, lp_projects, filter_bug=u"456") |
866 | 1813 | coding_lane = FauxLane(board=board, path='coding') | 1827 | coding_lane = FauxLane(board=board, path='coding') |
867 | 1814 | self.assertEqual(new_lane, bug_card2.moved_to) | 1828 | self.assertEqual(new_lane, bug_card2.moved_to) |
868 | 1815 | self.assertIsNone(bug_card1.moved_to) | 1829 | self.assertIsNone(bug_card1.moved_to) |
869 | 1816 | 1830 | ||
873 | 1817 | def test_sync_cards_skips_bugs_of_external_projects_when_active_projects_set(self): | 1831 | def test_sync_cards_skips_bugs_of_external_projects_active_projects(self): |
874 | 1818 | """Bugs of external projects are skipped when autosync set to active-projects.""" | 1832 | """Bugs of external projects are skipped when autosync set to |
875 | 1819 | # Configure bugs2cards to sync only active-projects myproject1 and myproject2 | 1833 | active-projects.""" |
876 | 1834 | # Configure bugs2cards to sync only active-projects myproject1 | ||
877 | 1835 | # and myproject2 | ||
878 | 1820 | self.bconf.update({"autosync": "active-projects", | 1836 | self.bconf.update({"autosync": "active-projects", |
879 | 1821 | "projects": "myproject,myproject2", | 1837 | "projects": "myproject,myproject2", |
880 | 1822 | "sync_cards": "on", "move_cards": "on", | 1838 | "sync_cards": "on", "move_cards": "on", |
881 | 1823 | "new_lanes": "new"}) | 1839 | "new_lanes": "new"}) |
887 | 1824 | # Setup bug1 and bug2 in separate projects which both need to move coding -> new | 1840 | # Setup bug1 and bug2 in separate projects which both need to move |
888 | 1825 | project1 = FauxTarget(resource_type_link="project", project="myproject") | 1841 | # coding -> new |
889 | 1826 | project2 = FauxTarget(resource_type_link="project2", project="myproject2") | 1842 | project1 = FauxTarget( |
890 | 1827 | otherproject = FauxTarget(resource_type_link="project", project="otherproject") | 1843 | resource_type_link="project", project="myproject") |
891 | 1828 | lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"}) | 1844 | project2 = FauxTarget( |
892 | 1845 | resource_type_link="project2", project="myproject2") | ||
893 | 1846 | otherproject = FauxTarget( | ||
894 | 1847 | resource_type_link="project", project="otherproject") | ||
895 | 1848 | lp_users = FauxLaunchpadUsersForBoard( | ||
896 | 1849 | lp_to_kanban={"person1": "Person One"}) | ||
897 | 1829 | lp_projects = [project1, project2] | 1850 | lp_projects = [project1, project2] |
898 | 1830 | bug_task1 = FauxBugTask( | 1851 | bug_task1 = FauxBugTask( |
899 | 1831 | bug_id=123, target=project1, title='Test bug in myproject', | 1852 | bug_id=123, target=project1, title='Test bug in myproject', |
900 | @@ -1848,8 +1869,9 @@ | |||
901 | 1848 | bug_card2.type = bug_type | 1869 | bug_card2.type = bug_type |
902 | 1849 | self.board.cards.extend([bug_card1, bug_card2]) | 1870 | self.board.cards.extend([bug_card1, bug_card2]) |
903 | 1850 | # Include project1 and project2 in our active 'projects' fake LP data | 1871 | # Include project1 and project2 in our active 'projects' fake LP data |
906 | 1851 | lp = FauxLP({"projects": {"myproject": project1, "myproject2": project2}, | 1872 | lp = FauxLP({ |
907 | 1852 | "bugs": {123: bug_task1.bug, 456: bug_task2.bug}}) | 1873 | "projects": {"myproject": project1, "myproject2": project2}, |
908 | 1874 | "bugs": {123: bug_task1.bug, 456: bug_task2.bug}}) | ||
909 | 1853 | sync_cards_external_ids(board, self.bconf, lp, lp_users, lp_projects) | 1875 | sync_cards_external_ids(board, self.bconf, lp, lp_users, lp_projects) |
910 | 1854 | coding_lane = FauxLane(board=board, path='coding') | 1876 | coding_lane = FauxLane(board=board, path='coding') |
911 | 1855 | self.assertEqual(new_lane, bug_card1.moved_to) | 1877 | self.assertEqual(new_lane, bug_card1.moved_to) |
912 | 1856 | 1878 | ||
913 | === modified file 'src/lp2kanban/tests/test_cards2workitems.py' | |||
914 | --- src/lp2kanban/tests/test_cards2workitems.py 2013-03-19 16:06:09 +0000 | |||
915 | +++ src/lp2kanban/tests/test_cards2workitems.py 2016-09-08 22:02:56 +0000 | |||
916 | @@ -1,5 +1,4 @@ | |||
917 | 1 | # Copyright 2012 Canonical Ltd. | 1 | # Copyright 2012 Canonical Ltd. |
918 | 2 | __metaclass__ = type | ||
919 | 3 | 2 | ||
920 | 4 | import re | 3 | import re |
921 | 5 | import testtools | 4 | import testtools |
922 | @@ -27,6 +26,8 @@ | |||
923 | 27 | from testtools.monkey import patch | 26 | from testtools.monkey import patch |
924 | 28 | from textwrap import dedent | 27 | from textwrap import dedent |
925 | 29 | 28 | ||
926 | 29 | __metaclass__ = type | ||
927 | 30 | |||
928 | 30 | 31 | ||
929 | 31 | class CardsToWorkItemsBase(testtools.TestCase): | 32 | class CardsToWorkItemsBase(testtools.TestCase): |
930 | 32 | 33 | ||
931 | @@ -56,9 +57,9 @@ | |||
932 | 56 | "String \"{string}\" did not match pattern \"{pattern}\"".format( | 57 | "String \"{string}\" did not match pattern \"{pattern}\"".format( |
933 | 57 | string=string, pattern=pattern)) | 58 | string=string, pattern=pattern)) |
934 | 58 | 59 | ||
938 | 59 | def _generateCard(self, board=None, lane=None, | 60 | def _generateCard( |
939 | 60 | description_annotations=None, assigned_user_id=0, | 61 | self, board=None, lane=None, description_annotations=None, |
940 | 61 | title=None, external_system_url=None): | 62 | assigned_user_id=0, title=None, external_system_url=None): |
941 | 62 | if board is None: | 63 | if board is None: |
942 | 63 | board = self.board | 64 | board = self.board |
943 | 64 | if lane is None: | 65 | if lane is None: |
944 | @@ -220,7 +221,8 @@ | |||
945 | 220 | work_item = convert_card_to_work_item( | 221 | work_item = convert_card_to_work_item( |
946 | 221 | card, self.board, self.lp_users_for_board) | 222 | card, self.board, self.lp_users_for_board) |
947 | 222 | self.assertEqual( | 223 | self.assertEqual( |
949 | 223 | card.description_annotations.blueprint_url, work_item.blueprint_url) | 224 | card.description_annotations.blueprint_url, |
950 | 225 | work_item.blueprint_url) | ||
951 | 224 | 226 | ||
952 | 225 | def test_blueprint_extracted_from_card_external_link(self): | 227 | def test_blueprint_extracted_from_card_external_link(self): |
953 | 226 | # Cards can also be linked to blueprints by putting the | 228 | # Cards can also be linked to blueprints by putting the |
954 | @@ -233,7 +235,7 @@ | |||
955 | 233 | external_system_url=self.blueprint_url, | 235 | external_system_url=self.blueprint_url, |
956 | 234 | description_annotations=Record(), | 236 | description_annotations=Record(), |
957 | 235 | ) | 237 | ) |
959 | 236 | work_item = convert_card_to_work_item( | 238 | convert_card_to_work_item( |
960 | 237 | card, self.board, self.lp_users_for_board) | 239 | card, self.board, self.lp_users_for_board) |
961 | 238 | self.assertEqual( | 240 | self.assertEqual( |
962 | 239 | self.blueprint_url, card.description_annotations.blueprint_url) | 241 | self.blueprint_url, card.description_annotations.blueprint_url) |
963 | @@ -243,8 +245,8 @@ | |||
964 | 243 | # isn't a blueprint, convert_card_to_work_item() will return | 245 | # isn't a blueprint, convert_card_to_work_item() will return |
965 | 244 | # None. | 246 | # None. |
966 | 245 | card = self._generateCard( | 247 | card = self._generateCard( |
969 | 246 | description_annotations=Record | 248 | description_annotations=Record( |
970 | 247 | (blueprint_url= 'https://nyancat.com')) | 249 | blueprint_url='https://nyancat.com')) |
971 | 248 | work_item = convert_card_to_work_item( | 250 | work_item = convert_card_to_work_item( |
972 | 249 | card, self.board, self.lp_users_for_board) | 251 | card, self.board, self.lp_users_for_board) |
973 | 250 | self.assertIsNone(work_item) | 252 | self.assertIsNone(work_item) |
974 | @@ -268,8 +270,8 @@ | |||
975 | 268 | self._generateCard( | 270 | self._generateCard( |
976 | 269 | title="Card %i" % id, | 271 | title="Card %i" % id, |
977 | 270 | description_annotations=Record( | 272 | description_annotations=Record( |
980 | 271 | blueprint_url=blueprint_url_tpl % id)) | 273 | blueprint_url=blueprint_url_tpl % id |
981 | 272 | for id in range(3)] | 274 | )) for id in range(3)] |
982 | 273 | self.work_items = convert_cards_to_work_items( | 275 | self.work_items = convert_cards_to_work_items( |
983 | 274 | self.cards, self.board, self.lp_users_for_board) | 276 | self.cards, self.board, self.lp_users_for_board) |
984 | 275 | self.blueprints = {} | 277 | self.blueprints = {} |
985 | @@ -283,6 +285,7 @@ | |||
986 | 283 | # We stub out the Launchpad interaction using a fake version of | 285 | # We stub out the Launchpad interaction using a fake version of |
987 | 284 | # fetch_blueprint() | 286 | # fetch_blueprint() |
988 | 285 | called_with_urls = [] | 287 | called_with_urls = [] |
989 | 288 | |||
990 | 286 | def fake_fetch_blueprint(lp, url): | 289 | def fake_fetch_blueprint(lp, url): |
991 | 287 | called_with_urls.append(url) | 290 | called_with_urls.append(url) |
992 | 288 | return FauxBlueprint(name="somename") | 291 | return FauxBlueprint(name="somename") |
993 | 289 | 292 | ||
994 | === modified file 'src/lp2kanban/tests/test_description_annotations.py' | |||
995 | --- src/lp2kanban/tests/test_description_annotations.py 2013-03-18 13:25:36 +0000 | |||
996 | +++ src/lp2kanban/tests/test_description_annotations.py 2016-09-08 22:02:56 +0000 | |||
997 | @@ -9,6 +9,7 @@ | |||
998 | 9 | from textwrap import dedent | 9 | from textwrap import dedent |
999 | 10 | import unittest | 10 | import unittest |
1000 | 11 | 11 | ||
1001 | 12 | |||
1002 | 12 | class DescriptionAnnotationsTest(unittest.TestCase): | 13 | class DescriptionAnnotationsTest(unittest.TestCase): |
1003 | 13 | """Tests for kanban.LeankitCard description annotations.""" | 14 | """Tests for kanban.LeankitCard description annotations.""" |
1004 | 14 | 15 | ||
1005 | @@ -16,7 +17,7 @@ | |||
1006 | 16 | # parse_card_description() parses key:value pairs out of a | 17 | # parse_card_description() parses key:value pairs out of a |
1007 | 17 | # card's description and returns them as a dict. | 18 | # card's description and returns them as a dict. |
1008 | 18 | card = LeankitCard.create(FauxLane("Test")) | 19 | card = LeankitCard.create(FauxLane("Test")) |
1010 | 19 | card.title=u"some card" | 20 | card.title = u"some card" |
1011 | 20 | card.description = dedent(""" | 21 | card.description = dedent(""" |
1012 | 21 | {"key2": "value2", "key1": "value 1"} | 22 | {"key2": "value2", "key1": "value 1"} |
1013 | 22 | """) | 23 | """) |
1014 | @@ -28,7 +29,7 @@ | |||
1015 | 28 | # Any text not in the key=value form is returned unparsed by | 29 | # Any text not in the key=value form is returned unparsed by |
1016 | 29 | # card.parsed_description(). | 30 | # card.parsed_description(). |
1017 | 30 | card = LeankitCard.create(FauxLane("Test")) | 31 | card = LeankitCard.create(FauxLane("Test")) |
1019 | 31 | card.title=u"some card" | 32 | card.title = u"some card" |
1020 | 32 | card.description = dedent(""" | 33 | card.description = dedent(""" |
1021 | 33 | Some other text, | 34 | Some other text, |
1022 | 34 | {"key2": "value2", "key1": "value 1"} | 35 | {"key2": "value2", "key1": "value 1"} |
1023 | @@ -44,7 +45,7 @@ | |||
1024 | 44 | # LeankitCard.description_annotations returns only the key=value | 45 | # LeankitCard.description_annotations returns only the key=value |
1025 | 45 | # pairs from the description field. | 46 | # pairs from the description field. |
1026 | 46 | card = LeankitCard.create(FauxLane("Test")) | 47 | card = LeankitCard.create(FauxLane("Test")) |
1028 | 47 | card.title=u"some card" | 48 | card.title = u"some card" |
1029 | 48 | card.description = dedent(""" | 49 | card.description = dedent(""" |
1030 | 49 | {"key2": "value2", "key1": "value 1"} | 50 | {"key2": "value2", "key1": "value 1"} |
1031 | 50 | Here's a URL: http://test.com?a=b | 51 | Here's a URL: http://test.com?a=b |
1032 | @@ -57,7 +58,7 @@ | |||
1033 | 57 | # LeankitCard._setDescriptionAnnotations() can be used to write | 58 | # LeankitCard._setDescriptionAnnotations() can be used to write |
1034 | 58 | # new annotations to the description field. | 59 | # new annotations to the description field. |
1035 | 59 | card = LeankitCard.create(FauxLane("Test")) | 60 | card = LeankitCard.create(FauxLane("Test")) |
1037 | 60 | card.title=u"some card" | 61 | card.title = u"some card" |
1038 | 61 | card.description = dedent(""" | 62 | card.description = dedent(""" |
1039 | 62 | Some text | 63 | Some text |
1040 | 63 | {"key2": "value2", "key1": "value 1"} | 64 | {"key2": "value2", "key1": "value 1"} |
1041 | @@ -73,7 +74,7 @@ | |||
1042 | 73 | # LeankitCard.description_annotations is a Record, so it hass | 74 | # LeankitCard.description_annotations is a Record, so it hass |
1043 | 74 | # attribute access for its values. | 75 | # attribute access for its values. |
1044 | 75 | card = LeankitCard.create(FauxLane("Test")) | 76 | card = LeankitCard.create(FauxLane("Test")) |
1046 | 76 | card.title=u"some card" | 77 | card.title = u"some card" |
1047 | 77 | card.description = '{"key1": "value 1"}' | 78 | card.description = '{"key1": "value 1"}' |
1048 | 78 | self.assertIsInstance(card.description_annotations, Record) | 79 | self.assertIsInstance(card.description_annotations, Record) |
1049 | 79 | self.assertEqual("value 1", card.description_annotations.key1) | 80 | self.assertEqual("value 1", card.description_annotations.key1) |
1050 | 80 | 81 | ||
1051 | === modified file 'src/lp2kanban/workitems2cards.py' | |||
1052 | --- src/lp2kanban/workitems2cards.py 2013-03-18 13:42:24 +0000 | |||
1053 | +++ src/lp2kanban/workitems2cards.py 2016-09-08 22:02:56 +0000 | |||
1054 | @@ -66,8 +66,8 @@ | |||
1055 | 66 | assigned_user_id = assigned_user.id | 66 | assigned_user_id = assigned_user.id |
1056 | 67 | else: | 67 | else: |
1057 | 68 | assigned_user_id = 0 | 68 | assigned_user_id = 0 |
1060 | 69 | print "Creating card {title} [{assignee}]".format( | 69 | print(u"Creating card {title} [{assignee}]".format( |
1061 | 70 | title=work_item.text, assignee=assigned_user_id) | 70 | title=work_item.text, assignee=assigned_user_id)) |
1062 | 71 | card = lane.addCard() | 71 | card = lane.addCard() |
1063 | 72 | card.title = work_item.text | 72 | card.title = work_item.text |
1064 | 73 | card.description_annotations.blueprint_url = work_item.blueprint | 73 | card.description_annotations.blueprint_url = work_item.blueprint |
1065 | @@ -90,15 +90,15 @@ | |||
1066 | 90 | 90 | ||
1067 | 91 | match = re.match(BLUEPRINT_URL_PATTERN, args.spec_url) | 91 | match = re.match(BLUEPRINT_URL_PATTERN, args.spec_url) |
1068 | 92 | if match is None: | 92 | if match is None: |
1070 | 93 | print "Spec URL is not a Launchpad blueprint." | 93 | print("Spec URL is not a Launchpad blueprint.") |
1071 | 94 | sys.exit(1) | 94 | sys.exit(1) |
1072 | 95 | 95 | ||
1073 | 96 | if not args.dry_run: | 96 | if not args.dry_run: |
1075 | 97 | should_continue = raw_input( | 97 | should_continue = raw_input( # noqa |
1076 | 98 | "WARNING: This script is not well-tested and will mess with " | 98 | "WARNING: This script is not well-tested and will mess with " |
1077 | 99 | "your Kanban board; use it at your own risk. Continue? [y/N] ") | 99 | "your Kanban board; use it at your own risk. Continue? [y/N] ") |
1078 | 100 | if should_continue.lower() != "y": | 100 | if should_continue.lower() != "y": |
1080 | 101 | print "Exiting." | 101 | print("Exiting.") |
1081 | 102 | sys.exit(0) | 102 | sys.exit(0) |
1082 | 103 | 103 | ||
1083 | 104 | match_dict = match.groupdict() | 104 | match_dict = match.groupdict() |
1084 | @@ -115,17 +115,17 @@ | |||
1085 | 115 | sys.exit(1) | 115 | sys.exit(1) |
1086 | 116 | work_items = parse_work_items(lp, **match_dict) | 116 | work_items = parse_work_items(lp, **match_dict) |
1087 | 117 | if not args.dry_run: | 117 | if not args.dry_run: |
1089 | 118 | should_continue = raw_input( | 118 | should_continue = raw_input( # noqa |
1090 | 119 | "{count} cards will be created, whether or not you want them all. " | 119 | "{count} cards will be created, whether or not you want them all. " |
1091 | 120 | "Continue [y/N]?".format(count=len(work_items))) | 120 | "Continue [y/N]?".format(count=len(work_items))) |
1092 | 121 | if args.dry_run or should_continue.lower() == "y": | 121 | if args.dry_run or should_continue.lower() == "y": |
1095 | 122 | print "Creating cards from work items in lane {lane}.".format( | 122 | print(u"Creating cards from work items in lane {lane}.".format( |
1096 | 123 | lane=args.target_lane) | 123 | lane=args.target_lane)) |
1097 | 124 | cards = work_items_to_cards( | 124 | cards = work_items_to_cards( |
1098 | 125 | work_items, board, lane, lp_users, dry_run=args.dry_run) | 125 | work_items, board, lane, lp_users, dry_run=args.dry_run) |
1100 | 126 | print "{count} cards created".format(count=len(cards)) | 126 | print(u"{count} cards created").format(count=len(cards)) |
1101 | 127 | else: | 127 | else: |
1103 | 128 | print "Aborting." | 128 | print(u"Aborting.") |
1104 | 129 | 129 | ||
1105 | 130 | if __name__ == '__main__': | 130 | if __name__ == '__main__': |
1106 | 131 | main(sys.argv) | 131 | main(sys.argv) |
Command: http_proxy= $CI_PROXY https_proxy= $CI_PROXY make ci-test /ci.lscape. net/job/ latch-test/ 10190/
Result: Success
Revno: 178
Branch: lp:~davidpbritton/lp2kanban/lint-1
Jenkins: https:/