1#! /usr/bin/env python3
3"""
4bts -- manage bugs filed against ftp.debian.org
6@contact: Debian FTP Master <ftpmaster@debian.org>
7@copyright: 2009 Mike O'Connor <stew@vireo.org>
8@copyright: 2010 Alexander Reichle-Schmehl <tolimar@debian.org>
9@license: GNU General Public License version 2 or later
10"""
12# This program is free software; you can redistribute it and/or modify it
13# under the terms of the GNU General Public License as published by the
14# Free Software Foundation; either version 2, or (at your option) any
15# later version.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License
23# along with this program; if not, write to the Free Software
24# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25# USA.
27################################################################################
28################################################################################
30import logging
31import re
32import sys
34log = logging.getLogger()
36import apt_pkg
37import debianbts as bts
39from daklib import utils
42def usage():
43 print(
44 """
45SYNOPSIS
46 dak bts-categorize [options]
48OPTIONS
49 -s
50 --simulate
51 Don't send email, instead output the lines that would be sent to
52 control@b.d.o.
54 -v
55 --verbose
56 Print more informational log messages
58 -q
59 --quiet
60 Suppress informational messages
62 -h
63 --help
64 Print this documentation.
65"""
66 )
69arguments = [
70 ("s", "simulate", "BtsCategorize::Options::Simulate"),
71 ("v", "verbose", "BtsCategorize::Options::Verbose"),
72 ("q", "quiet", "BtsCategorize::Options::Quiet"),
73 ("h", "help", "BtsCategorize::Options::Help"),
74 ("o", "option", "", "ArbItem"),
75]
78class BugClassifier:
79 """
80 classify bugs using usertags based on the bug subject lines
82 >>> BugClassifier.rm_re.match( "RM: asdf" ) != None
83 True
84 >>> BugClassifier.rm_re.match( "[dak] Packages.diff/Index broken" ) != None
85 False
86 >>> BugClassifier.dak_re.match( "[dak] Packages.diff/Index broken" ) != None
87 True
88 """
90 rm_re = re.compile("^RM")
91 dak_re = re.compile(r"^\[dak\]")
92 arch_re = re.compile(r"^\[Architectures\]")
93 override_re = re.compile("^override")
95 classifiers = {
96 rm_re: "remove",
97 dak_re: "dak",
98 arch_re: "archs",
99 override_re: "override",
100 }
102 def unclassified_bugs(self):
103 """
104 Returns a list of open bugs which have not yet been classified
105 by one of our usertags.
106 """
108 tagged_bugs = bts.get_usertag("ftp.debian.org@packages.debian.org")
109 tagged_bugs_ftp = []
110 for tags in tagged_bugs.keys():
111 tagged_bugs_ftp += tagged_bugs[tags]
113 return [
114 bug
115 for bug in bts.get_status(bts.get_bugs(package="ftp.debian.org"))
116 if bug.pending == "pending" and bug.bug_num not in tagged_bugs_ftp
117 ]
119 def classify_bug(self, bug):
120 """
121 if any of our classifiers match, return a newline terminated
122 command to set an appropriate usertag, otherwise return an
123 empty string
124 """
125 retval = ""
127 for classifier in self.classifiers.keys():
128 if classifier.match(bug.subject):
129 retval = "usertag %s %s\n" % (bug.bug_num, self.classifiers[classifier])
130 break
132 if retval:
133 log.info(retval)
134 else:
135 log.debug("Unmatched: [%s] %s" % (bug.bug_num, bug.subject))
137 return retval
139 def email_text(self):
140 controls = ""
142 bc = BugClassifier()
143 try:
144 for bug in bc.unclassified_bugs():
145 controls += bc.classify_bug(bug)
147 return controls
148 except:
149 log.error(
150 "couldn't retrieve bugs from soap interface: %s" % sys.exc_info()[0]
151 )
152 return None
155def send_email(commands, simulate=False):
156 global Cnf
158 Subst = {
159 "__COMMANDS__": commands,
160 "__DAK_ADDRESS__": Cnf["Dinstall::MyAdminAddress"],
161 }
163 bts_mail_message = utils.TemplateSubst(
164 Subst, Cnf["Dir::Templates"] + "/bts-categorize"
165 )
167 if simulate:
168 print(bts_mail_message)
169 else:
170 utils.send_mail(bts_mail_message)
173def main():
174 """
175 for now, we just dump a list of commands that could be sent for
176 control@b.d.o
177 """
178 global Cnf
179 Cnf = utils.get_conf()
181 for arg in arguments:
182 opt = "BtsCategorize::Options::%s" % arg[1]
183 if opt not in Cnf: 183 ↛ 181line 183 didn't jump to line 181, because the condition on line 183 was never false
184 Cnf[opt] = ""
186 apt_pkg.parse_commandline(Cnf, arguments, sys.argv)
187 Options = Cnf.subtree("BtsCategorize::Options")
189 if Options["Help"]: 189 ↛ 193line 189 didn't jump to line 193, because the condition on line 189 was never false
190 usage()
191 sys.exit(0)
193 if Options["Quiet"]:
194 level = logging.ERROR
196 elif Options["Verbose"]:
197 level = logging.DEBUG
199 else:
200 level = logging.INFO
202 logging.basicConfig(
203 level=level, format="%(asctime)s %(levelname)s %(message)s", stream=sys.stderr
204 )
206 body = BugClassifier().email_text()
208 if body:
209 send_email(body, Options["Simulate"])
211 else:
212 log.info("nothing to do")
215if __name__ == "__main__":
216 # import doctest
217 # doctest.testmod()
218 main()