bugzilla scripts folders deleted

This commit is contained in:
Anoop Valluthadam 2015-10-19 16:36:37 +05:30
Родитель 542f060ff7
Коммит d13348e0bd
12 изменённых файлов: 0 добавлений и 1479 удалений

Просмотреть файл

@ -1,16 +0,0 @@
__all__ = ['models', 'utils', 'agents']
import httplib
from remoteobjects import http
# Printing throws an error if we are printing using ascii
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
# Monkey patch remoteobjects to accept 202 status codes.
http.HttpObject.response_has_content[httplib.ACCEPTED] = False
VERSION = (0, 0, 1)
__version__ = '.'.join(map(str, VERSION))

Просмотреть файл

@ -1,43 +0,0 @@
import os
from bugzilla.models import BugSearch, Bug
from bugzilla.utils import urljoin, qs
class InvalidAPI_ROOT(Exception):
def __str__(self):
return "Invalid API url specified. " + \
"Please set BZ_API_ROOT in your environment " + \
"or pass it to the agent constructor"
class BugzillaAgent(object):
def __init__(self, api_root=None, api_key=None):
if not api_root:
api_root = os.environ.get('BZ_API_ROOT')
if not api_root:
raise InvalidAPI_ROOT
self.API_ROOT = api_root
self.api_key = api_key
def get_bug(self, bug, include_fields='_default,token,cc,keywords,whiteboard,comments', exclude_fields=None, params={}):
params['include_fields'] = [include_fields]
params['exclude_fields'] = [exclude_fields]
url = urljoin(self.API_ROOT, 'bug/%s?%s' % (bug, self.qs(**params)))
return Bug.get(url)
def get_bug_list(self, params={}):
url = urljoin(self.API_ROOT, 'bug/?%s' % (self.qs(**params)))
return BugSearch.get(url).bugs
def qs(self, **params):
if self.api_key:
params['api_key'] = [self.api_key]
return qs(**params)
class BMOAgent(BugzillaAgent):
def __init__(self, api_key=None):
super(BMOAgent, self).__init__('https://bugzilla.mozilla.org/bzapi/', api_key)

Просмотреть файл

@ -1,23 +0,0 @@
from datetime import datetime
from remoteobjects import fields
import dateutil.parser
class StringBoolean(fields.Field):
"""Decodes a boolean hidden in a string."""
def decode(self, value):
return bool(int(value))
class Datetime(fields.Datetime):
"""Uses python-dateutil for working with datetimes."""
def decode(self, value):
return dateutil.parser.parse(value)
def encode(self, value):
if not isinstance(value, datetime):
raise TypeError('Value to encode %r is not a datetime' % (value,))
return value.replace(microsecond=0).strftime(self.dateformat)

Просмотреть файл

@ -1,235 +0,0 @@
from remoteobjects import RemoteObject as RemoteObject_, fields
from .fields import StringBoolean, Datetime
# The datetime format is inconsistent.
DATETIME_FORMAT_WITH_SECONDS = '%Y-%m-%d %H:%M:%S %z'
DATETIME_FORMAT = '%Y-%m-%d %H:%M %Z'
class RemoteObject(RemoteObject_):
def post_to(self, url):
self._location = url
self.post(self)
return self.api_data['ref']
def put_to(self, url):
self._location = url
self.put()
def _get_location(self):
if self.__location is not None:
return self.__location
else:
return self.api_data.get('ref', None)
def _set_location(self, url):
self.__location = url
_location = property(_get_location, _set_location)
class Bug(RemoteObject):
id = fields.Field()
summary = fields.Field()
assigned_to = fields.Object('User')
creator = fields.Object('User')
target_milestone = fields.Field()
attachments = fields.List(fields.Object('Attachment'))
comments = fields.List(fields.Object('Comment'))
history = fields.List(fields.Object('Changeset'))
keywords = fields.List(fields.Object('Keyword'))
status = fields.Field()
resolution = fields.Field()
# TODO: These are Mozilla specific and should be generalized
cf_blocking_20 = fields.Field()
cf_blocking_fennec = fields.Field()
cf_crash_signature = fields.Field()
creation_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
flags = fields.List(fields.Object('Flag'))
blocks = fields.List(fields.Field())
depends_on = fields.List(fields.Field())
url = fields.Field()
cc = fields.List(fields.Object('User'))
keywords = fields.List(fields.Field())
whiteboard = fields.Field()
op_sys = fields.Field()
platform = fields.Field()
priority = fields.Field()
product = fields.Field()
qa_contact = fields.Object('User')
severity = fields.Field()
see_also = fields.List(fields.Field())
version = fields.Field()
alias = fields.Field()
classification = fields.Field()
component = fields.Field()
is_cc_accessible = StringBoolean()
is_everconfirmed = StringBoolean()
is_creator_accessible = StringBoolean()
last_change_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
ref = fields.Field()
# Needed for submitting changes.
token = fields.Field(api_name='update_token')
# Time tracking.
actual_time = fields.Field()
deadline = Datetime(DATETIME_FORMAT_WITH_SECONDS)
estimated_time = fields.Field()
# groups = fields.Field() # unimplemented
percentage_complete = fields.Field()
remaining_time = fields.Field()
work_time = fields.Field()
def __repr__(self):
return '<Bug %s: "%s">' % (self.id, self.summary)
def __str__(self):
return "[Bug %s] - %s" % (self.id, self.summary)
def __hash__(self):
return self.id
class User(RemoteObject):
name = fields.Field()
real_name = fields.Field()
ref = fields.Field()
def __repr__(self):
return '<User "%s">' % self.real_name
def __str__(self):
return self.real_name or self.name
def __hash__(self):
if not self or not self.name:
return 0
return self.name.__hash__()
class Attachment(RemoteObject):
# Attachment data.
id = fields.Field()
attacher = fields.Object('User')
creation_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
last_change_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
description = fields.Field()
bug_id = fields.Field()
bug_ref = fields.Field()
# File data.
file_name = fields.Field()
size = fields.Field()
content_type = fields.Field()
# Attachment metadata.
flags = fields.List(fields.Object('Flag'))
is_obsolete = StringBoolean()
is_private = StringBoolean()
is_patch = StringBoolean()
# Used for submitting changes.
token = fields.Field()
ref = fields.Field()
# Only with attachmentdata=1
data = fields.Field()
encoding = fields.Field()
def __repr__(self):
return '<Attachment %s: "%s">' % (self.id, self.description)
def __hash__(self):
return self.id
class Comment(RemoteObject):
id = fields.Field()
creator = fields.Object('User')
creation_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
text = fields.Field()
is_private = StringBoolean()
def __repr__(self):
return '<Comment by %s on %s>' % (
self.creator, self.creation_time.strftime(DATETIME_FORMAT))
def __str__(self):
return self.text
def __hash__(self):
return self.id
class Change(RemoteObject):
field_name = fields.Field()
added = fields.Field()
removed = fields.Field()
def __repr__(self):
return '<Change "%s": "%s" -> "%s">' % (self.field_name, self.removed,
self.added)
class Changeset(RemoteObject):
changer = fields.Object('User')
changes = fields.List(fields.Object('Change'))
change_time = Datetime(DATETIME_FORMAT_WITH_SECONDS)
def __repr__(self):
return '<Changeset by %s on %s>' % (
self.changer, self.change_time.strftime(DATETIME_FORMAT))
class Flag(RemoteObject):
id = fields.Field()
name = fields.Field()
setter = fields.Object('User')
status = fields.Field()
requestee = fields.Object('User')
type_id = fields.Field()
def __repr__(self):
return '<Flag "%s">' % self.name
def __str__(self):
return self.name
def __hash__(self):
return self.id
class Keyword(RemoteObject):
name = fields.Field()
def __repr__(self):
return '<Keyword "%s">' % self.name
def __str__(self):
return self.name
def __hash__(self):
if not self or not self.name:
return 0
return self.name.__hash__()
class BugSearch(RemoteObject):
bugs = fields.List(fields.Object('Bug'))

