Merge lp:~dpb/lp2kanban/lint-1 into lp:lp2kanban

Proposed by David Britton
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
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 :

Command: http_proxy=$CI_PROXY https_proxy=$CI_PROXY make ci-test
Result: Success
Revno: 178
Branch: lp:~davidpbritton/lp2kanban/lint-1
Jenkins: https://ci.lscape.net/job/latch-test/10190/

review: Approve (test results)
Revision history for this message
Alberto Donato (ack) wrote :

nice! +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2016-08-29 20:19:31 +0000
+++ Makefile 2016-09-08 22:02:56 +0000
@@ -61,4 +61,7 @@
61 . venv/bin/activate; \61 . venv/bin/activate; \
62 $(MAKE) check62 $(MAKE) check
6363
64.PHONY: check default configs needs-xdg-utils clean ci-test64lint:
65 flake8 src/lp2kanban
66
67.PHONY: check default configs needs-xdg-utils clean ci-test lint
6568
=== modified file 'src/lp2kanban/bugs2cards.py'
--- src/lp2kanban/bugs2cards.py 2016-09-08 22:02:56 +0000
+++ src/lp2kanban/bugs2cards.py 2016-09-08 22:02:56 +0000
@@ -141,7 +141,7 @@
141 self.lp_to_group.update(parse_groups_config(groups_config))141 self.lp_to_group.update(parse_groups_config(groups_config))
142142
143 self.feature_lanes = {}143 self.feature_lanes = {}
144 new_lane = config.get("new_lanes")144 new_lane = config.get("new_lanes")
145 if not new_lane:145 if not new_lane:
146 return146 return
147 if "${group}" in new_lane:147 if "${group}" in new_lane:
@@ -149,7 +149,7 @@
149 for group in set(self.lp_to_group.values()):149 for group in set(self.lp_to_group.values()):
150 lane_path = new_lane.replace("${group}", group)150 lane_path = new_lane.replace("${group}", group)
151 lane = board.getLaneByPath(lane_path)151 lane = board.getLaneByPath(lane_path)
152 group_tag = "squad-%s" % group.lower().replace(" ","-")152 group_tag = "squad-%s" % group.lower().replace(" ", "-")
153 self.feature_lanes[group_tag] = lane153 self.feature_lanes[group_tag] = lane
154 for card in board.getFeatureCards():154 for card in board.getFeatureCards():
155 if not card.tags:155 if not card.tags:
@@ -192,6 +192,7 @@
192IGNORE_STATUSES = ('Superseded',)192IGNORE_STATUSES = ('Superseded',)
193FINAL_STATUSES = ('Approved', 'Merged')193FINAL_STATUSES = ('Approved', 'Merged')
194194
195
195def get_lp_bug(card, launchpad):196def get_lp_bug(card, launchpad):
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.
197198
@@ -273,7 +274,7 @@
273274
274 for card in feature_card.lane.board.cards:275 for card in feature_card.lane.board.cards:
275 if card_type and card.type.name != card_type:276 if card_type and card.type.name != card_type:
276 continue277 continue
277 if card.tags:278 if card.tags:
278 card_tags = set(card.tags.split(","))279 card_tags = set(card.tags.split(","))
279 if feature_tags.intersection(card_tags):280 if feature_tags.intersection(card_tags):
@@ -304,7 +305,7 @@
304 mps = []305 mps = []
305 has_inprogress_branches = False306 has_inprogress_branches = False
306 for branch in branches:307 for branch in branches:
307 if hasattr(branch, 'branch') :308 if hasattr(branch, 'branch'):
308 branch = branch.branch309 branch = branch.branch
309 for mp in branch.landing_targets:310 for mp in branch.landing_targets:
310 mp_info = Record(rank=None, status=None, mp=None)311 mp_info = Record(rank=None, status=None, mp=None)
@@ -319,7 +320,7 @@
319 mps.append(mp_info)320 mps.append(mp_info)
320 if len(mps) == 0:321 if len(mps) == 0:
321 return None322 return None
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)
323 if not has_inprogress_branches:324 if not has_inprogress_branches:
324 return mps[0] # Return highest-ranked and most-recent MP325 return mps[0] # Return highest-ranked and most-recent MP
325 for mp in mps:326 for mp in mps:
@@ -395,7 +396,7 @@
395 return False396 return False
396 if card.tags and "no-sync" in set(card.tags.split(",")):397 if card.tags and "no-sync" in set(card.tags.split(",")):
397 return False398 return False
398 399
399 enabled = conf.get('move_cards', 'off') == 'on'400 enabled = conf.get('move_cards', 'off') == 'on'
400 active = not card.lane.board.is_archived401 active = not card.lane.board.is_archived
401 return enabled and active402 return enabled and active
@@ -498,7 +499,7 @@
498 status = CardStatus.LANDING499 status = CardStatus.LANDING
499 elif branch_card_status:500 elif branch_card_status:
500 return branch_card_status501 return branch_card_status
501 502
502 elif bug_status in DONE_FIX_BUG_STATUSES:503 elif bug_status in DONE_FIX_BUG_STATUSES:
503 status = CardStatus.DONE_FIX504 status = CardStatus.DONE_FIX
504 elif bug_status in DONE_NOFIX_BUG_STATUSES:505 elif bug_status in DONE_NOFIX_BUG_STATUSES:
@@ -512,7 +513,7 @@
512 if task.target in all_projects:513 if task.target in all_projects:
513 return True514 return True
514 if (hasattr(task.target, "project") and515 if (hasattr(task.target, "project") and
515 task.target.project in all_projects):516 task.target.project in all_projects):
516 return True517 return True
517 return False518 return False
518519
@@ -525,13 +526,13 @@
525 for task in tasks:526 for task in tasks:
526 # Ignore bug task status for series targets527 # Ignore bug task status for series targets
527 if (task.target.resource_type_link == RESOURCE_TYPE_LINK_SERIES and528 if (task.target.resource_type_link == RESOURCE_TYPE_LINK_SERIES and
528 task.target.project in all_projects):529 task.target.project in all_projects):
529 continue # Skip series task status for our projects530 continue # Skip series task status for our projects
530 if (task.assignee is not None and531 if (task.assignee is not None and
531 task.assignee.name not in all_users):532 task.assignee.name not in all_users):
532 continue # Skip non-users tasks533 continue # Skip non-users tasks
533 if (task.target in all_projects or534 if (task.target in all_projects or
534 task.assignee is not None):535 task.assignee is not None):
535 matches = True536 matches = True
536 if (lowest_status is None or537 if (lowest_status is None or
537 (ORDERED_BUG_STATUSES.index(task.status) <538 (ORDERED_BUG_STATUSES.index(task.status) <
@@ -555,7 +556,6 @@
555 return possible_target_lane556 return possible_target_lane
556557
557558
558
559def find_card_target_lane_next(lane, path):559def find_card_target_lane_next(lane, path):
560 """Find the target lane for the path using the "next" algorithm.560 """Find the target lane for the path using the "next" algorithm.
561561
@@ -723,7 +723,7 @@
723 assignee = Record(name=owner_name)723 assignee = Record(name=owner_name)
724 try:724 try:
725 move_card(card, card_status, bconf, [assignee], lp_users)725 move_card(card, card_status, bconf, [assignee], lp_users)
726 except IOError as e:726 except IOError:
727 log.exception("Error moving card: %s (in %s)" % (727 log.exception("Error moving card: %s (in %s)" % (
728 card.title, card_path))728 card.title, card_path))
729 continue729 continue
@@ -751,7 +751,8 @@
751 continue751 continue
752 ignore_external_projects = (752 ignore_external_projects = (
753 bconf.get("autosync") == "active-projects")753 bconf.get("autosync") == "active-projects")
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(
755 lp_bug, lp_projects):
755 continue # Skip bugs in external projects756 continue # Skip bugs in external projects
756 synced, card_status, assignees, mp_url, mp_type = get_bug_status(757 synced, card_status, assignees, mp_url, mp_type = get_bug_status(
757 lp_bug, lp_projects, lp_users.lp_to_kanban.keys())758 lp_bug, lp_projects, lp_users.lp_to_kanban.keys())
@@ -766,7 +767,7 @@
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):
767 try:768 try:
768 move_card(card, card_status, bconf, assignees, lp_users)769 move_card(card, card_status, bconf, assignees, lp_users)
769 except IOError as e:770 except IOError:
770 log.exception("Error Moving: lp:%s: %s (in %s)" % (771 log.exception("Error Moving: lp:%s: %s (in %s)" % (
771 lp_bug.id, card.title, card_path))772 lp_bug.id, card.title, card_path))
772 continue773 continue
@@ -878,7 +879,8 @@
878 return879 return
879880
880 log.info(881 log.info(
881 u" * Moving card#{}: {} -> {}".format(card.id, card.lane.path, target_lane_path))882 u" * Moving card#{}: {} -> {}".format(
883 card.id, card.lane.path, target_lane_path))
882 card.move(target_lane)884 card.move(target_lane)
883885
884886
@@ -896,8 +898,8 @@
896 # happens.898 # happens.
897 try:899 try:
898 card.save()900 card.save()
899 except IOError as e:901 except IOError:
900 log.exception(u"Exception caught. Skipping board.\n"902 log.exception("Exception caught. Skipping board.\n"
901 " desc:{}\n"903 " desc:{}\n"
902 " kanban id:{}\n"904 " kanban id:{}\n"
903 " LP bug id:{}\n".format(905 " LP bug id:{}\n".format(
@@ -937,7 +939,8 @@
937939
938 if args.debug:940 if args.debug:
939 logging.basicConfig(941 logging.basicConfig(
940 level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s')942 level=logging.DEBUG,
943 format='%(asctime)s %(levelname)s %(message)s')
941 import httplib944 import httplib
942 httplib.HTTPConnection.debuglevel = 1945 httplib.HTTPConnection.debuglevel = 1
943 else:946 else:
@@ -975,7 +978,7 @@
975 continue978 continue
976 bconf = boards_config[board_name]979 bconf = boards_config[board_name]
977 process_board(board, bconf, args.bug, args.card)980 process_board(board, bconf, args.bug, args.card)
978 except (KeyError, AssertionError) as e:981 except (KeyError, AssertionError):
979 log.exception("Exception caught. Skipping board.")982 log.exception("Exception caught. Skipping board.")
980 continue983 continue
981984
982985
=== modified file 'src/lp2kanban/cards2workitems.py'
--- src/lp2kanban/cards2workitems.py 2013-06-19 19:44:20 +0000
+++ src/lp2kanban/cards2workitems.py 2016-09-08 22:02:56 +0000
@@ -13,9 +13,9 @@
13# support POSTPONED work items in this mapping, since the Kanban board13# support POSTPONED work items in this mapping, since the Kanban board
14# has no such concept.14# has no such concept.
15LANE_TYPE_TO_WORK_ITEM_STATUS_MAP = {15LANE_TYPE_TO_WORK_ITEM_STATUS_MAP = {
16 1: u"TODO", # Kanban type "Ready"16 1: u"TODO", # Kanban type "Ready"
17 2: u"INPROGRESS", # Kanban type "In process"17 2: u"INPROGRESS", # Kanban type "In process"
18 3: u"DONE", # Kanban type "Complete"18 3: u"DONE", # Kanban type "Complete"
19 }19 }
2020
2121
@@ -34,7 +34,7 @@
34 card.description_annotations.get('blueprint_url') or34 card.description_annotations.get('blueprint_url') or
35 card.external_system_url)35 card.external_system_url)
36 if (blueprint_url is None or not36 if (blueprint_url is None or not
37 blueprint_url_pattern.match(blueprint_url)):37 blueprint_url_pattern.match(blueprint_url)):
38 # Return early if the card isn't linked to a blueprint, since38 # Return early if the card isn't linked to a blueprint, since
39 # there's no point doing the conversion if we're not going to39 # there's no point doing the conversion if we're not going to
40 # sync it.40 # sync it.
@@ -118,8 +118,8 @@
118 work_items_text = "\n".join(work_items)118 work_items_text = "\n".join(work_items)
119 blueprint = fetch_blueprint(lp, blueprint_url)119 blueprint = fetch_blueprint(lp, blueprint_url)
120 if blueprint is not None:120 if blueprint is not None:
121 print "Adding %s work items to %s" % (121 print("Adding %s work items to %s" % (
122 len(work_items), blueprint.name)122 len(work_items), blueprint.name))
123 blueprint.workitems_text = work_items_text123 blueprint.workitems_text = work_items_text
124 blueprint.lp_save()124 blueprint.lp_save()
125125
126126
=== modified file 'src/lp2kanban/kanban.py'
--- src/lp2kanban/kanban.py 2016-09-08 22:02:56 +0000
+++ src/lp2kanban/kanban.py 2016-09-08 22:02:56 +0000
@@ -3,7 +3,6 @@
33
4import requests4import requests
5import json5import json
6import logging
7import operator6import operator
8from pprint import pprint7from pprint import pprint
9import re8import re
@@ -112,14 +111,14 @@
112111
113 resp = request.response112 resp = request.response
114 if (not sent or113 if (not sent or
115 resp.status_code not in LeankitResponseCodes.SUCCESS_CODES):114 resp.status_code not in LeankitResponseCodes.SUCCESS_CODES):
116 print "Error from kanban"115 print("Error from kanban")
117 pprint(resp)116 pprint(resp)
118 raise IOError('kanban error %d' % (resp.status_code))117 raise IOError('kanban error %d' % (resp.status_code))
119 response = Record(json.loads(resp.content))118 response = Record(json.loads(resp.content))
120119
121 if (handle_errors and120 if (handle_errors and
122 response.ReplyCode not in LeankitResponseCodes.SUCCESS_CODES):121 response.ReplyCode not in LeankitResponseCodes.SUCCESS_CODES):
123 raise IOError('kanban error %d: %s' % (122 raise IOError('kanban error %d: %s' % (
124 response.ReplyCode, response.ReplyText))123 response.ReplyCode, response.ReplyText))
125 return response124 return response
@@ -166,7 +165,7 @@
166 def _prettifyName(self, camelcase):165 def _prettifyName(self, camelcase):
167 camelcase = camelcase.replace('ID', '_id')166 camelcase = camelcase.replace('ID', '_id')
168 if len(camelcase) > 1:167 if len(camelcase) > 1:
169 repl_func = lambda match: '_' + match.group(1).lower()168 def repl_func(match): return '_' + match.group(1).lower()
170 camelcase = camelcase[0].lower() + camelcase[1:]169 camelcase = camelcase[0].lower() + camelcase[1:]
171 return re.sub('([A-Z])', repl_func, camelcase)170 return re.sub('([A-Z])', repl_func, camelcase)
172 else:171 else:
@@ -174,7 +173,7 @@
174173
175 def _toCamelCase(self, name):174 def _toCamelCase(self, name):
176 if len(name) > 1:175 if len(name) > 1:
177 repl_func = lambda match: match.group(1)[1:].upper()176 def repl_func(match): return match.group(1)[1:].upper()
178 name = name[0].upper() + name[1:]177 name = name[0].upper() + name[1:]
179 return re.sub('(_[a-z])', repl_func, name)178 return re.sub('(_[a-z])', repl_func, name)
180 else:179 else:
@@ -236,9 +235,9 @@
236 # no-op.235 # no-op.
237 return236 return
238 data = self._raw_data237 data = self._raw_data
239 data["UserWipOverrideComment"] = None;238 data["UserWipOverrideComment"] = None
240 if ("AssignedUsers" in data and239 if ("AssignedUsers" in data and
241 "assigned_user_id" not in self.dirty_attrs):240 "assigned_user_id" not in self.dirty_attrs):
242 if 'AssignedUserId' in data.keys():241 if 'AssignedUserId' in data.keys():
243 del data['AssignedUserId']242 del data['AssignedUserId']
244 if 'AssignedUserName' in data.keys():243 if 'AssignedUserName' in data.keys():
@@ -247,7 +246,6 @@
247 lambda X: X['AssignedUserId'], data['AssignedUsers'])246 lambda X: X['AssignedUserId'], data['AssignedUsers'])
248247
249 for attr in self.dirty_attrs:248 for attr in self.dirty_attrs:
250 #print "Storing %s in %s..." % (attr, self._toCamelCase(attr))
251 data[self._toCamelCase(attr)] = getattr(self, attr)249 data[self._toCamelCase(attr)] = getattr(self, attr)
252250
253 board_id = str(self.lane.board.id)251 board_id = str(self.lane.board.id)
@@ -380,7 +378,6 @@
380 self.external_card_id = src.external_card_id378 self.external_card_id = src.external_card_id
381 self.assigned_user_id = src.assigned_user_id379 self.assigned_user_id = src.assigned_user_id
382380
383
384 @property381 @property
385 def parsed_description(self):382 def parsed_description(self):
386 """Parse the card description to find key=value pairs.383 """Parse the card description to find key=value pairs.
@@ -397,8 +394,8 @@
397 end = match.end()394 end = match.end()
398 try:395 try:
399 annotations = Record(json.loads(self.description[start:end]))396 annotations = Record(json.loads(self.description[start:end]))
400 except ValueError, ex:397 except ValueError as ex:
401 print "Unable to parse card %s: %s" % (self.id, ex.message)398 print("Unable to parse card %s: %s" % (self.id, ex.message))
402 annotations = Record()399 annotations = Record()
403 return (400 return (
404 annotations,401 annotations,
@@ -580,7 +577,7 @@
580 archive_lanes = [lane_dict['Lane'] for lane_dict in self._archive]577 archive_lanes = [lane_dict['Lane'] for lane_dict in self._archive]
581 archive_lanes.extend(578 archive_lanes.extend(
582 [lane_dict['Lane'] for579 [lane_dict['Lane'] for
583 lane_dict in self._archive[0]['ChildLanes']])580 lane_dict in self._archive[0]['ChildLanes']])
584 lanes += archive_lanes581 lanes += archive_lanes
585 lanes += self.connector.get(582 lanes += self.connector.get(
586 "/Kanban/Api/Board/" + str(self.id) + "/Backlog").ReplyData[0]583 "/Kanban/Api/Board/" + str(self.id) + "/Backlog").ReplyData[0]
@@ -594,7 +591,7 @@
594591
595 def _classifyCards(self):592 def _classifyCards(self):
596 """Classify the cards into buckets for lookups later."""593 """Classify the cards into buckets for lookups later."""
597 print " Classifying cards %s cards." % len(self.cards)594 print(" Classifying cards %s cards." % len(self.cards))
598 for card in self.cards:595 for card in self.cards:
599 if card.type.name == "Feature":596 if card.type.name == "Feature":
600 self._feature_cards.add(card)597 self._feature_cards.add(card)
@@ -608,18 +605,18 @@
608 if BRANCH_MP_REGEX.match(card.external_system_url):605 if BRANCH_MP_REGEX.match(card.external_system_url):
609 self._cards_with_branches.add(card)606 self._cards_with_branches.add(card)
610 else:607 else:
611 print ("WARNING: Card won't sync. Non-branch link ({}) "608 print("WARNING: Card won't sync. Non-branch link ({}) "
612 "for {}.".format(url, card.title))609 "for {}.".format(url, card.title))
613 print " - %s feature cards" % len(610 print(" - %s feature cards" % len(
614 self._feature_cards)611 self._feature_cards))
615 print " - %s cards with external ids" % len(612 print(" - %s cards with external ids" % len(
616 self._cards_with_external_ids)613 self._cards_with_external_ids))
617 print " - %s cards with external links" % len(614 print(" - %s cards with external links" % len(
618 self._cards_with_external_links)615 self._cards_with_external_links))
619 print " - %s cards with description annotations" % len(616 print(" - %s cards with description annotations" % len(
620 self._cards_with_description_annotations)617 self._cards_with_description_annotations))
621 print " - %s cards with branches" % len(618 print(" - %s cards with branches" % len(
622 self._cards_with_branches)619 self._cards_with_branches))
623620
624 def _populateFeatureTagPaths(self):621 def _populateFeatureTagPaths(self):
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."""
@@ -672,12 +669,13 @@
672669
673 def getLane(self, lane_id):670 def getLane(self, lane_id):
674 flat_lanes = {}671 flat_lanes = {}
672
675 def flatten_lane(lane):673 def flatten_lane(lane):
676 flat_lanes[lane.id] = lane674 flat_lanes[lane.id] = lane
677 for child in lane.child_lanes:675 for child in lane.child_lanes:
678 flatten_lane(child)676 flatten_lane(child)
679 map(flatten_lane, self.root_lane.child_lanes)677 map(flatten_lane, self.root_lane.child_lanes)
680 return flat_lanes[lane_id];678 return flat_lanes[lane_id]
681679
682 def getLaneByTitle(self, title):680 def getLaneByTitle(self, title):
683 if len(self.root_lane.child_lanes) > 0:681 if len(self.root_lane.child_lanes) > 0:
@@ -689,7 +687,7 @@
689 else:687 else:
690 for child in lane.child_lanes:688 for child in lane.child_lanes:
691 result = self._getLaneByTitle(child, title)689 result = self._getLaneByTitle(child, title)
692 if result != None:690 if result is not None:
693 return result691 return result
694 return None692 return None
695693
@@ -698,7 +696,7 @@
698 return self._getLaneByPath(self.root_lane, path, ignorecase)696 return self._getLaneByPath(self.root_lane, path, ignorecase)
699697
700 def _getLaneByPath(self, lane, path, ignorecase):698 def _getLaneByPath(self, lane, path, ignorecase):
701 if ignorecase == True:699 if ignorecase is True:
702 if lane.path.lower() == path.lower():700 if lane.path.lower() == path.lower():
703 return lane701 return lane
704 else:702 else:
@@ -707,24 +705,23 @@
707705
708 for child in lane.child_lanes:706 for child in lane.child_lanes:
709 result = self._getLaneByPath(child, path, ignorecase)707 result = self._getLaneByPath(child, path, ignorecase)
710 if result != None:708 if result is not None:
711 return result709 return result
712 return None710 return None
713711
714
715 def _printLanes(self, lane, indent, include_cards=False):712 def _printLanes(self, lane, indent, include_cards=False):
716 next_lane = lane.getNextLanes()713 next_lane = lane.getNextLanes()
717 if next_lane is None:714 if next_lane is None:
718 next_lane = ''715 next_lane = ''
719 else:716 else:
720 next_lane = (' (next: any of [' +717 next_lane = (' (next: any of [' +
721 ', '.join([my_lane.path for my_lane in next_lane])718 ', '.join([my_lane.path for my_lane in next_lane]) +
722 + '])')719 '])')
723 next_lane += ' - %d cards' % len(lane.cards)720 next_lane += ' - %d cards' % len(lane.cards)
724 print " " * indent + "* " + lane.title + next_lane721 print(" " * indent + "* " + lane.title + next_lane)
725 for card in lane.cards:722 for card in lane.cards:
726 print (" " * (indent + 1) + "- #" + card.external_card_id +723 print(" " * (indent + 1) + "- #" + card.external_card_id +
727 ': ' + card.title)724 ': ' + card.title)
728 for child in lane.child_lanes:725 for child in lane.child_lanes:
729 self._printLanes(child, indent + 1)726 self._printLanes(child, indent + 1)
730727
@@ -732,7 +729,7 @@
732 """Recursively prints all the lanes in the board with indentation."""729 """Recursively prints all the lanes in the board with indentation."""
733 if len(self.root_lane.child_lanes) == 0:730 if len(self.root_lane.child_lanes) == 0:
734 return731 return
735 print "Board lanes:"732 print("Board lanes:")
736 indent = 1733 indent = 1
737 for lane in self.root_lane.child_lanes:734 for lane in self.root_lane.child_lanes:
738 self._printLanes(lane, indent, include_cards)735 self._printLanes(lane, indent, include_cards)
@@ -818,16 +815,16 @@
818 kanban = LeankitKanban('launchpad.leankitkanban.com',815 kanban = LeankitKanban('launchpad.leankitkanban.com',
819 'user@email', 'password')816 'user@email', 'password')
820817
821 print "Active boards:"818 print("Active boards:")
822 boards = kanban.getBoards()819 boards = kanban.getBoards()
823 for board in boards:820 for board in boards:
824 print " * %s (%d)" % (board.title, board.id)821 print(" * %s (%d)" % (board.title, board.id))
825822
826 # Get a board by the title.823 # Get a board by the title.
827 board_name = 'Landscape 2016'824 board_name = 'Landscape 2016'
828 print "Getting board '%s'..." % board_name825 print("Getting board '%s'..." % board_name)
829 board = kanban.getBoard(title=board_name)826 board = kanban.getBoard(title=board_name)
830 board.printLanes()827 board.printLanes()
831828
832 # Print all users.829 # Print all users.
833 print board.users830 print(board.users)
834831
=== modified file 'src/lp2kanban/tests/test_bugs2cards.py'
--- src/lp2kanban/tests/test_bugs2cards.py 2016-09-08 22:02:56 +0000
+++ src/lp2kanban/tests/test_bugs2cards.py 2016-09-08 22:02:56 +0000
@@ -1,8 +1,6 @@
1# Copyright 2011-2012 Canonical Ltd1# Copyright 2011-2012 Canonical Ltd
2"""Tests for the bugs2cards.py script."""2"""Tests for the bugs2cards.py script."""
33
4__metaclass__ = type
5
6from ConfigParser import (4from ConfigParser import (
7 ConfigParser,5 ConfigParser,
8 NoSectionError,6 NoSectionError,
@@ -38,12 +36,9 @@
38 )36 )
39from lp2kanban.kanban import (37from lp2kanban.kanban import (
40 RESOURCE_TYPE_LINK_SERIES,38 RESOURCE_TYPE_LINK_SERIES,
41 LeankitCard,
42 Record,39 Record,
43 )40 )
44from lp2kanban.tests.common import (41from lp2kanban.tests.common import (
45 FauxLeankitUser,
46 FauxLaunchpadUser,
47 FauxLaunchpadUsersForBoard,42 FauxLaunchpadUsersForBoard,
48 FauxBoard,43 FauxBoard,
49 FauxCard,44 FauxCard,
@@ -51,6 +46,8 @@
51 FauxLane,46 FauxLane,
52 )47 )
5348
49__metaclass__ = type
50
5451
55def parse_config_text(text):52def parse_config_text(text):
56 config = ConfigParser()53 config = ConfigParser()
@@ -90,8 +87,7 @@
90 globals, boards = parse_config(config)87 globals, boards = parse_config(config)
9188
92 self.assertEqual({'var1': 'value1',89 self.assertEqual({'var1': 'value1',
93 'var2': 'value2',90 'var2': 'value2'}, globals)
94 }, globals)
95 self.assertEqual({'Board 1': {'var1': 'board value 1'}}, boards)91 self.assertEqual({'Board 1': {'var1': 'board value 1'}}, boards)
9692
97 def test_parse_config_defaults(self):93 def test_parse_config_defaults(self):
@@ -131,7 +127,9 @@
131127
132128
133class FauxMP:129class FauxMP:
134 def __init__(self, queue_status, target_name, link=None, description=None, date_created=None):130 def __init__(
131 self, queue_status, target_name, link=None, description=None,
132 date_created=None):
135 self.queue_status = queue_status133 self.queue_status = queue_status
136 self.target_branch = Record(name=target_name)134 self.target_branch = Record(name=target_name)
137 self.web_link = link135 self.web_link = link
@@ -145,7 +143,7 @@
145 self.projects = data.get("projects", {})143 self.projects = data.get("projects", {})
146 self.project_group = data.get("project_groups", {})144 self.project_group = data.get("project_groups", {})
147 self.bugs = data.get("bugs", {})145 self.bugs = data.get("bugs", {})
148 146
149147
150class BranchInfoTests(unittest.TestCase):148class BranchInfoTests(unittest.TestCase):
151149
@@ -296,7 +294,7 @@
296 def __init__(self, resource_type_link="http://api/project", project=None):294 def __init__(self, resource_type_link="http://api/project", project=None):
297 self.resource_type_link = resource_type_link295 self.resource_type_link = resource_type_link
298 self.project = project296 self.project = project
299 297
300298
301class FauxBugTask:299class FauxBugTask:
302300
@@ -312,23 +310,25 @@
312class IsBugInProjectsTest(unittest.TestCase):310class IsBugInProjectsTest(unittest.TestCase):
313311
314 def test_false_when_part_of_external_project(self):312 def test_false_when_part_of_external_project(self):
315 # Return False when the bug is part of an unconfigured (external) project313 # Return False when the bug is part of an unconfigured (external)
314 # project
316 bug = FauxBug()315 bug = FauxBug()
317 project = FauxTarget(resource_type_link="project", project="otherproject")316 project = FauxTarget(
317 resource_type_link="project", project="otherproject")
318 bug.addTask(project)318 bug.addTask(project)
319 self.assertFalse(is_bug_in_projects(bug, ["myproject1", "myproject2"]))319 self.assertFalse(is_bug_in_projects(bug, ["myproject1", "myproject2"]))
320320
321 def test_true_when_part_of_configured_projects(self):321 def test_true_when_part_of_configured_projects(self):
322 # Return True when the bug is one of the configured (internal) projects322 # Return True when the bug is one of the configured (internal) projects
323 bug = FauxBug()323 bug = FauxBug()
324 project = FauxTarget(resource_type_link="project", project="myproject2")324 project = FauxTarget(
325 resource_type_link="project", project="myproject2")
325 bug.addTask(project)326 bug.addTask(project)
326 self.assertTrue(is_bug_in_projects(bug, ["myproject1", "myproject2"]))327 self.assertTrue(is_bug_in_projects(bug, ["myproject1", "myproject2"]))
327328
328
329 def test_true_when_series_within_a_configured_project(self):329 def test_true_when_series_within_a_configured_project(self):
330 # Return True when the bug has a series task that is related to one of the330 # Return True when the bug has a series task that is related to one
331 # configured projects.331 # of the configured projects.
332 bug = FauxBug()332 bug = FauxBug()
333 series = FauxTarget(333 series = FauxTarget(
334 resource_type_link=RESOURCE_TYPE_LINK_SERIES, project="myproject1")334 resource_type_link=RESOURCE_TYPE_LINK_SERIES, project="myproject1")
@@ -407,7 +407,8 @@
407 # than 'Fix Committed').407 # than 'Fix Committed').
408 bug = FauxBug()408 bug = FauxBug()
409 project1 = FauxTarget(resource_type_link="project", project="lazr")409 project1 = FauxTarget(resource_type_link="project", project="lazr")
410 project2 = FauxTarget(resource_type_link="project", project="launchpad")410 project2 = FauxTarget(
411 resource_type_link="project", project="launchpad")
411 bug.addTask(project1, status='Fix Released')412 bug.addTask(project1, status='Fix Released')
412 bug.addTask(project2, status='In Progress')413 bug.addTask(project2, status='In Progress')
413 matches, status, assignees, mp_url, mp_type = get_bug_status(414 matches, status, assignees, mp_url, mp_type = get_bug_status(
@@ -583,8 +584,9 @@
583 self.assertTrue(should_sync_card(card, {'autosync': 'on',584 self.assertTrue(should_sync_card(card, {'autosync': 'on',
584 'sync_cards': 'on'}))585 'sync_cards': 'on'}))
585586
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):
587 # When autosync is 'active-projects', cards with external card IDs are synced.588 # When autosync is 'active-projects', cards with external card
589 # IDs are synced.
588 card = Record(title=u'no sync', description=u'',590 card = Record(title=u'no sync', description=u'',
589 external_card_id=u'11', external_system_url=u'')591 external_card_id=u'11', external_system_url=u'')
590 self.assertTrue(should_sync_card(card, {'autosync': 'active-projects',592 self.assertTrue(should_sync_card(card, {'autosync': 'active-projects',
@@ -663,8 +665,8 @@
663 self.assertFalse(should_sync_card(card2, conf))665 self.assertFalse(should_sync_card(card2, conf))
664666
665 def test_get_card_status_new_no_branch(self):667 def test_get_card_status_new_no_branch(self):
666 # For a bug in 'New', 'Triaged' or 'Confirmed' or 'Incomplete' statuses,668 # For a bug in 'New', 'Triaged' or 'Confirmed' or
667 # the branch status is new state.669 # 'Incomplete' statuses the branch status is new state.
668 no_branch_info = Record(status=None)670 no_branch_info = Record(status=None)
669 self.assertEqual(671 self.assertEqual(
670 CardStatus.NEW,672 CardStatus.NEW,
@@ -681,29 +683,29 @@
681683
682 def test_get_card_status_coding_no_bug(self):684 def test_get_card_status_coding_no_bug(self):
683 # For a card with no associated bug, an 'In Progress' branch status685 # For a card with no associated bug, an 'In Progress' branch status
684 # will be in the coding state. 686 # will be in the coding state.
685 branch_info = Record(status='In Progress', target=None)687 branch_info = Record(status='In Progress', target=None)
686 self.assertEqual(688 self.assertEqual(
687 CardStatus.CODING, 689 CardStatus.CODING,
688 get_card_status(None, [], branch_info))690 get_card_status(None, [], branch_info))
689691
690 def test_get_card_status_review_no_bug(self):692 def test_get_card_status_review_no_bug(self):
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'
692 # branch status will be in the review state. 694 # branch status will be in the review state.
693 branch_infos = [695 branch_infos = [
694 Record(status='In Review', target=None),696 Record(status='In Review', target=None),
695 Record(status='Approved', target=None)]697 Record(status='Approved', target=None)]
696 for branch_info in branch_infos:698 for branch_info in branch_infos:
697 self.assertEqual(699 self.assertEqual(
698 CardStatus.REVIEW, 700 CardStatus.REVIEW,
699 get_card_status(None, [], branch_info))701 get_card_status(None, [], branch_info))
700702
701 def test_get_card_status_landing_no_bug(self):703 def test_get_card_status_landing_no_bug(self):
702 # For a card with no associated bug, a merged branch status will be in704 # For a card with no associated bug, a merged branch status will be in
703 # the landing state. 705 # the landing state.
704 branch_info = Record(status='Merged', target=None)706 branch_info = Record(status='Merged', target=None)
705 self.assertEqual(707 self.assertEqual(
706 CardStatus.LANDING, 708 CardStatus.LANDING,
707 get_card_status(None, [], branch_info))709 get_card_status(None, [], branch_info))
708710
709 def test_get_card_status_coding_in_progress_no_branch(self):711 def test_get_card_status_coding_in_progress_no_branch(self):
@@ -888,7 +890,7 @@
888 cards = get_cards_for_feature(890 cards = get_cards_for_feature(
889 self.feature_card, self.feature_bug, card_type='Branch')891 self.feature_card, self.feature_bug, card_type='Branch')
890 self.assertItemsEqual([branch_card], cards)892 self.assertItemsEqual([branch_card], cards)
891 893
892894
893class FindLaneNextTest(unittest.TestCase):895class FindLaneNextTest(unittest.TestCase):
894896
@@ -980,7 +982,7 @@
980 def test_no_bugs(self):982 def test_no_bugs(self):
981 # If no bugs are found, no cards are created.983 # If no bugs are found, no cards are created.
982 board = FauxBoard()984 board = FauxBoard()
983 bug_lane = board.addLane('bugs')985 board.addLane('bugs')
984 project = FauxLaunchpadProject(bug_tasks=[])986 project = FauxLaunchpadProject(bug_tasks=[])
985 create_cards_in_project(board, project, 'kanban')987 create_cards_in_project(board, project, 'kanban')
986 self.assertEqual([], board.cards)988 self.assertEqual([], board.cards)
@@ -989,7 +991,7 @@
989 # If a new bug is found, a card is created for it, having the991 # If a new bug is found, a card is created for it, having the
990 # bug id, title and description saved.992 # bug id, title and description saved.
991 board = FauxBoard()993 board = FauxBoard()
992 bug_lane = board.addLane('bugs')994 board.addLane('bugs')
993 project = FauxLaunchpadProject(995 project = FauxLaunchpadProject(
994 bug_tasks=[FauxBugTask(996 bug_tasks=[FauxBugTask(
995 bug_id=42, title='Test bug',997 bug_id=42, title='Test bug',
@@ -1007,7 +1009,7 @@
1007 # After a card is created from a bug, the bug tag is removed, so1009 # After a card is created from a bug, the bug tag is removed, so
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.
1009 board = FauxBoard()1011 board = FauxBoard()
1010 bug_lane = board.addLane('bugs')1012 board.addLane('bugs')
1011 project = FauxLaunchpadProject(1013 project = FauxLaunchpadProject(
1012 bug_tasks=[FauxBugTask(1014 bug_tasks=[FauxBugTask(
1013 bug_id=42, title='Test bug',1015 bug_id=42, title='Test bug',
@@ -1038,7 +1040,7 @@
1038 # Cards are created only for bugs having the bug tag that is1040 # Cards are created only for bugs having the bug tag that is
1039 # passed in to create_cards_in_project().1041 # passed in to create_cards_in_project().
1040 board = FauxBoard()1042 board = FauxBoard()
1041 bug_lane = board.addLane('bugs')1043 board.addLane('bugs')
1042 project = FauxLaunchpadProject(1044 project = FauxLaunchpadProject(
1043 bug_tasks=[FauxBugTask(1045 bug_tasks=[FauxBugTask(
1044 bug_id=21, title='Tagged with other tag',1046 bug_id=21, title='Tagged with other tag',
@@ -1058,7 +1060,7 @@
1058 # It's possible to specify which card type that the created card1060 # It's possible to specify which card type that the created card
1059 # should have, instead of the default one.1061 # should have, instead of the default one.
1060 board = FauxBoard()1062 board = FauxBoard()
1061 bug_lane = board.addLane('bugs')1063 board.addLane('bugs')
1062 project = FauxLaunchpadProject(1064 project = FauxLaunchpadProject(
1063 bug_tasks=[FauxBugTask(1065 bug_tasks=[FauxBugTask(
1064 bug_id=42, title='Test bug',1066 bug_id=42, title='Test bug',
@@ -1066,7 +1068,8 @@
1066 tags=['kanban'])])1068 tags=['kanban'])])
1067 feature_type = FauxCardType(id=2, name='Feature', is_default=False)1069 feature_type = FauxCardType(id=2, name='Feature', is_default=False)
1068 board.cardtypes[feature_type.id] = feature_type1070 board.cardtypes[feature_type.id] = feature_type
1069 create_cards_in_project(board, project, 'kanban', cardtype_name='Feature')1071 create_cards_in_project(
1072 board, project, 'kanban', cardtype_name='Feature')
1070 [card] = board.cards1073 [card] = board.cards
1071 self.assertEqual(feature_type.id, card.type_id)1074 self.assertEqual(feature_type.id, card.type_id)
1072 self.assertTrue(card.saved)1075 self.assertTrue(card.saved)
@@ -1075,8 +1078,8 @@
1075 # It's possible to specify to which lane the card should be1078 # It's possible to specify to which lane the card should be
1076 # added.1079 # added.
1077 board = FauxBoard()1080 board = FauxBoard()
1078 bug_lane = board.addLane('bugs')1081 board.addLane('bugs')
1079 bug_lane = board.addLane('incoming')1082 board.addLane('incoming')
1080 project = FauxLaunchpadProject(1083 project = FauxLaunchpadProject(
1081 bug_tasks=[FauxBugTask(1084 bug_tasks=[FauxBugTask(
1082 bug_id=42, title='Test bug',1085 bug_id=42, title='Test bug',
@@ -1094,7 +1097,7 @@
1094 # Bug descriptions that exceed the 19997 character limit of a1097 # Bug descriptions that exceed the 19997 character limit of a
1095 # kanban card are truncated to 19500 bytes.1098 # kanban card are truncated to 19500 bytes.
1096 board = FauxBoard()1099 board = FauxBoard()
1097 bug_lane = board.addLane('bugs')1100 board.addLane('bugs')
1098 project = FauxLaunchpadProject(1101 project = FauxLaunchpadProject(
1099 bug_tasks=[FauxBugTask(1102 bug_tasks=[FauxBugTask(
1100 bug_id=42, title='Test bug',1103 bug_id=42, title='Test bug',
@@ -1185,7 +1188,6 @@
1185 parse_groups_config(config))1188 parse_groups_config(config))
11861189
11871190
1188
1189class LaunchpadUsersForBoardTest(unittest.TestCase):1191class LaunchpadUsersForBoardTest(unittest.TestCase):
11901192
1191 def setUp(self):1193 def setUp(self):
@@ -1266,12 +1268,12 @@
1266 lp_users = LaunchpadUsersForBoard()1268 lp_users = LaunchpadUsersForBoard()
1267 lp_users.set_up_users(lp, board, config)1269 lp_users.set_up_users(lp, board, config)
1268 self.assertEqual(1270 self.assertEqual(
1269 {"sometag": coding_lane, "anothertag": coding_lane}, 1271 {"sometag": coding_lane, "anothertag": coding_lane},
1270 lp_users.feature_lanes)1272 lp_users.feature_lanes)
12711273
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):
1273 # When a feature card is present and tagged and the new_lanes1275 # When a feature card is present and tagged and the new_lanes
1274 # in the config specifies the ${group} variable, 1276 # in the config specifies the ${group} variable,
1275 # feature_lanes will be unset when there are no assigned users on the1277 # feature_lanes will be unset when there are no assigned users on the
1276 # card.1278 # card.
1277 feature_card = FauxCard(tags="sometag")1279 feature_card = FauxCard(tags="sometag")
@@ -1305,7 +1307,8 @@
1305 lp = None1307 lp = None
1306 config = {"groups_config_file": groups_file.name,1308 config = {"groups_config_file": groups_file.name,
1307 "new_lanes": "dev::${group}::coding"}1309 "new_lanes": "dev::${group}::coding"}
1308 lp_users = LaunchpadUsersForBoard(kanban_to_lp={1:FauxPerson("User1")})1310 lp_users = LaunchpadUsersForBoard(
1311 kanban_to_lp={1: FauxPerson("User1")})
1309 lp_users.set_up_users(lp, board, config)1312 lp_users.set_up_users(lp, board, config)
1310 self.assertEqual(1313 self.assertEqual(
1311 {"squad-group-1": coding_lane, "squad-group-2": coding_lane2},1314 {"squad-group-1": coding_lane, "squad-group-2": coding_lane2},
@@ -1313,7 +1316,7 @@
13131316
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):
1315 # When a feature card is present and tagged and the new_lanes1318 # When a feature card is present and tagged and the new_lanes
1316 # in the config specifies the ${group} variable, 1319 # in the config specifies the ${group} variable,
1317 # feature_lanes will be set when there are is an assigned user on the1320 # feature_lanes will be set when there are is an assigned user on the
1318 # card.1321 # card.
1319 feature_card = FauxCard(tags="sometag,anothertag")1322 feature_card = FauxCard(tags="sometag,anothertag")
@@ -1336,17 +1339,17 @@
1336 lp = None1339 lp = None
1337 config = {"groups_config_file": groups_file.name,1340 config = {"groups_config_file": groups_file.name,
1338 "new_lanes": "dev::${group}::coding"}1341 "new_lanes": "dev::${group}::coding"}
1339 lp_users = LaunchpadUsersForBoard(kanban_to_lp={1:FauxPerson("User1")})1342 lp_users = LaunchpadUsersForBoard(
1343 kanban_to_lp={1: FauxPerson("User1")})
1340 lp_users.set_up_users(lp, board, config)1344 lp_users.set_up_users(lp, board, config)
1341 self.assertEqual(1345 self.assertEqual(
1342 {"sometag": coding_lane, "anothertag": coding_lane, 1346 {"sometag": coding_lane, "anothertag": coding_lane,
1343 "squad-group-1": coding_lane, "squad-group-2": coding_lane2}, 1347 "squad-group-1": coding_lane, "squad-group-2": coding_lane2},
1344 lp_users.feature_lanes)1348 lp_users.feature_lanes)
13451349
13461350
1347class FauxPerson:1351class FauxPerson:
13481352
1349
1350 def __init__(self, name):1353 def __init__(self, name):
1351 self.name = name1354 self.name = name
13521355
@@ -1654,7 +1657,7 @@
1654 self.lp_users = FauxLaunchpadUsersForBoard()1657 self.lp_users = FauxLaunchpadUsersForBoard()
16551658
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):
1657 """1660 """
1658 Feature-linked cards in landing lanes will move to taskboard when the1661 Feature-linked cards in landing lanes will move to taskboard when the
1659 feature card is in landing_lanes.1662 feature card is in landing_lanes.
1660 """1663 """
@@ -1735,16 +1738,20 @@
1735 self.assertEqual((self.feature_card, 'Done'), landed_card2.moved_to)1738 self.assertEqual((self.feature_card, 'Done'), landed_card2.moved_to)
17361739
1737 def test_sync_cards_external_ids_filter_card(self):1740 def test_sync_cards_external_ids_filter_card(self):
1738 """filter_card skips and selects correctly."""1741 """filter_card skips and selects correctly."""
1739 # Configure bugs2cards to sync only active-projects myproject1 and myproject21742 # Configure bugs2cards to sync only active-projects myproject1
1743 # and myproject2
1740 self.bconf.update({1744 self.bconf.update({
1741 "autosync": "active-projects",1745 "autosync": "active-projects",
1742 "projects": "myproject",1746 "projects": "myproject",
1743 "sync_cards": "on", "move_cards": "on",1747 "sync_cards": "on", "move_cards": "on",
1744 "new_lanes": "new"})1748 "new_lanes": "new"})
1745 # Setup bug1 and bug2 in separate projects which both need to move coding -> new1749 # Setup bug1 and bug2 in separate projects which both need to
1746 project1 = FauxTarget(resource_type_link="project", project="myproject")1750 # move coding -> new
1747 lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"})1751 project1 = FauxTarget(
1752 resource_type_link="project", project="myproject")
1753 lp_users = FauxLaunchpadUsersForBoard(
1754 lp_to_kanban={"person1": "Person One"})
1748 lp_projects = [project1]1755 lp_projects = [project1]
1749 bug_task1 = FauxBugTask(1756 bug_task1 = FauxBugTask(
1750 bug_id=123, target=project1, title='Test bug in myproject',1757 bug_id=123, target=project1, title='Test bug in myproject',
@@ -1766,25 +1773,31 @@
1766 bug_card1.type = bug_type1773 bug_card1.type = bug_type
1767 bug_card2.type = bug_type1774 bug_card2.type = bug_type
1768 self.board.cards.extend([bug_card1, bug_card2])1775 self.board.cards.extend([bug_card1, bug_card2])
1769 lp = FauxLP({"projects": {"myproject": project1},1776 lp = FauxLP({
1770 "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})1777 "projects": {"myproject": project1},
1778 "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})
1771 sync_cards_external_ids(1779 sync_cards_external_ids(
1772 board, self.bconf, lp, lp_users, lp_projects, filter_card=bug_card2.id)1780 board, self.bconf, lp, lp_users,
1781 lp_projects, filter_card=bug_card2.id)
1773 coding_lane = FauxLane(board=board, path='coding')1782 coding_lane = FauxLane(board=board, path='coding')
1774 self.assertEqual(new_lane, bug_card2.moved_to)1783 self.assertEqual(new_lane, bug_card2.moved_to)
1775 self.assertIsNone(bug_card1.moved_to)1784 self.assertIsNone(bug_card1.moved_to)
17761785
1777 def test_sync_cards_skips_bugs_not_in_filter_bug(self):1786 def test_sync_cards_skips_bugs_not_in_filter_bug(self):
1778 """filter_bug skips and selects bugs in sync_cards_external_ids."""1787 """filter_bug skips and selects bugs in sync_cards_external_ids."""
1779 # Configure bugs2cards to sync only active-projects myproject1 and myproject21788 # Configure bugs2cards to sync only active-projects myproject1
1789 # and myproject2
1780 self.bconf.update({1790 self.bconf.update({
1781 "autosync": "active-projects",1791 "autosync": "active-projects",
1782 "projects": "myproject",1792 "projects": "myproject",
1783 "sync_cards": "on", "move_cards": "on",1793 "sync_cards": "on", "move_cards": "on",
1784 "new_lanes": "new"})1794 "new_lanes": "new"})
1785 # Setup bug1 and bug2 in separate projects which both need to move coding -> new1795 # Setup bug1 and bug2 in separate projects which both need
1786 project1 = FauxTarget(resource_type_link="project", project="myproject")1796 # to move coding -> new
1787 lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"})1797 project1 = FauxTarget(
1798 resource_type_link="project", project="myproject")
1799 lp_users = FauxLaunchpadUsersForBoard(
1800 lp_to_kanban={"person1": "Person One"})
1788 lp_projects = [project1]1801 lp_projects = [project1]
1789 bug_task1 = FauxBugTask(1802 bug_task1 = FauxBugTask(
1790 bug_id=123, target=project1, title='Test bug in myproject',1803 bug_id=123, target=project1, title='Test bug in myproject',
@@ -1806,26 +1819,34 @@
1806 bug_card1.type = bug_type1819 bug_card1.type = bug_type
1807 bug_card2.type = bug_type1820 bug_card2.type = bug_type
1808 self.board.cards.extend([bug_card1, bug_card2])1821 self.board.cards.extend([bug_card1, bug_card2])
1809 lp = FauxLP({"projects": {"myproject": project1},1822 lp = FauxLP({
1810 "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})1823 "projects": {"myproject": project1},
1824 "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})
1811 sync_cards_external_ids(1825 sync_cards_external_ids(
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")
1813 coding_lane = FauxLane(board=board, path='coding')1827 coding_lane = FauxLane(board=board, path='coding')
1814 self.assertEqual(new_lane, bug_card2.moved_to)1828 self.assertEqual(new_lane, bug_card2.moved_to)
1815 self.assertIsNone(bug_card1.moved_to)1829 self.assertIsNone(bug_card1.moved_to)
18161830
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):
1818 """Bugs of external projects are skipped when autosync set to active-projects."""1832 """Bugs of external projects are skipped when autosync set to
1819 # Configure bugs2cards to sync only active-projects myproject1 and myproject21833 active-projects."""
1834 # Configure bugs2cards to sync only active-projects myproject1
1835 # and myproject2
1820 self.bconf.update({"autosync": "active-projects",1836 self.bconf.update({"autosync": "active-projects",
1821 "projects": "myproject,myproject2",1837 "projects": "myproject,myproject2",
1822 "sync_cards": "on", "move_cards": "on",1838 "sync_cards": "on", "move_cards": "on",
1823 "new_lanes": "new"})1839 "new_lanes": "new"})
1824 # Setup bug1 and bug2 in separate projects which both need to move coding -> new1840 # Setup bug1 and bug2 in separate projects which both need to move
1825 project1 = FauxTarget(resource_type_link="project", project="myproject")1841 # coding -> new
1826 project2 = FauxTarget(resource_type_link="project2", project="myproject2")1842 project1 = FauxTarget(
1827 otherproject = FauxTarget(resource_type_link="project", project="otherproject")1843 resource_type_link="project", project="myproject")
1828 lp_users = FauxLaunchpadUsersForBoard(lp_to_kanban={"person1": "Person One"})1844 project2 = FauxTarget(
1845 resource_type_link="project2", project="myproject2")
1846 otherproject = FauxTarget(
1847 resource_type_link="project", project="otherproject")
1848 lp_users = FauxLaunchpadUsersForBoard(
1849 lp_to_kanban={"person1": "Person One"})
1829 lp_projects = [project1, project2]1850 lp_projects = [project1, project2]
1830 bug_task1 = FauxBugTask(1851 bug_task1 = FauxBugTask(
1831 bug_id=123, target=project1, title='Test bug in myproject',1852 bug_id=123, target=project1, title='Test bug in myproject',
@@ -1848,8 +1869,9 @@
1848 bug_card2.type = bug_type1869 bug_card2.type = bug_type
1849 self.board.cards.extend([bug_card1, bug_card2])1870 self.board.cards.extend([bug_card1, bug_card2])
1850 # Include project1 and project2 in our active 'projects' fake LP data1871 # Include project1 and project2 in our active 'projects' fake LP data
1851 lp = FauxLP({"projects": {"myproject": project1, "myproject2": project2},1872 lp = FauxLP({
1852 "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})1873 "projects": {"myproject": project1, "myproject2": project2},
1874 "bugs": {123: bug_task1.bug, 456: bug_task2.bug}})
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)
1854 coding_lane = FauxLane(board=board, path='coding')1876 coding_lane = FauxLane(board=board, path='coding')
1855 self.assertEqual(new_lane, bug_card1.moved_to)1877 self.assertEqual(new_lane, bug_card1.moved_to)
18561878
=== modified file 'src/lp2kanban/tests/test_cards2workitems.py'
--- src/lp2kanban/tests/test_cards2workitems.py 2013-03-19 16:06:09 +0000
+++ src/lp2kanban/tests/test_cards2workitems.py 2016-09-08 22:02:56 +0000
@@ -1,5 +1,4 @@
1# Copyright 2012 Canonical Ltd.1# Copyright 2012 Canonical Ltd.
2__metaclass__ = type
32
4import re3import re
5import testtools4import testtools
@@ -27,6 +26,8 @@
27from testtools.monkey import patch26from testtools.monkey import patch
28from textwrap import dedent27from textwrap import dedent
2928
29__metaclass__ = type
30
3031
31class CardsToWorkItemsBase(testtools.TestCase):32class CardsToWorkItemsBase(testtools.TestCase):
3233
@@ -56,9 +57,9 @@
56 "String \"{string}\" did not match pattern \"{pattern}\"".format(57 "String \"{string}\" did not match pattern \"{pattern}\"".format(
57 string=string, pattern=pattern))58 string=string, pattern=pattern))
5859
59 def _generateCard(self, board=None, lane=None,60 def _generateCard(
60 description_annotations=None, assigned_user_id=0,61 self, board=None, lane=None, description_annotations=None,
61 title=None, external_system_url=None):62 assigned_user_id=0, title=None, external_system_url=None):
62 if board is None:63 if board is None:
63 board = self.board64 board = self.board
64 if lane is None:65 if lane is None:
@@ -220,7 +221,8 @@
220 work_item = convert_card_to_work_item(221 work_item = convert_card_to_work_item(
221 card, self.board, self.lp_users_for_board)222 card, self.board, self.lp_users_for_board)
222 self.assertEqual(223 self.assertEqual(
223 card.description_annotations.blueprint_url, work_item.blueprint_url)224 card.description_annotations.blueprint_url,
225 work_item.blueprint_url)
224226
225 def test_blueprint_extracted_from_card_external_link(self):227 def test_blueprint_extracted_from_card_external_link(self):
226 # Cards can also be linked to blueprints by putting the228 # Cards can also be linked to blueprints by putting the
@@ -233,7 +235,7 @@
233 external_system_url=self.blueprint_url,235 external_system_url=self.blueprint_url,
234 description_annotations=Record(),236 description_annotations=Record(),
235 )237 )
236 work_item = convert_card_to_work_item(238 convert_card_to_work_item(
237 card, self.board, self.lp_users_for_board)239 card, self.board, self.lp_users_for_board)
238 self.assertEqual(240 self.assertEqual(
239 self.blueprint_url, card.description_annotations.blueprint_url)241 self.blueprint_url, card.description_annotations.blueprint_url)
@@ -243,8 +245,8 @@
243 # isn't a blueprint, convert_card_to_work_item() will return245 # isn't a blueprint, convert_card_to_work_item() will return
244 # None.246 # None.
245 card = self._generateCard(247 card = self._generateCard(
246 description_annotations=Record248 description_annotations=Record(
247 (blueprint_url= 'https://nyancat.com'))249 blueprint_url='https://nyancat.com'))
248 work_item = convert_card_to_work_item(250 work_item = convert_card_to_work_item(
249 card, self.board, self.lp_users_for_board)251 card, self.board, self.lp_users_for_board)
250 self.assertIsNone(work_item)252 self.assertIsNone(work_item)
@@ -268,8 +270,8 @@
268 self._generateCard(270 self._generateCard(
269 title="Card %i" % id,271 title="Card %i" % id,
270 description_annotations=Record(272 description_annotations=Record(
271 blueprint_url=blueprint_url_tpl % id))273 blueprint_url=blueprint_url_tpl % id
272 for id in range(3)]274 )) for id in range(3)]
273 self.work_items = convert_cards_to_work_items(275 self.work_items = convert_cards_to_work_items(
274 self.cards, self.board, self.lp_users_for_board)276 self.cards, self.board, self.lp_users_for_board)
275 self.blueprints = {}277 self.blueprints = {}
@@ -283,6 +285,7 @@
283 # We stub out the Launchpad interaction using a fake version of285 # We stub out the Launchpad interaction using a fake version of
284 # fetch_blueprint()286 # fetch_blueprint()
285 called_with_urls = []287 called_with_urls = []
288
286 def fake_fetch_blueprint(lp, url):289 def fake_fetch_blueprint(lp, url):
287 called_with_urls.append(url)290 called_with_urls.append(url)
288 return FauxBlueprint(name="somename")291 return FauxBlueprint(name="somename")
289292
=== modified file 'src/lp2kanban/tests/test_description_annotations.py'
--- src/lp2kanban/tests/test_description_annotations.py 2013-03-18 13:25:36 +0000
+++ src/lp2kanban/tests/test_description_annotations.py 2016-09-08 22:02:56 +0000
@@ -9,6 +9,7 @@
9from textwrap import dedent9from textwrap import dedent
10import unittest10import unittest
1111
12
12class DescriptionAnnotationsTest(unittest.TestCase):13class DescriptionAnnotationsTest(unittest.TestCase):
13 """Tests for kanban.LeankitCard description annotations."""14 """Tests for kanban.LeankitCard description annotations."""
1415
@@ -16,7 +17,7 @@
16 # parse_card_description() parses key:value pairs out of a17 # parse_card_description() parses key:value pairs out of a
17 # card's description and returns them as a dict.18 # card's description and returns them as a dict.
18 card = LeankitCard.create(FauxLane("Test"))19 card = LeankitCard.create(FauxLane("Test"))
19 card.title=u"some card"20 card.title = u"some card"
20 card.description = dedent("""21 card.description = dedent("""
21 {"key2": "value2", "key1": "value 1"}22 {"key2": "value2", "key1": "value 1"}
22 """)23 """)
@@ -28,7 +29,7 @@
28 # Any text not in the key=value form is returned unparsed by29 # Any text not in the key=value form is returned unparsed by
29 # card.parsed_description().30 # card.parsed_description().
30 card = LeankitCard.create(FauxLane("Test"))31 card = LeankitCard.create(FauxLane("Test"))
31 card.title=u"some card"32 card.title = u"some card"
32 card.description = dedent("""33 card.description = dedent("""
33 Some other text,34 Some other text,
34 {"key2": "value2", "key1": "value 1"}35 {"key2": "value2", "key1": "value 1"}
@@ -44,7 +45,7 @@
44 # LeankitCard.description_annotations returns only the key=value45 # LeankitCard.description_annotations returns only the key=value
45 # pairs from the description field.46 # pairs from the description field.
46 card = LeankitCard.create(FauxLane("Test"))47 card = LeankitCard.create(FauxLane("Test"))
47 card.title=u"some card"48 card.title = u"some card"
48 card.description = dedent("""49 card.description = dedent("""
49 {"key2": "value2", "key1": "value 1"}50 {"key2": "value2", "key1": "value 1"}
50 Here's a URL: http://test.com?a=b51 Here's a URL: http://test.com?a=b
@@ -57,7 +58,7 @@
57 # LeankitCard._setDescriptionAnnotations() can be used to write58 # LeankitCard._setDescriptionAnnotations() can be used to write
58 # new annotations to the description field.59 # new annotations to the description field.
59 card = LeankitCard.create(FauxLane("Test"))60 card = LeankitCard.create(FauxLane("Test"))
60 card.title=u"some card"61 card.title = u"some card"
61 card.description = dedent("""62 card.description = dedent("""
62 Some text63 Some text
63 {"key2": "value2", "key1": "value 1"}64 {"key2": "value2", "key1": "value 1"}
@@ -73,7 +74,7 @@
73 # LeankitCard.description_annotations is a Record, so it hass74 # LeankitCard.description_annotations is a Record, so it hass
74 # attribute access for its values.75 # attribute access for its values.
75 card = LeankitCard.create(FauxLane("Test"))76 card = LeankitCard.create(FauxLane("Test"))
76 card.title=u"some card"77 card.title = u"some card"
77 card.description = '{"key1": "value 1"}'78 card.description = '{"key1": "value 1"}'
78 self.assertIsInstance(card.description_annotations, Record)79 self.assertIsInstance(card.description_annotations, Record)
79 self.assertEqual("value 1", card.description_annotations.key1)80 self.assertEqual("value 1", card.description_annotations.key1)
8081
=== modified file 'src/lp2kanban/workitems2cards.py'
--- src/lp2kanban/workitems2cards.py 2013-03-18 13:42:24 +0000
+++ src/lp2kanban/workitems2cards.py 2016-09-08 22:02:56 +0000
@@ -66,8 +66,8 @@
66 assigned_user_id = assigned_user.id66 assigned_user_id = assigned_user.id
67 else:67 else:
68 assigned_user_id = 068 assigned_user_id = 0
69 print "Creating card {title} [{assignee}]".format(69 print(u"Creating card {title} [{assignee}]".format(
70 title=work_item.text, assignee=assigned_user_id)70 title=work_item.text, assignee=assigned_user_id))
71 card = lane.addCard()71 card = lane.addCard()
72 card.title = work_item.text72 card.title = work_item.text
73 card.description_annotations.blueprint_url = work_item.blueprint73 card.description_annotations.blueprint_url = work_item.blueprint
@@ -90,15 +90,15 @@
9090
91 match = re.match(BLUEPRINT_URL_PATTERN, args.spec_url)91 match = re.match(BLUEPRINT_URL_PATTERN, args.spec_url)
92 if match is None:92 if match is None:
93 print "Spec URL is not a Launchpad blueprint."93 print("Spec URL is not a Launchpad blueprint.")
94 sys.exit(1)94 sys.exit(1)
9595
96 if not args.dry_run:96 if not args.dry_run:
97 should_continue = raw_input(97 should_continue = raw_input( # noqa
98 "WARNING: This script is not well-tested and will mess with "98 "WARNING: This script is not well-tested and will mess with "
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] ")
100 if should_continue.lower() != "y":100 if should_continue.lower() != "y":
101 print "Exiting."101 print("Exiting.")
102 sys.exit(0)102 sys.exit(0)
103103
104 match_dict = match.groupdict()104 match_dict = match.groupdict()
@@ -115,17 +115,17 @@
115 sys.exit(1)115 sys.exit(1)
116 work_items = parse_work_items(lp, **match_dict)116 work_items = parse_work_items(lp, **match_dict)
117 if not args.dry_run:117 if not args.dry_run:
118 should_continue = raw_input(118 should_continue = raw_input( # noqa
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. "
120 "Continue [y/N]?".format(count=len(work_items)))120 "Continue [y/N]?".format(count=len(work_items)))
121 if args.dry_run or should_continue.lower() == "y":121 if args.dry_run or should_continue.lower() == "y":
122 print "Creating cards from work items in lane {lane}.".format(122 print(u"Creating cards from work items in lane {lane}.".format(
123 lane=args.target_lane)123 lane=args.target_lane))
124 cards = work_items_to_cards(124 cards = work_items_to_cards(
125 work_items, board, lane, lp_users, dry_run=args.dry_run)125 work_items, board, lane, lp_users, dry_run=args.dry_run)
126 print "{count} cards created".format(count=len(cards))126 print(u"{count} cards created").format(count=len(cards))
127 else:127 else:
128 print "Aborting."128 print(u"Aborting.")
129129
130if __name__ == '__main__':130if __name__ == '__main__':
131 main(sys.argv)131 main(sys.argv)

Subscribers

People subscribed via source and target branches

to all changes: