1#! /usr/bin/env python3 

2 

3""" 

4bts -- manage bugs filed against ftp.debian.org 

5 

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""" 

11 

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. 

26 

27################################################################################ 

28################################################################################ 

29 

30import logging 

31import re 

32import sys 

33 

34log = logging.getLogger() 

35 

36import apt_pkg 

37import debianbts as bts 

38 

39from daklib import utils 

40 

41 

42def usage(): 

43 print( 

44 """ 

45SYNOPSIS 

46 dak bts-categorize [options] 

47 

48OPTIONS 

49 -s 

50 --simulate 

51 Don't send email, instead output the lines that would be sent to 

52 control@b.d.o. 

53 

54 -v 

55 --verbose 

56 Print more informational log messages 

57 

58 -q 

59 --quiet 

60 Suppress informational messages 

61 

62 -h 

63 --help 

64 Print this documentation. 

65""" 

66 ) 

67 

68 

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] 

76 

77 

78class BugClassifier: 

79 """ 

80 classify bugs using usertags based on the bug subject lines 

81 

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 """ 

89 

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") 

94 

95 classifiers = { 

96 rm_re: "remove", 

97 dak_re: "dak", 

98 arch_re: "archs", 

99 override_re: "override", 

100 } 

101 

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 """ 

107 

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] 

112 

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 ] 

118 

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 = "" 

126 

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 

131 

132 if retval: 

133 log.info(retval) 

134 else: 

135 log.debug("Unmatched: [%s] %s" % (bug.bug_num, bug.subject)) 

136 

137 return retval 

138 

139 def email_text(self): 

140 controls = "" 

141 

142 bc = BugClassifier() 

143 try: 

144 for bug in bc.unclassified_bugs(): 

145 controls += bc.classify_bug(bug) 

146 

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 

153 

154 

155def send_email(commands, simulate=False): 

156 global Cnf 

157 

158 Subst = { 

159 "__COMMANDS__": commands, 

160 "__DAK_ADDRESS__": Cnf["Dinstall::MyAdminAddress"], 

161 } 

162 

163 bts_mail_message = utils.TemplateSubst( 

164 Subst, Cnf["Dir::Templates"] + "/bts-categorize" 

165 ) 

166 

167 if simulate: 

168 print(bts_mail_message) 

169 else: 

170 utils.send_mail(bts_mail_message) 

171 

172 

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() 

180 

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] = "" 

185 

186 apt_pkg.parse_commandline(Cnf, arguments, sys.argv) 

187 Options = Cnf.subtree("BtsCategorize::Options") 

188 

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) 

192 

193 if Options["Quiet"]: 

194 level = logging.ERROR 

195 

196 elif Options["Verbose"]: 

197 level = logging.DEBUG 

198 

199 else: 

200 level = logging.INFO 

201 

202 logging.basicConfig( 

203 level=level, format="%(asctime)s %(levelname)s %(message)s", stream=sys.stderr 

204 ) 

205 

206 body = BugClassifier().email_text() 

207 

208 if body: 

209 send_email(body, Options["Simulate"]) 

210 

211 else: 

212 log.info("nothing to do") 

213 

214 

215if __name__ == "__main__": 

216 # import doctest 

217 # doctest.testmod() 

218 main()