Просмотреть файл

@ -1,90 +0,0 @@
import base64
from ConfigParser import ConfigParser
import getpass
import os
import posixpath
import urllib
def urljoin(base, *args):
"""Remove any leading slashes so no subpaths look absolute."""
return posixpath.join(base, *[str(s).lstrip('/') for s in args])
def qs(**kwargs):
"""Build a URL query string."""
url = ''
for k, v in kwargs.iteritems():
if k == 'username' or k == 'password':
pass
for value in v:
url += '&%s=%s' % (urllib.quote(k), value)
return url
def get_credentials(username=None):
# Try to get it from the environment first
if not username:
username = os.environ.get('BZ_USERNAME', None)
password = os.environ.get('BZ_PASSWORD', None)
# Try to get it from the system keychain next
if not username and not password:
try:
import keyring
if not username:
# Grab the default username as we weren't passed in a specific one
username = keyring.get_password("bugzilla", 'default_username')
if username:
# Get the password for the username
password = keyring.get_password("bugzilla", username)
except ImportError:
# If they don't have the keyring lib, fall back to next method
pass
# Then try a config file in their home directory
if not (username and password):
rcfile = os.path.expanduser('~/.bztoolsrc')
config = ConfigParser()
config.add_section('bugzilla')
if os.path.exists(rcfile):
try:
config.read(rcfile)
username = config.get('bugzilla', 'username')
_password = config.get('bugzilla', 'password')
if _password:
password = base64.b64decode(_password)
except Exception:
pass
# Finally, prompt the user for the info if we didn't get it above
if not (username and password):
username = raw_input('Bugzilla username: ')
password = getpass.getpass('Bugzilla password: ')
try:
# Save the data to the keyring if possible
import keyring
keyring.set_password("bugzilla", 'default_username', username)
keyring.set_password("bugzilla", username, password)
except ImportError:
# Otherwise save it to a config file
config.set('bugzilla', 'username', username)
config.set('bugzilla', 'password', base64.b64encode(password))
with open(rcfile, 'wb') as configfile:
config.write(configfile)
return username, password
FILE_TYPES = {
'text': 'text/plain',
'html': 'text/html',
'xml': 'application/xml',
'gif': 'image/gif',
'jpg': 'image/jpeg',
'png': 'image/png',
'svg': 'image/svg+xml',
'binary': 'application/octet-stream',
'xul': 'application/vnd.mozilla.xul+xml',
}

Просмотреть файл

Просмотреть файл

@ -1,145 +0,0 @@
#!/usr/bin/env python
import base64
import itertools
import os
import argparse
from bugzilla.models import Attachment, Flag, User, Comment
from bugzilla.agents import BugzillaAgent
from bugzilla.utils import urljoin, get_credentials, FILE_TYPES
REVIEW = 4
class AttachmentAgent(BugzillaAgent):
"""Stores credentials, navigates the site."""
def attach(self, bug_id, filename, description, patch=False,
reviewer=None, comment='', content_type='text/plain'):
"""Create an attachment, add a comment, obsolete other attachments."""
print 'Adding "%s" to %s' % (filename, bug_id)
self._attach(bug_id, filename, description, patch,
reviewer, content_type)
bug = self.get_bug(bug_id)
if comment:
print 'Adding the comment'
self._comment(bug_id, comment)
print 'Finding attachments to make obsolete...'
self.obsolete(bug)
def _attach(self, bug_id, filename, description, is_patch=False,
reviewer=None, content_type='text/plain'):
"""Create a new attachment."""
fields = {
'data': base64.b64encode(open(filename).read()),
'encoding': 'base64',
'file_name': filename,
'content_type': content_type,
'description': description,
'is_patch': is_patch,
}
if reviewer is not None:
fields['flags'] = [Flag(type_id=REVIEW, status='?',
requestee=User(name=reviewer))]
url = urljoin(self.API_ROOT, 'bug/%s/attachment?%s' % (bug_id, self.qs()))
return Attachment(**fields).post_to(url)
def _comment(self, bug_id, comment):
"""Create a new comment."""
url = urljoin(self.API_ROOT, 'bug/%s/comment?%s' % (bug_id, self.qs()))
return Comment(text=comment).post_to(url)
def obsolete(self, bug):
"""Ask what attachments should be obsoleted."""
attachments = [a for a in bug.attachments
if not bool(int(a.is_obsolete))]
if not attachments:
return
print "What attachments do you want to obsolete?"
msg = '[{index}] {a.id}: "{a.description}" ({a.file_name})'
for index, a in enumerate(attachments):
print msg.format(index=index, a=a)
numbers = raw_input('Enter the numbers (space-separated) of '
'attachments to make obsolete:\n').split()
if not numbers:
return
map_ = dict((str(index), a) for index, a in enumerate(attachments))
for num, _ in itertools.groupby(sorted(numbers)):
try:
self._obsolete(map_[num])
except KeyError:
pass
def _obsolete(self, attachment):
"""Mark an attachment obsolete."""
print "Obsoleting", attachment
attachment.is_obsolete = True
attachment._location += '?%s' % self.qs()
attachment.put()
def main():
# Script options
parser = argparse.ArgumentParser(description='Submit Bugzilla attachments')
parser.add_argument('bug_id',
type=int,
metavar='BUG',
help='Bug number')
parser.add_argument('filename',
metavar='FILE',
help='File to upload')
parser.add_argument('--description',
help='Attachment description',
required=True)
parser.add_argument('--patch',
action='store_true',
help='Is this a patch?')
parser.add_argument('--reviewer',
help='Bugzilla name of someone to r?')
parser.add_argument('--comment',
help='Comment for the attachment')
parser.add_argument('--content_type',
choices=FILE_TYPES,
help="File's content_type")
args = parser.parse_args()
if args.content_type:
args.content_type = FILE_TYPES[args.content_type]
# Get the API root, default to bugzilla.mozilla.org
API_ROOT = os.environ.get('BZ_API_ROOT',
'https://api-dev.bugzilla.mozilla.org/latest/')
# Authenticate
username, password = get_credentials()
# Load the agent
bz = AttachmentAgent(API_ROOT, username, password)
# Attach the file
bz.attach(**dict(args._get_kwargs()))
if __name__ == '__main__':
main()

Просмотреть файл

