Merge lp:~jkakar/kanban/project-kanban-board into lp:kanban

Proposed by Jamu Kakar
Status: Needs review
Proposed branch: lp:~jkakar/kanban/project-kanban-board
Merge into: lp:kanban
Diff against target: 207 lines (+83/-12)
6 files modified
kanban/board.py (+13/-2)
kanban/commands.py (+25/-4)
kanban/html.py (+2/-1)
kanban/launchpad.py (+16/-0)
kanban/templates/kanban.html (+3/-1)
kanban/tests/test_board.py (+24/-4)
To merge this branch: bzr merge lp:~jkakar/kanban/project-kanban-board
Reviewer Review Type Date Requested Status
Martin Pool Needs Fixing
Review via email: mp+61489@code.launchpad.net

Description of the change

This branch introduces the following changes:

- A new 'generate-project-kanban' command is available. It fetches
  all the bugs in a project or project group and renders a kanban
  board to represent them.

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

How wonderful! The diff looks good; I'm just trying it out for bzr now.

Revision history for this message
Martin Pool (mbp) wrote :

The results are at <http://people.canonical.com/~mbp/kanban/bzr-project-kanban.html>; unfortunately they are a bit buggy:
 * something has caused the columns to be scrambled
 * it seems to be showing every fixed bug ever, but we should probably leave them off after 30 days like for the person/team view
 * showing every open bug as Queued is kind of consistent with the existing code, but lists too many bugs. for the way we use it, showing only assigned confirmed/triaged bugs would be reasonable.

review: Needs Fixing
Revision history for this message
Martin Pool (mbp) wrote :

i think this was actually merged, or something equivalent to it?

Revision history for this message
Jamu Kakar (jkakar) wrote :

It was never merged and I don't think anything similar was... it's
still sitting here on my drive. I'll try to give it some love. :)

Unmerged revisions

29. By Jamu Kakar

- Added a new ProjectBoard class to represent kanban boards for a
  project or project group.
- Added a new 'generate-project-kanban' command to generate a kanban
  board for a project.
- Added a new get_project_bugs function to get bugs for a particular
  project or project group.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'kanban/board.py'
