Updated copyright information
[be.git] / libbe / command / target.py
1 # Copyright (C) 2005-2010 Aaron Bentley and Panometrics, Inc.
2 #                         Chris Ball <cjb@laptop.org>
3 #                         Gianluca Montecchi <gian@grys.it>
4 #                         Marien Zwart <marienz@gentoo.org>
5 #                         Thomas Gerigk <tgerigk@gmx.de>
6 #                         W. Trevor King <wking@drexel.edu>
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License along
19 # with this program; if not, write to the Free Software Foundation, Inc.,
20 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22 import libbe
23 import libbe.command
24 import libbe.command.util
25 import libbe.command.depend
26
27
28 class Target (libbe.command.Command):
29     """Assorted bug target manipulations and queries
30
31     >>> import os, StringIO, sys
32     >>> import libbe.bugdir
33     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
34     >>> io = libbe.command.StringInputOutput()
35     >>> io.stdout = sys.stdout
36     >>> ui = libbe.command.UserInterface(io=io)
37     >>> ui.storage_callbacks.set_storage(bd.storage)
38     >>> cmd = Target(ui=ui)
39
40     >>> ret = ui.run(cmd, args=['/a'])
41     No target assigned.
42     >>> ret = ui.run(cmd, args=['/a', 'tomorrow'])
43     >>> ret = ui.run(cmd, args=['/a'])
44     tomorrow
45
46     >>> ui.io.stdout = StringIO.StringIO()
47     >>> ret = ui.run(cmd, {'resolve':True}, ['tomorrow'])
48     >>> output = ui.io.get_stdout().strip()
49     >>> target = bd.bug_from_uuid(output)
50     >>> print target.summary
51     tomorrow
52     >>> print target.severity
53     target
54
55     >>> ui.io.stdout = sys.stdout
56     >>> ret = ui.run(cmd, args=['/a', 'none'])
57     >>> ret = ui.run(cmd, args=['/a'])
58     No target assigned.
59     >>> ui.cleanup()
60     >>> bd.cleanup()
61     """
62     name = 'target'
63
64     def __init__(self, *args, **kwargs):
65         libbe.command.Command.__init__(self, *args, **kwargs)
66         self.options.extend([
67                 libbe.command.Option(name='resolve', short_name='r',
68                     help="Print the UUID for the target bug whose summary "
69                     "matches TARGET.  If TARGET is not given, print the UUID "
70                     "of the current bugdir target."),
71                 ])
72         self.args.extend([
73                 libbe.command.Argument(
74                     name='id', metavar='BUG-ID', optional=True,
75                     completion_callback=libbe.command.util.complete_bug_id),
76                 libbe.command.Argument(
77                     name='target', metavar='TARGET', optional=True,
78                     completion_callback=complete_target),
79                 ])
80
81     def _run(self, **params):
82         if params['resolve'] == False:
83             if params['id'] == None:
84                 raise libbe.command.UserError('Please specify a bug id.')
85         else:
86             if params['target'] != None:
87                 raise libbe.command.UserError('Too many arguments')
88             params['target'] = params.pop('id')
89         bugdir = self._get_bugdir()
90         if params['resolve'] == True:
91             bug = bug_from_target_summary(bugdir, params['target'])
92             if bug == None:
93                 print >> self.stdout, 'No target assigned.'
94             else:
95                 print >> self.stdout, bug.uuid
96             return 0
97         bug,dummy_comment = libbe.command.util.bug_comment_from_user_id(
98             bugdir, params['id'])
99         if params['target'] == None:
100             target = bug_target(bugdir, bug)
101             if target == None:
102                 print >> self.stdout, 'No target assigned.'
103             else:
104                 print >> self.stdout, target.summary
105         else:
106             if params['target'] == 'none':
107                 target = remove_target(bugdir, bug)
108             else:
109                 target = add_target(bugdir, bug, params['target'])
110         return 0
111
112     def usage(self):
113         return 'usage: be %(name)s BUG-ID [TARGET]\nor:    be %(name)s --resolve [TARGET]' \
114             % vars(self)
115
116     def _long_help(self):
117         return """
118 Assorted bug target manipulations and queries.
119
120 If no target is specified, the bug's current target is printed.  If
121 TARGET is specified, it will be assigned to the bug, creating a new
122 target bug if necessary.
123
124 Targets are free-form; any text may be specified.  They will generally
125 be milestone names or release numbers.  The value "none" can be used
126 to unset the target.
127
128 In the alternative `be target --resolve TARGET` form, print the UUID
129 of the target-bug with summary TARGET.  If target is not given, return
130 use the bugdir's current target (see `be set`).
131
132 If you want to list all bugs blocking the current target, try
133   $ be depend --status -closed,fixed,wontfix --severity -target \
134     $(be target --resolve)
135
136 If you want to set the current bugdir target by summary (rather than
137 by UUID), try
138   $ be set target $(be target --resolve SUMMARY)
139 """
140
141 def bug_from_target_summary(bugdir, summary=None):
142     if summary == None:
143         if bugdir.target == None:
144             return None
145         else:
146             return bugdir.bug_from_uuid(bugdir.target)
147     matched = []
148     for uuid in bugdir.uuids():
149         bug = bugdir.bug_from_uuid(uuid)
150         if bug.severity == 'target' and bug.summary == summary:
151             matched.append(bug)
152     if len(matched) == 0:
153         return None
154     if len(matched) > 1:
155         raise Exception('Several targets with same summary:  %s'
156                         % '\n  '.join([bug.uuid for bug in matched]))
157     return matched[0]
158
159 def bug_target(bugdir, bug):
160     if bug.severity == 'target':
161         return bug
162     matched = []
163     for blocked in libbe.command.depend.get_blocks(bugdir, bug):
164         if blocked.severity == 'target':
165             matched.append(blocked)
166     if len(matched) == 0:
167         return None
168     if len(matched) > 1:
169         raise Exception('This bug (%s) blocks several targets:  %s'
170                         % (bug.uuid,
171                            '\n  '.join([b.uuid for b in matched])))
172     return matched[0]
173
174 def remove_target(bugdir, bug):
175     target = bug_target(bugdir, bug)
176     libbe.command.depend.remove_block(target, bug)
177     return target
178
179 def add_target(bugdir, bug, summary):
180     target = bug_from_target_summary(bugdir, summary)
181     if target == None:
182         target = bugdir.new_bug(summary=summary)
183         target.severity = 'target'
184     libbe.command.depend.add_block(target, bug)
185     return target
186
187 def targets(bugdir):
188     bugdir.load_all_bugs()
189     for bug in bugdir:
190         if bug.severity == 'target':
191             yield bug.summary
192
193 def complete_target(command, argument, fragment=None):
194     """
195     List possible command completions for fragment.
196
197     argument argument is not used.
198     """
199     return targets(command._get_bugdir())