@ -1,92 +0,0 @@
# Post-Mortem and Sign-off automated reminder
# Daily cronjob, 6am PT
import urllib2
import datetime
import re
import os
import json
import smtplib
REPLY_TO_EMAIL = 'release-mgmt@mozilla.com'
SMTP = 'smtp.mozilla.org'
CONFIG_JSON = os.getcwd() + "/bztools/scripts/configs/config.json"
config = json.load(open(CONFIG_JSON, 'r'))
scripts_dir = os.getcwd() + "/scripts/"
subject = None
toaddrs = ['dev-planning@lists.mozilla.org', 'release-drivers@mozilla.com']
def sendMail(toaddr, options):
message = (
"From: %s\r\n" % options['username']
+ "To: %s\r\n" % toaddr
+ "CC: %s\r\n" % options['cclist']
+ "Reply-To: %s\r\n" % REPLY_TO_EMAIL
+ "Subject: %s\r\n" % options['subject']
+ "\r\n"
+ options['body'])
server = smtplib.SMTP_SSL(SMTP, 465)
server.set_debuglevel(1)
server.login(options['username'], options['password'])
# note: toaddrs is required for transport agents, the msg['To'] header is not modified
server.sendmail(options['username'], toaddr, message)
server.quit()
def getTemplateValue(url):
version_regex = re.compile(".*<p>(.*)</p>.*")
template_page = urllib2.urlopen(url).read().replace('\n', '')
parsed_template = version_regex.match(template_page)
return parsed_template.groups()[0]
# Grab the release date, the beta version number
release_date = getTemplateValue("https://wiki.mozilla.org/Template:FIREFOX_SHIP_DATE")
beta_version = getTemplateValue("https://wiki.mozilla.org/Template:BETA_VERSION")
current_version = getTemplateValue("https://wiki.mozilla.org/Template:CURRENT_VERSION")
today = datetime.date.today()
release = datetime.datetime.strptime(release_date, "%B %d, %Y").date()
# Check the timedelta between today and releasedate and if:
# -7 days before release date Sign Off reminder for 'tomorrow': Thurs at 10am PT
# -29 days before next release date send Post-Mortem for the previous version 'tomorrow': Tues at 10am PT)
timedelta = today - release
if timedelta.days == -7:
# send the reminder email for sign off meeting
print "Sending Sign-off email reminder %s" % today
subject = "Automatic Reminder: Firefox %s Sign Off Meeting" % beta_version
body = """
This is a reminder that the FF%s sign-off meeting will be held tomorrow in the Release Coordination Vidyo room @ 10:00 am PT.
The wiki page is up and ready for you to add notes : https://wiki.mozilla.org/Releases/Firefox_%s/Final_Signoffs
-- Release Management
""" % (beta_version, beta_version)
if timedelta.days == -29:
# send the reminder email for post-mortem of curent release version
print "Sending post-mortem email reminder %s" % today
subject = "Reminder: Firefox %s Post Mortem Meeting Tomorrow" % current_version
body = """
Friendly Reminder that the FF%s.0 Post-Mortem will take place tomorrow @ 10:00 am PT during the Channel Meeting in the Release Co-ordination Vidyo room.
Etherpad - https://etherpad.mozilla.org/%s-0-Post-Mortem
-- Release Management
""" % (current_version, current_version)
if subject is not None:
options = {
"username": config['ldap_username'],
"password": config['ldap_password'],
"subject": subject,
"body": body,
"cclist": "release-mgmt@mozilla.com",
"toaddrs": toaddrs
}
for email in toaddrs:
sendMail(email, options)
else:
print "No command today: %s" % today

Просмотреть файл

@ -1,138 +0,0 @@
#!/usr/bin/python
import datetime
import subprocess
import os
import json
from argparse import ArgumentParser
CONFIG_JSON = os.getcwd() + "/scripts/configs/config.json"
config = json.load(open(CONFIG_JSON, 'r'))
scripts_dir = os.getcwd() + "/scripts/"
queries_dir = os.getcwd() + "/queries/"
def createQuery(title, short_title, url, show_summary, cc):
file_name = queries_dir + str(datetime.date.today()) + '_' + short_title
qf = open(file_name, 'w')
qf.write("query_name = \'" + title + "\'\n")
qf.write("query_url = \'" + url + "\'\n")
qf.write("show_summary = \'" + str(show_summary) + "\'\n")
if cc is not None:
qf.write("cc = \'" + ",".join(cc) + "\'\n")
return file_name
def createQueriesList(print_all):
queries = []
weekday = datetime.datetime.today().weekday()
for url in urls:
try:
cc = url[1][4]
except IndexError:
cc = None # no cc
# Every Weekday
if weekday >= 0 and weekday < 5 and url[0] == 5:
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
# Monday only emails
if weekday == 0 and url[0] == 0:
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
# Tuesday only emails
if weekday == 1 and url[0] == 1:
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
# Thursday only emails
if weekday == 3 and url[0] == 3:
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
# Friday only emails
if weekday == 4 and url[0] == 4:
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3], cc=cc))
return queries
# ========================= CURRENT QUERIES ============================
# NOTE: You must replace query bug_status with comma-separated values
# eg: &bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED
#
# ==== KOI (v1.2) ====
unfixed_koi_sec_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_blocking_b2g&o1=equals&resolution=---&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&v1=koi%2B&f2=status_whiteboard&o2=notsubstring&v2=[no-nag]&o3=anywordssubstr&v3=Confidential%20Security&f3=bug_group"
unfixed_koi_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_blocking_b2g&o1=equals&resolution=---&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&v1=koi%2B&f2=status_whiteboard&o2=notsubstring&v2=[no-nag]&o3=anywordssubstr&v3=Confidential%20Security&f3=bug_group&n3=1"
team_dev_koi_nom = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=DOM%3A%20Device%20Interfaces&v2=koi%3F&product=Core&o5=equals&n5=1&v5=FIXED&f5=resolution"
team_dev_koi_blockers = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=DOM%3A%20Device%20Interfaces&v2=koi%2B&product=Core&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
team_gfx_koi_nom = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Graphics%3A%20Layers&v2=koi%3F&product=Core&o5=equals&n5=1&v5=FIXED&f5=resolution"
team_gfx_koi_blockers = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Graphics%3A%20Layers&v2=koi%2B&product=Core&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
team_media_koi_nom = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Gaia%3A%3ACamera&component=Gaia%3A%3AFMRadio&component=Gaia%3A%3AMusic&component=Gaia%3A%3AVideo&v2=koi%3F&product=Firefox%20OS&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
team_media_koi_blockers = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Gaia%3A%3ACamera&component=Gaia%3A%3AFMRadio&component=Gaia%3A%3AMusic&component=Gaia%3A%3AVideo&v2=koi%2B&product=Firefox%20OS&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
team_comm_koi_nom = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Gaia%3A%3AContacts&component=Gaia%3A%3ADialer&component=Gaia%3A%3AEverything.me&component=Gaia%3A%3ASMS&v2=koi%3F&product=Firefox%20OS&o5=equals&n5=1&v5=FIXED&f5=resolution"
team_comm_koi_blockers = "https://bugzilla.mozilla.org/buglist.cgi?f1=OP&f0=OP&o2=equals&f4=CP&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED,NEW,ASSIGNED,REOPENED&component=Gaia%3A%3AContacts&component=Gaia%3A%3ADialer&component=Gaia%3A%3AEverything.me&component=Gaia%3A%3ASMS&v2=koi%2B&product=Firefox%20OS&o5=equals&n5=1&v5=FIXED&f5=resolution&o6=equals&n6=1&v6=WORKSFORME&f6=resolution&o7=equals&n7=1&v7=DUPLICATE&f7=resolution&o8=equals&n8=1&v8=INVALID&f8=resolution&o9=equals&n9=1&v9=WONTFIX&f9=resolution"
koi_regressions_unfixed = "https://bugzilla.mozilla.org/buglist.cgi?j_top=OR&keywords=regression%2C%20regressionwindow-wanted%2C%20&keywords_type=anywords&f1=cf_blocking_b2g&o1=anywords&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&v1=koi"
koi_assignee_no_comment_three_days = "https://bugzilla.mozilla.org/buglist.cgi?o5=nowordssubstr&f1=OP&f0=OP&f8=owner_idle_time&o2=equals&f4=OP&v5=fixed%20verified%20unaffected%20wontfix&j1=OR&f3=CP&f2=cf_blocking_b2g&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&j4=OR&f5=cf_status_b2g_1_2&v8=-3d&f6=CP&v2=koi%2B&f7=CP&o8=greaterthan"
# ==== General ====
needinfo_sec_blockers_url = "https://bugzilla.mozilla.org/buglist.cgi?o1=anywords&o2=substring&v1=koi%2B&v2=needinfo%3F&f1=cf_blocking_b2g&resolution=---&f2=flagtypes.name&f3=status_whiteboard&o3=notsubstring&v3=[no-nag]&o4=anywordssubstr&v4=Confidential%20Security&f4=bug_group"
needinfo_blockers_url = "https://bugzilla.mozilla.org/buglist.cgi?o1=anywords&o2=substring&v1=koi%2B&v2=needinfo%3F&f1=cf_blocking_b2g&resolution=---&f2=flagtypes.name&f3=status_whiteboard&o3=notsubstring&v3=[no-nag]&o4=anywordssubstr&v4=Confidential%20Security&f4=bug_group&n4=1"
# TODO - sort the queries according to a priority flag
# TODO - batch up by query name (so sec & non-sec get in the same output)
urls = [
(5, ["Blocker Bugs with Need-Info? (Sec)", "needinfo_sec_blockers", needinfo_sec_blockers_url, 0]),
(5, ["Blocker Bugs with Need-Info?", "needinfo_blockers", needinfo_blockers_url, 1]),
(0, ["Bugs Blocking KOI (Sec)", "unfixed_koi_sec", unfixed_koi_sec_url, 0]),
(0, ["Bugs Blocking KOI", "unfixed_koi", unfixed_koi_url, 1]),
(1, ["Koi Blocking Nominiations for DOM: Dev Interfaces", "team_dev_koi_nom", team_dev_koi_nom, 1, ["dhylands@mozilla.com", ]]),
(1, ["Koi Blocker Bugs, DOM: Dev Interfaces", "team_dev_koi_blockers", team_dev_koi_blockers, 1, ["dhylands@mozilla.com", ]]),
(1, ["Koi Blocking Nominiations for Graphics", "team_gfx_koi_nom", team_gfx_koi_nom, 1, ["msreckovic@mozilla.com", ]]),
(1, ["Koi Blocker Bugs, Graphics", "team_gfx_koi_blockers", team_gfx_koi_blockers, 1, ["msreckovic@mozilla.com", ]]),
(1, ["Koi Blocking Nominiations for B2G: Media", "team_media_koi_nom", team_media_koi_nom, 1, ["hkoka@mozilla.com", ]]),
(1, ["Koi Blocker Bugs, B2G: Media", "team_media_koi_blockers", team_media_koi_blockers, 1, ["hkoka@mozilla.com", ]]),
(1, ["Koi Blocking Nominiations for B2G: Communications", "team_comm_koi_nom", team_comm_koi_nom, 1, ["dscravaglieri@mozilla.com", ]]),
(1, ["Koi Blocker Bugs, B2G: Communications", "team_comm_koi_blockers", team_comm_koi_blockers, 1, ["dscravaglieri@mozilla.com", ]]),
(4, ["Koi Blocker Bugs, No Comment from Assignee in 3 days or more", "koi_assignee_no_comment_three_days", koi_assignee_no_comment_three_days, 1]),
(4, ["Koi Blocker Bugs, Regressions", "koi_regressions_unfixed", koi_regressions_unfixed, 1]),
]
def cleanUp():
for file in os.listdir(queries_dir):
if file.startswith(str(datetime.date.today())):
os.remove(os.path.join(queries_dir, file))
if __name__ == '__main__':
parser = ArgumentParser(__doc__)
parser.set_defaults(
queries_only=False,
)
parser.add_argument("-q", "--queries-only", dest="queries_only", action="store_true",
help="just create and print queries")
options, args = parser.parse_known_args()
queries = createQueriesList(print_all=options.queries_only)
if options.queries_only:
for url in urls:
print url
else:
command = [
scripts_dir + "email_nag.py",
"-t", "daily_b2g_email",
"--no-verification",
"-m", config['ldap_username'],
"-p", config['ldap_password']]
for query in queries:
command.append('-q')
command.append(query)
if ('-r') in args:
subject = datetime.datetime.today().strftime("%A %b %d") + " -- Daily B2G Drivers Rollup"
command.extend(['-e', 'b2g-release-drivers@mozilla.org'])
else:
subject = datetime.datetime.today().strftime("%A %b %d") + " -- Daily B2G Blocking Bugs Alert"
command.extend(['-s', subject])
# send all other args to email_nag script argparser
command.extend(args)
subprocess.call(command)
cleanUp()