2--- kanban/board.py 2011-04-02 09:32:52 +0000
3+++ kanban/board.py 2011-05-18 22:31:41 +0000
4@@ -223,7 +223,7 @@
5
6 class MilestoneBoard(StoryCollectionMixin):
7 """
8- A milestone board contains a collection of L{Bug}s targetted to a
9+ A milestone board contains a collection of L{Bug}s targeted to a
10 milestone in Launchpad.
11
12 @param project_name: The name of the project or project group in Launchpad
13@@ -242,7 +242,7 @@
14
15 class PersonBoard(StoryCollectionMixin):
16 """
17- A person board contains a collection of L{Bug}s targetted to a particular
18+ A person board contains a collection of L{Bug}s targeted to a particular
19 person or team.
20
21 @param name: The name of the person or team.
22@@ -251,6 +251,17 @@
23 """
24
25
26+class ProjectBoard(StoryCollectionMixin):
27+ """
28+ A project board contains a collection of L{Bug}s targeted to a particular
29+ project or project group.
30+
31+ @param name: The name of the project or project group.
32+ @param include_needs_testing: Optionally, a flag indicating whether or not
33+ to use the 'Needs testing' category. Defaults to C{False}.
34+ """
35+
36+
37 def compare_stories(a, b):
38 """Compare two L{Story}s.
39
40
41=== modified file 'kanban/commands.py'
42--- kanban/commands.py 2011-03-12 07:54:27 +0000
43+++ kanban/commands.py 2011-05-18 22:31:41 +0000
44@@ -5,11 +5,12 @@
45
46 from launchpadlib.launchpad import Launchpad
47
48-from kanban.board import MilestoneBoard, PersonBoard, compare_bugs
49+from kanban.board import (
50+ MilestoneBoard, PersonBoard, ProjectBoard, compare_bugs)
51 from kanban.html import generate_html, generate_roadmap_html
52 from kanban.launchpad import (
53 get_config_path, get_cache_path, get_launchpad, get_milestone_bugs,
54- get_person_assigned_bugs, SERVICE_ROOT)
55+ get_person_assigned_bugs, get_project_bugs, SERVICE_ROOT)
56 from kanban.roadmap import load_roadmap
57
58
59@@ -48,7 +49,7 @@
60
61
62 class cmd_generate_person_kanban(HTMLOutputMixin, Command):
63- """Print an HTML kanban board for a person or team to the screen.
64+ """Generate an HTML kanban board for a person or team.
65
66 The page shows bugs that are either open or were fixed within the last
67 month, that are directly assigned to the named person. If a team is
68@@ -72,8 +73,28 @@
69 self.write_output(generate_html(person_board), output_file)
70
71
72+class cmd_generate_project_kanban(HTMLOutputMixin, Command):
73+ """Generate an HTML kanban board for a project or project group."""
74+
75+ takes_args = ["project"]
76+ takes_options = [Option("output-file", short_name="o", type=str,
77+ help="Write HTML to file."),
78+ Option("include-needs-testing",
79+ help="Include the 'Needs testing' category.")]
80+ _see_also = ["launchpad-login"]
81+
82+ def run(self, project, output_file=None, include_needs_testing=None):
83+ launchpad = get_launchpad()
84+ project_board = ProjectBoard(
85+ project, include_needs_testing=include_needs_testing)
86+ bugs = get_project_bugs(launchpad, project)
87+ for bug in sorted(bugs, compare_bugs):
88+ project_board.add(bug)
89+ self.write_output(generate_html(project_board), output_file)
90+
91+
92 class cmd_generate_milestone_kanban(HTMLOutputMixin, Command):
93- """Print an HTML kanban board for a milestone to the screen."""
94+ """Generate an HTML kanban board for a milestone."""
95
96 takes_args = ["project_group", "milestone_name"]
97 takes_options = [Option("output-file", short_name="o", type=str,
98
99=== modified file 'kanban/html.py'
100--- kanban/html.py 2011-02-17 10:07:00 +0000
101+++ kanban/html.py 2011-05-18 22:31:41 +0000
102@@ -2,7 +2,7 @@
103
104 from jinja2 import Environment, PackageLoader
105
106-from kanban.board import MilestoneBoard
107+from kanban.board import MilestoneBoard, PersonBoard
108
109
110 def branch_name(branch_url):
111@@ -67,6 +67,7 @@
112 environment.filters["danger"] = lambda bug: warn(bug, 7, 3)
113 template = environment.get_template("kanban.html")
114 data = {"kanban_board": kanban_board,
115+ "is_person": isinstance(kanban_board, PersonBoard),
116 "is_milestone": isinstance(kanban_board, MilestoneBoard),
117 "now": datetime.utcnow().strftime("%a %e %b at %H:%M UTC")}
118 return template.render(**data)
119
120=== modified file 'kanban/launchpad.py'
121--- kanban/launchpad.py 2011-03-16 03:24:01 +0000
122+++ kanban/launchpad.py 2011-05-18 22:31:41 +0000
123@@ -184,6 +184,22 @@
124 return bugs
125
126
127+def get_project_bugs(launchpad, project_name):
128+ """Get a C{list} of L{Bug}s from a project in Launchpad.
129+
130+ @param launchpad: A C{Launchpad} instance.
131+ @param project_name: The name of the Launchpad project. Optionally, this
132+ can be a project group.
133+ """
134+ bugs = []
135+ project = get_project_group(launchpad, project_name)
136+ if not project:
137+ project = get_project(launchpad, project_name)
138+ for bug_task in project.searchTasks(status=RELEVANT_STATUSES):
139+ bugs.append(_create_bug(bug_task))
140+ return bugs
141+
142+
143 def _create_bug(bug_task):
144 """Create a L{Bug} from a C{bug_task} instance loaded from Launchpad."""
145 launchpad_bug = bug_task.bug
146
147=== modified file 'kanban/templates/kanban.html'
148--- kanban/templates/kanban.html 2011-04-02 09:32:52 +0000
149+++ kanban/templates/kanban.html 2011-05-18 22:31:41 +0000
150@@ -19,8 +19,10 @@
151 {% endif %}
152 {% if is_milestone %}
153 <h1><a href="https://launchpad.net/{{ kanban_board.project_name }}">{{ kanban_board.project_name }}</a> <a href="https://launchpad.net/{{ kanban_board.project_name }}/+milestone/{{ kanban_board.name}}">{{ kanban_board.name }}</a> <span class="bug-count">{{ kanban_board.bugs|length }} bugs</span></h1>
154- {% else %}
155+ {% elif is_person %}
156 <h1><a href="https://launchpad.net/~{{ kanban_board.name }}">{{ kanban_board.name }}</a> <span class="bug-count">{{ kanban_board.bugs|length }} bugs</span></h1>
157+ {% else %}
158+ <h1><a href="https://launchpad.net/{{ kanban_board.name }}">{{ kanban_board.name }}</a> <span class="bug-count">{{ kanban_board.bugs|length }} bugs</span></h1>
159 {% endif %}
160 </div>
161 </div>
162
163=== modified file 'kanban/tests/test_board.py'
164--- kanban/tests/test_board.py 2011-04-02 09:32:52 +0000
165+++ kanban/tests/test_board.py 2011-05-18 22:31:41 +0000
166@@ -3,10 +3,10 @@
167 from testtools import TestCase
168
169 from kanban.board import (
170- Bug, MilestoneBoard, PersonBoard, Story, compare_bugs, compare_stories,
171- NEW, CONFIRMED, TRIAGED, IN_PROGRESS, FIX_COMMITTED, FIX_RELEASED,
172- UNDECIDED, WISHLIST, LOW, MEDIUM, HIGH, CRITICAL, WORK_IN_PROGRESS,
173- NEEDS_REVIEW, APPROVED, MERGED)
174+ Bug, MilestoneBoard, PersonBoard, ProjectBoard, Story, compare_bugs,
175+ compare_stories, NEW, CONFIRMED, TRIAGED, IN_PROGRESS, FIX_COMMITTED,
176+ FIX_RELEASED, UNDECIDED, WISHLIST, LOW, MEDIUM, HIGH, CRITICAL,
177+ WORK_IN_PROGRESS, NEEDS_REVIEW, APPROVED, MERGED)
178
179
180 class BugTest(TestCase):
181@@ -538,6 +538,26 @@
182 self.assertEqual([], kanban_board.stories)
183
184
185+class ProjectBoardTest(BugCollectionMixinTestBase, StoryCollectionMixinTestBase,
186+ TestCase):
187+
188+ def create_test_class(self, include_needs_testing=None):
189+ """
190+ Create a L{ProjectBoard} for use in L{BugCollectionMixinTestBase} and
191+ L{StoryCollectionMixinTestBase} tests.
192+ """
193+ return ProjectBoard("project",
194+ include_needs_testing=include_needs_testing)
195+
196+ def test_instantiate(self):
197+ """
198+ A L{ProjectBoard} needs the name of the project when its instantiated.
199+ """
200+ kanban_board = ProjectBoard("project")
201+ self.assertEqual("project", kanban_board.name)
202+ self.assertEqual([], kanban_board.stories)
203+
204+
205 class StoryTest(BugCollectionMixinTestBase, TestCase):
206
207 def create_test_class(self, include_needs_testing=None):

Subscribers

People subscribed via source and target branches

to all changes: