Merge lp:~bjornt/lp2kanban/bugs-to-cards into lp:~launchpad/lp2kanban/trunk

Proposed by Björn Tillenius
Status: Merged
Approved by: Graham Binns
Approved revision: 71
Merged at revision: 59
Proposed branch: lp:~bjornt/lp2kanban/bugs-to-cards
Merge into: lp:~launchpad/lp2kanban/trunk
Diff against target: 167 lines (+106/-1)
3 files modified
examples/config.ini (+11/-0)
src/lp2kanban/bugs2cards.py (+43/-0)
src/lp2kanban/tests/test_bugs2cards.py (+52/-1)
To merge this branch: bzr merge lp:~bjornt/lp2kanban/bugs-to-cards
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+135909@code.launchpad.net

Description of the change

Support creating new cards from Launchpad bugs.

The workflow is:

  1) Tag the bug you want to create a card for with the
     configured tag.
  2) bugs2cards.py gets all bugs with the configured tag
     and creates new cards from them, if there aren't already
     a card linking to that bug.
  3) bugs2cards.py removes the configured tag from the bug, in
     order to make the next runs faster.

I'm not completely happy with 3), since it adds noise to the bug and
requires authenticated use of the API, but it's needed, since otherwise
the list of bugs to process would just grow and grow.

I'm also not too happy of the name of the config options, but I couldn't
come up with anything better. I'm open to suggestions.

Something that I would like to do in the future is to have the type of the
card be determined by what tags the bug has, but for now it always creates
card of the same type.

To post a comment you must log in.
Revision history for this message
Gary Poster (gary) wrote :

Hi. This sounds nice. I agree that #3 is not ideal but I can't think of another approach yet. I've asked someone in yellow squad to give you a review soon. I'd love to have this landed.

Thank you!

Gary

lp:~bjornt/lp2kanban/bugs-to-cards updated
70. By Björn Tillenius

Pass in the project, not the project group.

Revision history for this message
Benji York (benji) wrote :

The branch looks good. I didn't see anything that should prevent this
from landing but I had several comments/suggestions while reading the
code.

> I'm not completely happy with 3), since it adds noise to the bug and
> requires authenticated use of the API, but it's needed, since
> otherwise the list of bugs to process would just grow and grow.

An idea how to tag cards as synced with kanban without having to remove
the tags later: Tag bugs that should be synced, the script then queries
for non-closed, tagged bugs and compares against existing cards,
creating those that do not yet exist.

We will be processing a few more bugs than is strictly necessary, but
once a bug is closed it will fall out of the set of bugs being
inspected, so the number of bugs examined will be bounded.

This approach also means that "semantic" tags can be used to manage
cards. For example, if you have a project to implement the "gizmo"
feature and all the "gizmo" bugs are already tagged, you can just start
running lp2kanban with the new tag name and all the bugs will have cards
created and the tags won't be removed (as they would now).

What do you think about that?

Config option brainstorming
---------------------------

bug_new_card_tag:

- card_creation_bug_tag
- new_card_bug_tag
- bug_to_card_tag

bug_new_card_type:

- bug_to_card_type

I like the last two suggestions in each category because they share a
prefix and "bug_to_card" is relatively obvious in meaning.

Is my reading correct in that create_new_cards creates all the bugs in
the left-most lane? It would be nice for that to be configurable, many
boards aren't structured that way. In fact, I can see people running
multiple passes; creating cards from one tag in one lane and cards from
another tag in another.

Hmm, maybe that doesn't matter because the cards are then synced after
they are created, thus moving them into the correct lanes. If that is
correct, then a comment to that effect in create_new_cards would be
helpful.

Shouldn't the newly created cards have the "Synced with Launchpad"
marker added to the description? Wait, since the cards are "dirty",
that will be done by save_board, right?

The tests are appreciated.

We could use some tests for create_new_cards too. I would include tests
for the lane lookup code (possibly breaking it out into its own
function) and for the card type name to card type ID translation
function (it would probably benefit from being its own function too).

review: Approve (code)
Revision history for this message
Björn Tillenius (bjornt) wrote :
Download full text (4.2 KiB)

On Wed, Nov 28, 2012 at 02:39:57PM -0000, Benji York wrote:
> Review: Approve code
>
> The branch looks good. I didn't see anything that should prevent this
> from landing but I had several comments/suggestions while reading the
> code.
>
> > I'm not completely happy with 3), since it adds noise to the bug and
> > requires authenticated use of the API, but it's needed, since
> > otherwise the list of bugs to process would just grow and grow.
>
> An idea how to tag cards as synced with kanban without having to remove
> the tags later: Tag bugs that should be synced, the script then queries
> for non-closed, tagged bugs and compares against existing cards,
> creating those that do not yet exist.
>
> We will be processing a few more bugs than is strictly necessary, but
> once a bug is closed it will fall out of the set of bugs being
> inspected, so the number of bugs examined will be bounded.
>
> This approach also means that "semantic" tags can be used to manage
> cards. For example, if you have a project to implement the "gizmo"
> feature and all the "gizmo" bugs are already tagged, you can just start
> running lp2kanban with the new tag name and all the bugs will have cards
> created and the tags won't be removed (as they would now).
>
> What do you think about that?

Hmm. Maybe. I'm going to try it and do some timings. I currently run the
sync script to create new bugs once every minute, and would like to keep
doing so.

It also raises the question, how to delete cards from the board.
Although, I guess I could make it a config option whether to remove the
tag...

> Config option brainstorming
> ---------------------------
>
> bug_new_card_tag:
>
> - card_creation_bug_tag
> - new_card_bug_tag
> - bug_to_card_tag
>
> bug_new_card_type:
>
> - bug_to_card_type
>
>
> I like the last two suggestions in each category because they share a
> prefix and "bug_to_card" is relatively obvious in meaning.

Yeah, those are good suggestion. Let's see, I will postpone merging this
branch a while, to see whether I have time to implement some other
features I want. One would be to have a default card type for new bugs,
but then being able to map specific tags to specific types. For example,
the default could be 'Feature', but if a bug is marked with a 'defect'
tag, the card type will be 'Bug'.

> Is my reading correct in that create_new_cards creates all the bugs in
> the left-most lane? It would be nice for that to be configurable, many
> boards aren't structured that way. In fact, I can see people running
> multiple passes; creating cards from one tag in one lane and cards from
> another tag in another.

Ah, that is correct, that's something I forgot to address before
proposing the merge. I'll ponder on how to do that in a nice way.

> Hmm, maybe that doesn't matter because the cards are then synced after
> they are created, thus moving them into the correct lanes. If that is
> correct, then a comment to that effect in create_new_cards would be
> helpful.

No, I don't think that will work. As far I as can see, the logic for
moving lanes are highly specific to a certain board layout, and it will
move cards only within the same horizontal lane...

Read more...

Revision history for this message
Gary Poster (gary) wrote :

FWIW, I encourage landing something soon and making a separate branch for as many of the new features as you think you can postpone reasonably. There is some more work coming soon on lp2kanban AIUI from Graham's team.

lp:~bjornt/lp2kanban/bugs-to-cards updated
71. By Björn Tillenius

Rename config options.

Revision history for this message
Björn Tillenius (bjornt) wrote :

Ok, let's get this branch landed then?

I've renamed the config options, since that's easy to do.

I will soon propose a branch that enables card moves to work for Landscape's
board layout, and after that I will add another branch for choosing which
lane to create new cards in.

I will also add more tests in later branches as well.