Просмотреть файл

@ -1,474 +0,0 @@
#!/usr/bin/env python
"""
A script for automated nagging emails based on passed in queries
These can be collated into several 'queries' through the use of multiple query files with
a 'query_name' param set eg: 'Bugs tracked for Firefox Beta (13)'
Once the bugs have been collected from Bugzilla they are sorted into buckets cc: assignee manager
and to the assignee(s) or need-info? for each query
"""
import sys
import os
import smtplib
import subprocess
import tempfile
import collections
from datetime import datetime
from argparse import ArgumentParser
from bugzilla.agents import BMOAgent
import phonebook
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('templates'))
REPLY_TO_EMAIL = 'release-mgmt@mozilla.com'
DEFAULT_CC = ['release-mgmt@mozilla.com']
EMAIL_SUBJECT = ''
SMTP = 'smtp.mozilla.org'
people = phonebook.PhonebookDirectory()
# TODO - Sort by who a bug is blocked on (thanks @dturner)
# TODO - write tests!
# TODO - look into knocking out duplicated bugs in queries -- perhaps print out if there are dupes in queries when queries > 1
# TODO - should compare bugmail from API results to phonebook bugmail in to_lower()
def get_last_manager_comment(comments, manager):
# go through in reverse order to get most recent
for comment in comments[::-1]:
if person is not None:
if comment.creator.name == manager['mozillaMail'] or comment.creator.name == manager['bugzillaEmail']:
return comment.creation_time.replace(tzinfo=None)
return None
def get_last_assignee_comment(comments, person):
# go through in reverse order to get most recent
for comment in comments[::-1]:
if person is not None:
if comment.creator.name == person['mozillaMail'] or comment.creator.name == person['bugzillaEmail']:
return comment.creation_time.replace(tzinfo=None)
return None
def query_url_to_dict(url):
if (';')in url:
fields_and_values = url.split("?")[1].split(";")
else:
fields_and_values = url.split("?")[1].split("&")
d = collections.defaultdict(list)
for pair in fields_and_values:
(key, val) = pair.split("=")
if key != "list_id":
d[key].append(val)
return d
def generateEmailOutput(subject, queries, template, show_comment=False, manager_email=None, rollup=False, rollupEmail=None):
cclist = []
toaddrs = []
template_params = {}
# stripping off the templates dir, just in case it gets passed in the args
template = env.get_template(template.replace('templates/', '', 1))
def addToAddrs(bug):
if bug.assigned_to.name in people.people_by_bzmail:
person = dict(people.people_by_bzmail[bug.assigned_to.name])
if person['mozillaMail'] not in toaddrs:
toaddrs.append(person['mozillaMail'])
for query in queries.keys():
# Avoid dupes in the cclist from several queries
query_cc = queries[query].get('cclist', [])
for qcc in query_cc:
if qcc not in cclist:
cclist.append(qcc)
if query not in template_params:
template_params[query] = {'buglist': []}
if len(queries[query]['bugs']) != 0:
for bug in queries[query]['bugs']:
if 'show_summary' in queries[query]:
if queries[query]['show_summary'] == '1':
summary = bug.summary
else:
summary = ""
else:
summary = ""
template_params[query]['buglist'].append(
{
'id': bug.id,
'summary': summary,
# 'comment': bug.comments[-1].creation_time.replace(tzinfo=None),
'assignee': bug.assigned_to.real_name,
'flags': bug.flags
}
)
# more hacking for JS special casing
if bug.assigned_to.name == 'general@js.bugs' and 'nihsanullah@mozilla.com' not in toaddrs:
toaddrs.append('nihsanullah@mozilla.com')
# if needinfo? in flags, add the flag.requestee to the toaddrs instead of bug assignee
if bug.flags:
for flag in bug.flags:
if 'needinfo' in flag.name and flag.status == '?':
try:
person = dict(people.people_by_bzmail[str(flag.requestee)])
if person['mozillaMail'] not in toaddrs:
toaddrs.append(person['mozillaMail'])
except:
if str(flag.requestee) not in toaddrs:
toaddrs.append(str(flag.requestee))
else:
addToAddrs(bug)
else:
addToAddrs(bug)
message_body = template.render(queries=template_params, show_comment=show_comment)
if manager_email is not None and manager_email not in cclist:
cclist.append(manager_email)
# no need to and cc the manager if more than one email
if len(toaddrs) > 1:
for email in toaddrs:
if email in cclist:
toaddrs.remove(email)
if cclist == ['']:
cclist = None
if rollup:
joined_to = ",".join(rollupEmail)
else:
joined_to = ",".join(toaddrs)
message = (
"From: %s\r\n" % REPLY_TO_EMAIL
+ "To: %s\r\n" % joined_to
+ "CC: %s\r\n" % ",".join(cclist)
+ "Subject: %s\r\n" % subject
+ "\r\n"
+ message_body)
toaddrs = toaddrs + cclist
return toaddrs, message
def sendMail(toaddrs, msg, username, password, dryrun=False):
if dryrun:
print "\n****************************\n* DRYRUN: not sending mail *\n****************************\n"
print msg
else:
server = smtplib.SMTP_SSL(SMTP, 465)
server.set_debuglevel(1)
server.login(username, password)
# note: toaddrs is required for transport agents, the msg['To'] header is not modified
server.sendmail(username, toaddrs, msg)
server.quit()
if __name__ == '__main__':
parser = ArgumentParser(__doc__)
parser.set_defaults(
dryrun=False,
username=None,
password=None,
roll_up=False,
show_comment=False,
email_cc_list=None,
queries=[],
days_since_comment=-1,
verbose=False,
keywords=None,
email_subject=None,
no_verification=False,
)
parser.add_argument("-d", "--dryrun", dest="dryrun", action="store_true",
help="just do the query, and print emails to console without emailing anyone")
parser.add_argument("-m", "--mozilla-email", dest="mozilla_mail",
help="specify a specific address for sending email"),
parser.add_argument("-p", "--email-password", dest="email_password",
help="specify a specific password for sending email")
parser.add_argument("-b", "--bz-api-key", dest="bz_api_key",
help="Bugzilla API key")
parser.add_argument("-t", "--template", dest="template",
required=True,
help="template to use for the buglist output")
parser.add_argument("-e", "--email-cc-list", dest="email_cc_list",
action="append",
help="email addresses to include in cc when sending mail")
parser.add_argument("-q", "--query", dest="queries",
action="append",
required=True,
help="a file containing a dictionary of a bugzilla query")
parser.add_argument("-k", "--keyword", dest="keywords",
action="append",
help="keywords to collate buglists")
parser.add_argument("-s", "--subject", dest="email_subject",
required=True,
help="The subject of the email being sent")
parser.add_argument("-r", "--roll-up", dest="roll_up", action="store_true",
help="flag to get roll-up output in one email instead of creating multiple emails")
parser.add_argument("--show-comment", dest="show_comment", action="store_true",
help="flag to display last comment on a bug in the message output")
parser.add_argument("--days-since-comment", dest="days_since_comment",
help="threshold to check comments against to take action based on days since comment")
parser.add_argument("--verbose", dest="verbose", action="store_true",
help="turn on verbose output")
parser.add_argument("--no-verification", dest="no_verification", action="store_true",
help="don't wait for human verification of every email")
options, args = parser.parse_known_args()
try:
int(options.days_since_comment)
except:
if options.days_since_comment is not None:
parser.error("Need to provide a number for days \
since last comment value")
if options.email_cc_list is None:
options.email_cc_list = DEFAULT_CC
# Load our agent for BMO
bmo = BMOAgent(options.bz_api_key)
# Get the buglist(s)
collected_queries = {}
for query in options.queries:
# import the query
if os.path.exists(query):
info = {}
execfile(query, info)
query_name = info['query_name']
if query_name not in collected_queries:
collected_queries[query_name] = {
'channel': info.get('query_channel', ''),
'bugs': [],
'show_summary': info.get('show_summary', 0),
'cclist': options.email_cc_list,
}
if 'cc' in info:
for c in info.get('cc').split(','):
collected_queries[query_name]['cclist'].append(c)
if 'query_params' in info:
print "Gathering bugs from query_params in %s" % query
collected_queries[query_name]['bugs'] = bmo.get_bug_list(info['query_params'])
elif 'query_url' in info:
print "Gathering bugs from query_url in %s" % query
collected_queries[query_name]['bugs'] = bmo.get_bug_list(query_url_to_dict(info['query_url']))
# print "DEBUG: %d bug(s) found for query %s" % \
# (len(collected_queries[query_name]['bugs']), info['query_url'])
else:
print "Error - no valid query params or url in the config file"
sys.exit(1)
else:
print "Not a valid path: %s" % query
total_bugs = 0
for channel in collected_queries.keys():
total_bugs += len(collected_queries[channel]['bugs'])
print "Found %s bugs total for %s queries" % (total_bugs, len(collected_queries.keys()))
print "Queries to collect: %s" % collected_queries.keys()
managers = people.managers
manual_notify = {}
counter = 0
def add_to_managers(manager_email, query, info={}):
if manager_email not in managers:
managers[manager_email] = {}
managers[manager_email]['nagging'] = {query: {'bugs': [bug],
'show_summary': info.get('show_summary', 0),
'cclist': info.get('cclist', [])}, }
return
if 'nagging' in managers[manager_email]:
if query in managers[manager_email]['nagging']:
managers[manager_email]['nagging'][query]['bugs'].append(bug)
if options.verbose:
print "Adding %s to %s in nagging for %s" % \
(bug.id, query, manager_email)
else:
managers[manager_email]['nagging'][query] = {
'bugs': [bug],
'show_summary': info.get('show_summary', 0),
'cclist': info.get('cclist', [])
}
if options.verbose:
print "Adding new query key %s for bug %s in nagging \
and %s" % (query, bug.id, manager_email)
else:
managers[manager_email]['nagging'] = {query: {'bugs': [bug],
'show_summary': info.get('show_summary', 0),
'cclist': info.get('cclist', [])}, }
if options.verbose:
print "Creating query key %s for bug %s in nagging and \
%s" % (query, bug.id, manager_email)
for query, info in collected_queries.items():
if len(collected_queries[query]['bugs']) != 0:
manual_notify[query] = {'bugs': [], 'show_summary': info.get('show_summary', 0)}
for b in collected_queries[query]['bugs']:
counter = counter + 1
send_mail = True
bug = bmo.get_bug(b.id)
manual_notify[query]['bugs'].append(bug)
assignee = bug.assigned_to.name
if "@" not in assignee:
print "Error - email address expect. Found '" + assignee + "' instead"
print "Check that the authentication worked correctly"
sys.exit(1)
if assignee in people.people_by_bzmail:
person = dict(people.people_by_bzmail[assignee])
else:
person = None
# kick bug out if days since comment check is on
if options.days_since_comment != -1:
# try to get last_comment by assignee & manager
if person is not None:
last_comment = get_last_assignee_comment(bug.comments, person)
if 'manager' in person and person['manager'] is not None:
manager_email = person['manager']['dn'].split('mail=')[1].split(',')[0]
manager = people.people[manager_email]
last_manager_comment = get_last_manager_comment(bug.comments, people.people_by_bzmail[manager['bugzillaEmail']])
# set last_comment to the most recent of last_assignee and last_manager
if last_manager_comment is not None and last_comment is not None and last_manager_comment > last_comment:
last_comment = last_manager_comment
# otherwise just get the last comment
else:
last_comment = bug.comments[-1].creation_time.replace(tzinfo=None)
if last_comment is not None:
timedelta = datetime.now() - last_comment
if timedelta.days <= int(options.days_since_comment):
if options.verbose:
print "Skipping bug %s since it's had an assignee or manager comment within the past %s days" % (bug.id, options.days_since_comment)
send_mail = False
counter = counter - 1
manual_notify[query]['bugs'].remove(bug)
else:
if options.verbose:
print "This bug needs notification, it's been %s since last comment of note" % timedelta.days
if send_mail:
if 'nobody' in assignee:
if options.verbose:
print "No one assigned to: %s, will be in the manual notification list..." % bug.id
# TODO - get rid of this, SUCH A HACK!
elif 'general@js.bugs' in assignee:
if options.verbose:
print "No one assigned to JS bug: %s, adding to Naveed's list..." % bug.id
add_to_managers('nihsanullah@mozilla.com', query, info)
else:
if bug.assigned_to.real_name is not None:
if person is not None:
# check if assignee is already a manager, add to their own list
if 'mozillaMail' in managers:
add_to_managers(person['mozillaMail'], query, info)
# otherwise we search for the assignee's manager
else:
# check for manager key first, a few people don't have them
if 'manager' in person and person['manager'] is not None:
manager_email = person['manager']['dn'].split('mail=')[1].split(',')[0]
if manager_email in managers:
add_to_managers(manager_email, query, info)
elif manager_email in people.vices:
# we're already at the highest level we'll go
if assignee in managers:
add_to_managers(assignee, query, info)
else:
if options.verbose:
print "%s has a V-level for a manager, and is not in the manager list" % assignee
managers[person['mozillaMail']] = {}
add_to_managers(person['mozillaMail'], query, info)
else:
# try to go up one level and see if we find a manager
if manager_email in people.people:
person = dict(people.people[manager_email])
manager_email = person['manager']['dn'].split('mail=')[1].split(',')[0]
if manager_email in managers:
add_to_managers(manager_email, query, info)
else:
print "Manager could not be found: %s" % manager_email
# if you don't have a manager listed, but are an employee, we'll nag you anyway
else:
add_to_managers(person['mozillaMail'], query, info)
print "%s's entry doesn't list a manager! Let's ask them to update phonebook but in the meantime they get the email directly." % person['name']
if options.roll_up:
# only send one email
toaddrs, msg = generateEmailOutput(subject=options.email_subject,
queries=manual_notify,
template=options.template,
show_comment=options.show_comment,
rollup=options.roll_up,
rollupEmail=options.email_cc_list)
if options.email_password is None or options.mozilla_mail is None:
print "Please supply a username/password (-m, -p) for sending email"
sys.exit(1)
if not options.dryrun:
print "SENDING EMAIL"
sendMail(toaddrs, msg, options.mozilla_mail, options.email_password, options.dryrun)
else:
# Get yr nag on!
for email, info in managers.items():
inp = ''
if 'nagging' in info:
toaddrs, msg = generateEmailOutput(
subject=options.email_subject,
manager_email=email,
queries=info['nagging'],
template=options.template,
show_comment=options.show_comment)
while True and not options.no_verification:
print "\nRelMan Nag is ready to send the following email:\n<------ MESSAGE BELOW -------->"
print msg
print "<------- END MESSAGE -------->\nWould you like to send now?"
inp = raw_input('\n Please select y/Y to send, v/V to edit, or n/N to skip and continue to next email: ')
if inp != 'v' and inp != 'V':
break
tempfilename = tempfile.mktemp()
temp_file = open(tempfilename, 'w')
temp_file.write(msg)
temp_file.close()
subprocess.call(['vi', tempfilename])
temp_file = open(tempfilename, 'r')
msg = temp_file.read()
toaddrs = msg.split("To: ")[1].split("\r\n")[0].split(',') + msg.split("CC: ")[1].split("\r\n")[0].split(',')
os.remove(tempfilename)
if inp == 'y' or inp == 'Y' or options.no_verification:
if options.email_password is None or options.mozilla_mail is None:
print "Please supply a username/password (-m, -p) for sending email"
sys.exit(1)
if not options.dryrun:
print "SENDING EMAIL"
sendMail(toaddrs, msg, options.mozilla_mail, options.email_password, options.dryrun)
sent_bugs = 0
for query, info in info['nagging'].items():
sent_bugs += len(info['bugs'])
# take sent bugs out of manual notification list
for bug in info['bugs']:
manual_notify[query]['bugs'].remove(bug)
counter = counter - sent_bugs
if not options.roll_up:
emailed_bugs = []
# Send RelMan the manual notification list only when there are bugs that didn't go out
msg_body = """\n******************************************\nNo nag emails were generated for these bugs because
they are either assigned to no one or to non-employees (though ni? on non-employees will get nagged).
\nYou will need to look at the following bugs:\n******************************************\n\n"""
for k, v in manual_notify.items():
if len(v['bugs']) != 0:
for bug in v['bugs']:
if bug.id not in emailed_bugs:
if k not in msg_body:
msg_body += "\n=== %s ===\n" % k
emailed_bugs.append(bug.id)
msg_body += "http://bugzil.la/" + "%s -- assigned to: %s\n -- Last commented on: %s\n" % (bug.id, bug.assigned_to.real_name, bug.comments[-1].creation_time.replace(tzinfo=None))
msg = ("From: %s\r\n" % REPLY_TO_EMAIL
+ "To: %s\r\n" % REPLY_TO_EMAIL
+ "Subject: RelMan Attention Needed: %s\r\n" % options.email_subject
+ "\r\n"
+ msg_body)
sendMail(['release-mgmt@mozilla.com'], msg, options.mozilla_mail, options.email_password, options.dryrun)

