bts_categorize.py 5.4 KB

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