I don't have permission to commit to lp:lp2kanban, so please someone either
land the branch for me, or give me permission.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/config.ini'
2--- examples/config.ini 2012-09-03 13:35:17 +0000
3+++ examples/config.ini 2012-12-07 09:40:24 +0000
4@@ -13,6 +13,17 @@
5 # for cards you don't want synced).
6 autosync = off
7
8+# Bug tag for which new cards should be created. If a bug in Launchpad
9+# has this tag, a new card will be created for it in the first kanban lane.
10+# If this config option isn't set, no cards will be created.
11+# After the card has been created, the tag will be removed from the bug, so a
12+# credentials_file has to be specified as well, to authenticate against LP.
13+#bug_to_card_tag = kanban
14+
15+# Which card type should cards created from bugs have. If not set, the default
16+# card type is used.
17+#bug_to_card_type = Defect
18+
19 # Lanes are assigned a "role" by looking for sublanes matching
20 # values given here. "Coding" will match all lanes which have
21 # a title of "Coding", and "QA::Ready" will match all lanes
22
23=== modified file 'src/lp2kanban/bugs2cards.py'
24--- src/lp2kanban/bugs2cards.py 2012-09-27 13:06:03 +0000
25+++ src/lp2kanban/bugs2cards.py 2012-12-07 09:40:24 +0000
26@@ -213,6 +213,15 @@
27 CardStatus.DONE: 'done_lanes',
28 }
29
30+def get_new_bugs(board, bugs):
31+ """Return the bugs that aren't already in the board."""
32+ existing_bugs = set(
33+ int(card.external_card_id) for card in board.cards
34+ if card.external_card_id)
35+ for bug in bugs:
36+ if bug.id not in existing_bugs:
37+ yield bug
38+
39
40 def get_card_status(bug_status, bug_tags, branch_info):
41 """Return the status of the card as one of CardStatus values."""
42@@ -277,6 +286,34 @@
43 return target_lane
44 return None
45
46+def create_new_cards(board, launchpad_project, bug_tag, cardtype_name=None):
47+ """Create cards for bugs tagged in Launchpad.
48+
49+ If a bug is tagged with the given tag, a new card will be created in
50+ the board and the tag will be removed from the bug.
51+ """
52+ new_cardtype = board.default_cardtype.id
53+ if cardtype_name is not None:
54+ for type_id, cardtype in board.cardtypes.items():
55+ if cardtype.name == cardtype_name:
56+ new_cardtype = type_id
57+ kanban_bugtasks = launchpad_project.searchTasks(tags=[bug_tag])
58+ kanban_bugs = get_new_bugs(
59+ board, [bugtask.bug for bugtask in kanban_bugtasks])
60+ next_lane = board.root_lane
61+ while next_lane.child_lanes:
62+ next_lane = next_lane.child_lanes[0]
63+ for bug in kanban_bugs:
64+ print " Creating card for bug %s in %s." % (bug.id, next_lane.title)
65+ card = next_lane.addCard()
66+ card.title = bug.title
67+ card.description = bug.description
68+ card.external_card_id = str(bug.id)
69+ card.type_id = new_cardtype
70+ card.save()
71+ bug.tags = [tag for tag in bug.tags if tag != bug_tag]
72+ bug.lp_save()
73+
74
75 def get_lp(config):
76 """Get a lp instance for a logged in user or anonymous access.
77@@ -315,6 +352,12 @@
78 if project:
79 all_projects.append(project)
80 lp_users = LaunchpadUsersForBoard(lp, board)
81+ new_bug_tag = bconf.get("bug_to_card_tag")
82+ if new_bug_tag is not None:
83+ for project in all_projects:
84+ print " Creating new cards for %s:" % project.name
85+ create_new_cards(
86+ board, project, new_bug_tag, bconf.get("bug_to_card_type"))
87 print " Syncing cards:"
88 for card in board.getCardsWithExternalIds():
89 if should_sync_card(card, bconf):
90
91=== modified file 'src/lp2kanban/tests/test_bugs2cards.py'
92--- src/lp2kanban/tests/test_bugs2cards.py 2012-10-02 14:02:04 +0000
93+++ src/lp2kanban/tests/test_bugs2cards.py 2012-12-07 09:40:24 +0000
94@@ -14,6 +14,7 @@
95 get_branch_info,
96 get_bug_status,
97 get_card_status,
98+ get_new_bugs,
99 parse_config,
100 should_sync_card,
101 )
102@@ -192,10 +193,15 @@
103
104
105 class FauxBug:
106- def __init__(self, tags=[]):
107+ def __init__(self, tags=[], title="Faux Bug",
108+ description="This is a faux bug.",
109+ bug_id=None):
110 self.bug_tasks_collection = []
111 self.linked_branches = []
112 self.tags = tags
113+ self.title = title
114+ self.description = description
115+ self.id = bug_id
116
117 def addTask(self, target, status='New', assignee=None):
118 self.bug_tasks_collection.append(
119@@ -477,3 +483,48 @@
120 slow_land.addNextLane(rollout)
121
122 self.assertEqual(rollout, find_card_target_lane(qa, 'Rollout'))
123+
124+
125+class FauxBoard:
126+
127+ def __init__(self, cards):
128+ self.cards = cards
129+
130+
131+class FauxCard:
132+
133+ def __init__(self, external_card_id=None):
134+ self.external_card_id = external_card_id
135+
136+
137+class GetNewBugsTest(unittest.TestCase):
138+
139+ def test_empty_board_no_bugs(self):
140+ # If the board is empty, and there are no new bugs, no
141+ # bugs are returned.
142+ board = FauxBoard(cards=[])
143+ bugs = []
144+ self.assertEqual([], list(get_new_bugs(board, bugs)))
145+
146+ def test_nonempty_board_no_bugs(self):
147+ # If the board has bugs, but there are no new bugs in the passed
148+ # in list, no bugs are returned.
149+ board = FauxBoard(
150+ cards=[FauxCard(), FauxCard(external_card_id="123")])
151+ bugs = []
152+ self.assertEqual([], list(get_new_bugs(board, bugs)))
153+
154+ def test_no_new_bugs(self):
155+ # If all the passed in bugs are already in the board, no bugs
156+ # are returned.
157+ board = FauxBoard(
158+ cards=[FauxCard(), FauxCard(external_card_id="123")])
159+ bugs = [FauxBug(bug_id=123)]
160+ self.assertEqual([], list(get_new_bugs(board, bugs)))
161+
162+ def test_new_bugs(self):
163+ # The passed in bugs that aren't in the board are returned.
164+ board = FauxBoard(
165+ cards=[FauxCard(), FauxCard(external_card_id="123")])
166+ bugs = [FauxBug(bug_id=123), FauxBug(bug_id=457)]
167+ self.assertEqual([bugs[1]], list(get_new_bugs(board, bugs)))

Subscribers

People subscribed via source and target branches