Просмотреть файл

@ -1,77 +0,0 @@
import requests
import os
import json
# NOTE: You must create a file for CONFIG_JSON with your LDAP auth in it like:
# {
# "username": "username",
# "password": "password"
# }
#
# In order to access the phonebook data
MY_DIR = os.path.abspath(os.path.dirname(__file__))
PEOPLE_FILENAME = os.path.join(MY_DIR, 'people.json')
CONFIG_JSON = os.path.join(os.path.dirname(__file__), "configs/config.json")
BASE_URL = 'https://phonebook.mozilla.org'
PEOPLE_URL = '%s/search.php?query=*&format=fligtar' % BASE_URL
'''
a single phonebook entry data looks like this when you pull it from JSON:
'email' = {
ims : [],
name : 'name',
title : 'title',
phones : 'string of numbers & assignments',
ext : XXX,
manager : {u'dn': u'mail=manager@mozilla.com,o=com,dc=mozilla', u'cn': u'Manager Name'},
bugzillaEmail : 'email@example.com'
## this script adds in:
mozillaMail : 'email@mozilla.com'
}
'''
class PhonebookDirectory():
def __init__(self, config=CONFIG_JSON):
config = json.load(open(config, 'r'))
print "Fetching people from phonebook..."
self.people = json.loads(requests.get(PEOPLE_URL, auth=(config['ldap_username'], config['ldap_password'])).content)
self.people_by_bzmail = self.get_people_by_bzmail()
self.managers = self.get_managers()
self.vices = self.get_vices()
def get_managers(self):
managers = {}
for email, info in self.people.items():
if self.people[email]['title'] is not None:
if 'director' in self.people[email]['title'].lower() or 'manager' in self.people[email]['title'].lower():
managers[email] = info
# HACK! don't have titles with manager/director or missing bugmail address
if email in ('dtownsend@mozilla.com', 'dougt@mozilla.com', 'mfinkle@mozilla.com', 'bsmedberg@mozilla.com', 'blassey@mozilla.com') and email not in managers.keys():
managers[email] = info
return managers
def get_vices(self):
vices = {}
for email, info in self.people.items():
if self.people[email]['title'] is not None:
if 'vice' in self.people[email]['title'].lower():
vices[email] = info
return vices
def get_people_by_bzmail(self):
temp = {}
for email, info in self.people.items():
# if someone doesn't have a bugzillaEmail set, we'll try their mozilla mail instead
if info.get('bugzillaEmail'):
temp[info['bugzillaEmail']] = dict(info.items())
temp[info['bugzillaEmail']].update({'mozillaMail': email})
else:
temp[email] = dict(info.items())
temp[email].update({'mozillaMail': email})
return temp

