Rewrite commands to use bugdirs instead of a single bugdir.
[be.git] / libbe / command / tag.py
1 # Copyright (C) 2009-2012 Chris Ball <cjb@laptop.org>
2 #                         Gianluca Montecchi <gian@grys.it>
3 #                         W. Trevor King <wking@drexel.edu>
4 #
5 # This file is part of Bugs Everywhere.
6 #
7 # Bugs Everywhere is free software: you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by the Free
9 # Software Foundation, either version 2 of the License, or (at your option) any
10 # later version.
11 #
12 # Bugs Everywhere is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15 # more details.
16 #
17 # You should have received a copy of the GNU General Public License along with
18 # Bugs Everywhere.  If not, see <http://www.gnu.org/licenses/>.
19
20 import itertools
21
22 import libbe
23 import libbe.command
24 import libbe.command.util
25
26
27 TAG_TAG = 'TAG:'
28
29
30 class Tag (libbe.command.Command):
31     __doc__ = """Tag a bug, or search bugs for tags
32
33     >>> import sys
34     >>> import libbe.bugdir
35     >>> bd = libbe.bugdir.SimpleBugDir(memory=False)
36     >>> io = libbe.command.StringInputOutput()
37     >>> io.stdout = sys.stdout
38     >>> ui = libbe.command.UserInterface(io=io)
39     >>> ui.storage_callbacks.set_bugdirs({bd.uuid: bd})
40     >>> cmd = Tag(ui=ui)
41
42     >>> a = bd.bug_from_uuid('a')
43     >>> print a.extra_strings
44     []
45     >>> ret = ui.run(cmd, args=['/a', 'GUI'])
46     Tags for abc/a:
47     GUI
48     >>> bd.flush_reload()
49     >>> a = bd.bug_from_uuid('a')
50     >>> print a.extra_strings
51     ['%(tag_tag)sGUI']
52     >>> ret = ui.run(cmd, args=['/a', 'later'])
53     Tags for abc/a:
54     GUI
55     later
56     >>> ret = ui.run(cmd, args=['/a'])
57     Tags for abc/a:
58     GUI
59     later
60     >>> ret = ui.run(cmd, {'list':True})
61     GUI
62     later
63     >>> ret = ui.run(cmd, args=['/a', 'Alphabetically first'])
64     Tags for abc/a:
65     Alphabetically first
66     GUI
67     later
68     >>> bd.flush_reload()
69     >>> a = bd.bug_from_uuid('a')
70     >>> print a.extra_strings
71     ['%(tag_tag)sAlphabetically first', '%(tag_tag)sGUI', '%(tag_tag)slater']
72     >>> a.extra_strings = []
73     >>> print a.extra_strings
74     []
75     >>> ret = ui.run(cmd, args=['/a'])
76     >>> bd.flush_reload()
77     >>> a = bd.bug_from_uuid('a')
78     >>> print a.extra_strings
79     []
80     >>> ret = ui.run(cmd, args=['/a', 'Alphabetically first'])
81     Tags for abc/a:
82     Alphabetically first
83     >>> ret = ui.run(cmd, {'remove':True}, ['/a', 'Alphabetically first'])
84     >>> ui.cleanup()
85     >>> bd.cleanup()
86     """ % {'tag_tag':TAG_TAG}
87     name = 'tag'
88
89     def __init__(self, *args, **kwargs):
90         libbe.command.Command.__init__(self, *args, **kwargs)
91         self.options.extend([
92                 libbe.command.Option(name='remove', short_name='r',
93                     help='Remove TAG (instead of adding it)'),
94                 libbe.command.Option(name='list', short_name='l',
95                     help='List all available tags and exit'),
96                 ])
97         self.args.extend([
98                 libbe.command.Argument(
99                     name='id', metavar='BUG-ID', optional=True,
100                     completion_callback=libbe.command.util.complete_bug_id),
101                 libbe.command.Argument(
102                     name='tag', metavar='TAG', default=tuple(),
103                     optional=True, repeatable=True),
104                 ])
105
106     def _run(self, **params):
107         if params['id'] == None and params['list'] == False:
108             raise libbe.command.UserError('Please specify a bug id.')
109         if params['id'] != None and params['list'] == True:
110             raise libbe.command.UserError(
111                 'Do not specify a bug id with the --list option.')
112         bugdirs = self._get_bugdirs()
113         if params['list'] == True:
114             tags = list(itertools.chain(*
115                     [get_all_tags(bugdir) for bugdir in bugdirs.values()]))
116             tags.sort()
117             if len(tags) > 0:
118                 print >> self.stdout, '\n'.join(tags)
119             return 0
120
121         bugdir,bug,comment = (
122             libbe.command.util.bugdir_bug_comment_from_user_id(
123                 bugdirs, params['id']))
124         if len(params['tag']) > 0:
125             tags = get_tags(bug)
126             for tag in params['tag']:
127                 if params['remove'] == True:
128                     tags.remove(tag)
129                 else: # add the tag
130                     tags.append(tag)
131             set_tags(bug, tags)
132
133         tags = []
134         for estr in bug.extra_strings:
135             if estr.startswith(TAG_TAG):
136                 tags.append(estr[len(TAG_TAG):])
137
138         if len(tags) > 0:
139             print "Tags for %s:" % bug.id.user()
140             print '\n'.join(tags)
141         return 0
142
143     def _long_help(self):
144         return """
145 If TAG is given, add TAG to BUG-ID.  If it is not specified, just
146 print the tags for BUG-ID.
147
148 To search for bugs with a particular tag, try
149   $ be list --extra-strings %s<your-tag>
150 """ % TAG_TAG
151
152 # functions exposed to other modules
153
154 def get_all_tags(bugdir):
155     bugdir.load_all_bugs()
156     tags = []
157     for bug in bugdir:
158         for tag in get_tags(bug):
159             if tag not in tags:
160                 tags.append(tag)
161     return tags
162
163 def get_tags(bug):
164     tags = []
165     for estr in bug.extra_strings:
166         if estr.startswith(TAG_TAG):
167             tag = estr[len(TAG_TAG):]
168             if tag not in tags:
169                 tags.append(tag)
170     return tags
171
172 def set_tags(bug, tags):
173     estrs = bug.extra_strings
174     new_estrs = []
175     for estr in estrs:
176         if not estr.startswith(TAG_TAG):
177             new_estrs.append(estr)
178     for tag in tags:
179         new_estrs.append('%s%s' % (TAG_TAG, tag))
180     bug.extra_strings = new_estrs # reassign to notice change
181
182 def append_tag(bug, tag):
183     estrs = bug.extra_strings
184     estrs.append('%s%s' % (TAG_TAG, tag))
185     bug.extra_strings = estrs # reassign to notice change
186
187 def remove_tag(bug, tag):
188     estrs = bug.extra_strings
189     estrs.remove('%s%s' % (TAG_TAG, tag))
190     bug.extra_strings = estrs # reassign to notice change