Просмотреть файл

@ -1,146 +0,0 @@
#!/usr/bin/python
import urllib2
import re
import datetime
import subprocess
import os
import json
from argparse import ArgumentParser
CONFIG_JSON = os.getcwd() + "/scripts/configs/config.json"
config = json.load(open(CONFIG_JSON, 'r'))
scripts_dir = os.getcwd() + "/scripts/"
queries_dir = os.getcwd() + "/queries/"
def getTemplateValue(url):
version_regex = re.compile(".*<p>(.*)</p>.*")
template_page = urllib2.urlopen(url).read().replace('\n', '')
parsed_template = version_regex.match(template_page)
return parsed_template.groups()[0]
def getReportURL(approval_flag, span):
a = urllib2.urlopen("https://bugzilla.mozilla.org/page.cgi?id=release_tracking_report.html&q=" + approval_flag + "%3A%2B%3A" + span + "%3A0%3Aand%3A")
return a.url
no_nag = ";field3-1-0=status_whiteboard;type3-1-0=notsubstring;value3-1-0=[no-nag]"
beta_version = getTemplateValue("https://wiki.mozilla.org/Template:BETA_VERSION")
aurora_version = getTemplateValue("https://wiki.mozilla.org/Template:AURORA_VERSION")
central_version = getTemplateValue("https://wiki.mozilla.org/Template:CENTRAL_VERSION")
esr_version = getTemplateValue("https://wiki.mozilla.org/Template:ESR_VERSION")
cycle_span = getTemplateValue("https://wiki.mozilla.org/Template:CURRENT_CYCLE")
unlanded_beta_url = getReportURL("approval-mozilla-beta", cycle_span) + ";field0-0-0=cf_status_firefox" + beta_version + ";type0-0-0=nowordssubstr;value0-0-0=unaffected,fixed,verified,wontfix,disabled" + ";field0-1-0=cf_tracking_firefox" + beta_version + ";type0-1-0=equals;value0-1-0=%2B;field0-2-0=status_whiteboard;type0-2-0=notsubstring;value0-2-0=[no-nag]"
unlanded_aurora_url = getReportURL("approval-mozilla-aurora", cycle_span) + ";field0-0-0=cf_status_firefox" + aurora_version + ";type0-0-0=nowordssubstr;value0-0-0=unaffected,fixed,verified,wontfix,disabled" + ";field0-1-0=cf_tracking_firefox" + aurora_version + ";type0-1-0=equals;value0-1-0=%2B;field0-2-0=status_whiteboard;type0-2-0=notsubstring;value0-2-0=[no-nag]"
unlanded_esr38_url = getReportURL("approval-mozilla-esr38", cycle_span) + ";field0-0-0=cf_status_firefox_esr" + esr_version + ";type0-0-0=nowordssubstr;value0-0-0=unaffected,fixed,verified,wontfix,disabled" + ";field0-1-0=cf_tracking_firefox_esr" + esr_version + ";type0-1-0=equals;value0-1-0=%2B;field0-2-0=status_whiteboard;type0-2-0=notsubstring;value0-2-0=[no-nag]"
tracking_beta_url = "https://bugzilla.mozilla.org/buglist.cgi?type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;field0-1-0=cf_status_firefox" + beta_version + ";field0-0-0=cf_tracking_firefox" + beta_version + ";field2-0-0=flagtypes.name;value0-3-0=unaffected;value0-6-0=verified%20disabled;value0-1-0=wontfix;field0-5-0=cf_status_firefox" + beta_version + ";type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;negate1=1;field0-3-0=cf_status_firefox" + beta_version + ";type0-4-0=notequals;value2-0-0=approval-mozilla-beta%3F;field0-6-0=cf_status_firefox" + beta_version + ";type2-0-0=notsubstring;value0-2-0=fixed;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + beta_version + ";field0-4-0=cf_status_firefox" + beta_version + ";type0-6-0=notequals;field1-0-0=bug_group;field2-1-0=status_whiteboard;type2-1-0=notsubstring;value2-1-0=[no-nag]"
tracking_aurora_url = "https://bugzilla.mozilla.org/buglist.cgi?type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=notequals;field0-1-0=cf_status_firefox" + aurora_version + ";field0-0-0=cf_tracking_firefox" + aurora_version + ";field2-0-0=flagtypes.name;value0-3-0=unaffected;value0-6-0=verified%20disabled;value0-1-0=wontfix;field0-5-0=cf_status_firefox" + aurora_version + ";type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;negate1=1;field0-3-0=cf_status_firefox" + aurora_version + ";type0-4-0=notequals;value3-0-0=%2B;value2-0-0=approval-mozilla-aurora%3F;field0-6-0=cf_status_firefox" + aurora_version + ";field3-0-0=cf_tracking_firefox" + beta_version + ";type2-0-0=notsubstring;value0-2-0=fixed;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + aurora_version + ";field0-4-0=cf_status_firefox" + aurora_version + ";type0-6-0=notequals;field1-0-0=bug_group;field2-1-0=status_whiteboard;type2-1-0=notsubstring;value2-1-0=[no-nag]"
tracking_central_url = "https://bugzilla.mozilla.org/buglist.cgi?negate1=1;field0-3-0=cf_status_firefox" + central_version + ";value3-1-0=%2B;type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=notequals;field0-1-0=cf_status_firefox" + central_version + ";field0-0-0=cf_tracking_firefox" + central_version + ";type0-4-0=notequals;value3-0-0=%2B;value2-0-0=approval-mozilla-aurora%3F;field2-0-0=flagtypes.name;field3-1-0=cf_tracking_firefox" + aurora_version + ";field0-6-0=cf_status_firefox" + central_version + ";value0-3-0=unaffected;field3-0-0=cf_tracking_firefox" + beta_version + ";type2-0-0=notsubstring;value0-2-0=fixed;value0-6-0=verified%20disabled;value0-1-0=wontfix;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + central_version + ";type3-1-0=notequals;field0-5-0=cf_status_firefox" + central_version + ";field0-4-0=cf_status_firefox" + central_version + ";type0-6-0=notequals;type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;field1-0-0=bug_group;field2-1-0=status_whiteboard;type2-1-0=notsubstring;value2-1-0=[no-nag]"
tracking_beta_touch_url = "https://bugzilla.mozilla.org/buglist.cgi?negate1=1;field0-3-0=cf_status_firefox" + beta_version + ";type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=greaterthan;field0-1-0=cf_status_firefox" + beta_version + ";field0-0-0=cf_tracking_firefox" + beta_version + ";type0-4-0=notequals;value3-0-0=3;value2-0-0=approval-mozilla-beta%3F;field2-0-0=flagtypes.name;field0-6-0=cf_status_firefox" + beta_version + ";value0-3-0=unaffected;field3-0-0=days_elapsed;type2-0-0=notsubstring;value0-2-0=fixed;value0-6-0=verified%20disabled;value0-1-0=wontfix;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + beta_version + ";field0-5-0=cf_status_firefox" + beta_version + ";field0-4-0=cf_status_firefox" + beta_version + ";type0-6-0=notequals;type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;field1-0-0=bug_group" + no_nag
tracking_aurora_touch_url = "https://bugzilla.mozilla.org/buglist.cgi?negate1=1;field0-3-0=cf_status_firefox" + aurora_version + ";type4-0-0=greaterthan;type1-0-0=equals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=notequals;field0-1-0=cf_status_firefox" + aurora_version + ";field0-0-0=cf_tracking_firefox" + aurora_version + ";type0-4-0=notequals;value3-0-0=%2B;field4-0-0=days_elapsed;value2-0-0=approval-mozilla-aurora%3F;field2-0-0=flagtypes.name;field0-6-0=cf_status_firefox" + aurora_version + ";value0-3-0=unaffected;field3-0-0=cf_tracking_firefox" + beta_version + ";type2-0-0=notsubstring;value0-2-0=fixed;value0-6-0=verified%20disabled;value0-1-0=wontfix;type0-3-0=notequals;value1-0-0=core-security;field0-2-0=cf_status_firefox" + aurora_version + ";field0-5-0=cf_status_firefox" + aurora_version + ";value4-0-0=3;field0-4-0=cf_status_firefox" + aurora_version + ";type0-6-0=notequals;type0-0-0=equals;value0-0-0=%2B;type0-2-0=notequals;field1-0-0=bug_group" + no_nag
tracking_central_touch_url = "https://bugzilla.mozilla.org/buglist.cgi?negate1=1;field0-3-0=cf_status_firefox" + central_version + ";type1-0-0=equals;type2-1-0=notequals;type0-1-0=notequals;type0-5-0=notequals;value0-5-0=disabled;value0-4-0=verified;type3-0-0=greaterthan;field0-1-0=cf_status_firefox" + central_version + ";field0-0-0=cf_tracking_firefox" + central_version + ";type0-4-0=notequals;value3-0-0=3;value2-0-0=%2B;field2-0-0=cf_tracking_firefox" + beta_version + ";field0-6-0=cf_status_firefox" + central_version + ";value0-3-0=unaffected;field3-0-0=days_elapsed;type2-0-0=notequals;value0-2-0=fixed;value0-6-0=verified%" + central_version + "disabled;value0-1-0=wontfix;type0-3-0=notequals;value2-1-0=%2B;value1-0-0=core-security;field0-2-0=cf_status_firefox" + central_version + ";field0-5-0=cf_status_firefox" + central_version + ";field0-4-0=cf_status_firefox" + central_version + ";type0-6-0=notequals;type0-0-0=equals;value0-0-0=%2B;field2-1-0=cf_tracking_firefox" + aurora_version + ";type0-2-0=notequals;field1-0-0=bug_group" + no_nag
tracking_esr38_url = "https://bugzilla.mozilla.org/buglist.cgi?type0-1-0=nowordssubstr;field0-1-0=cf_status_firefox_esr" + esr_version + ";field0-0-0=cf_tracking_firefox_esr" + esr_version + ";value0-1-0=fixed%20verified%20disabled%20wontfix%20unaffected;type0-0-0=equals;value0-0-0=" + beta_version + "%2B;field0-2-0=status_whiteboard;type0-2-0=notsubstring;value0-2-0=[no-nag]"
needinfo_beta_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_tracking_firefox" + beta_version + "&v6=fixed%2Cwontfix%2Cunaffected%2Cverified%2Cdisabled&o1=anywords&resolution=---&o6=anywords&f12=flagtypes.name&v12=needinfo%3F&f11=OP&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&o12=equals&v1=%2B%2C%3F&f6=cf_status_firefox" + beta_version + "&n6=1"
needinfo_aurora_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_tracking_firefox" + aurora_version + "&v6=fixed%2Cwontfix%2Cunaffected%2Cverified%2Cdisabled&o1=anywords&resolution=---&o6=anywords&f12=flagtypes.name&v12=needinfo%3F&f11=OP&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&o12=equals&v1=%2B%2C%3F&f6=cf_status_firefox" + aurora_version + "&n6=1"
needinfo_central_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_tracking_firefox" + central_version + "&v6=fixed%2Cwontfix%2Cunaffected%2Cverified%2Cdisabled&o1=anywords&resolution=---&o6=anywords&f12=flagtypes.name&v12=needinfo%3F&f11=OP&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&o12=equals&v1=%2B%2C%3F&f6=cf_status_firefox" + central_version + "&n6=1"
needinfo_esr38_url = "https://bugzilla.mozilla.org/buglist.cgi?f1=cf_tracking_firefox_esr" + esr_version + "&v6=fixed%2Cwontfix%2Cunaffected%2Cverified%2Cdisabled&o1=anywords&resolution=---&o6=anywords&f12=flagtypes.name&v12=needinfo%3F&f11=OP&bug_status=UNCONFIRMED,NEW,READY,ASSIGNED,REOPENED&o12=equals&v1=%2B%2C%3F&f6=cf_status_firefox_esr" + esr_version + "&n6=1"
# TODO - sort the queries according to a priority flag
# TODO - separate queries for sec bugs, for now hide summary
# TODO - fix the 'untouched' queries
# tracked beta/aurora/nightly nobodies: https://bugzilla.mozilla.org/buglist.cgi?j_top=OR&f1=cf_tracking_firefox25&o3=equals&v3=%2B&o1=equals&emailtype1=exact&o2=equals&emailassigned_to1=1&f3=cf_tracking_firefox27&f2=cf_tracking_firefox26&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&email1=nobody%40mozilla.org&v1=%2B&v2=%2B
# need same but for untouched
urls = [
(5, ["Unlanded Beta " + beta_version + " Bugs", "unlanded_beta", unlanded_beta_url, 0]),
(5, ["Unlanded Aurora " + aurora_version + " Bugs", "unlanded_aurora", unlanded_aurora_url, 0]),
(5, ["Unlanded ESR38 Bugs", "unlanded_esr38", unlanded_esr38_url, 0]),
(5, ["Tracked or Nominated for Tracking with Need-Info? Beta " + beta_version + " Bugs", "needinfo_beta", needinfo_beta_url, 0]),
(5, ["Tracked or Nominated for Tracking with Need-Info? Aurora " + aurora_version + " Bugs", "needinfo_aurora", needinfo_aurora_url, 0]),
(5, ["Tracked or Nominated for Tracking with Need-Info? Nightly " + central_version + " Bugs", "needinfo_central", needinfo_central_url, 0]),
(5, ["Tracked or Nominated for Tracking with Need-Info? ESR38 Bugs", "needinfo_esr38", needinfo_esr38_url, 0]),
(0, ["Bugs Tracked for Beta " + beta_version, "tracking_beta", tracking_beta_url, 0]),
(0, ["Bugs Tracked for Aurora " + aurora_version, "tracking_aurora", tracking_aurora_url, 0]),
(0, ["Bugs Tracked for Nightly " + central_version, "tracking_central", tracking_central_url, 0]),
(0, ["Bugs Tracked for ESR38", "tracking_esr38", tracking_esr38_url, 0]),
(3, ["Tracked Beta " + beta_version + " Bugs, untouched this week", "untouched_tracking_beta", tracking_beta_touch_url, 0]),
(3, ["Tracked Aurora " + aurora_version + " Bugs, untouched this week", "untouched_tracking_aurora", tracking_aurora_touch_url, 0]),
(3, ["Tracked Nightly " + central_version + " Bugs, untouched this week", "untouched_tracking_nightly", tracking_central_touch_url, 0])
]
def createQuery(title, short_title, url, show_summary):
file_name = queries_dir + str(datetime.date.today()) + '_' + short_title
if not os.path.exists(queries_dir):
os.makedirs(queries_dir)
qf = open(file_name, 'w')
qf.write("query_name = \'" + title + "\'\n")
qf.write("query_url = \'" + url + "\'\n")
qf.write("show_summary = \'" + str(show_summary) + "\'\n")
return file_name
def createQueriesList(print_all):
queries = []
weekday = datetime.datetime.today().weekday()
for url in urls:
if weekday >= 0 and weekday < 5 and url[0] == 5:
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3]))
if weekday == 0 and url[0] == 0:
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3]))
if weekday == 3 and url[0] == 3:
queries.append(createQuery(title=url[1][0], short_title=url[1][1], url=url[1][2], show_summary=url[1][3]))
print queries
return queries
def cleanUp():
for file in os.listdir(queries_dir):
if file.startswith(str(datetime.date.today())):
os.remove(os.path.join(queries_dir, file))
if __name__ == '__main__':
parser = ArgumentParser(__doc__)
parser.set_defaults(
queries_only=False,
)
parser.add_argument("-q", "--queries-only", dest="queries_only", action="store_true",
help="just create and print queries")
options, args = parser.parse_known_args()
queries = createQueriesList(print_all=options.queries_only)
if options.queries_only:
for url in urls:
print url
else:
command = [
scripts_dir + "email_nag.py",
"-t", "daily_email",
"--no-verification",
"-m", config['ldap_username'],
"-p", config['ldap_password'],
"-b", config['bz_api_key'],
"-e", "release-mgmt@mozilla.com"]
for query in queries:
command.append('-q')
command.append(query)
subject = datetime.datetime.today().strftime("%A %b %d") + " -- Daily Release Tracking Alert"
command.extend(['-s', subject])
# send all other args to email_nag script argparser
command.extend(args)
subprocess.call(command)
cleanUp()