Beispiel #1
0
def lookup_remotelink(options, jira_id):
    data = shared.jiraquery(options, "/rest/api/latest/issue/" + jira_id + "/remotelink")
    if (data and len(data) >=1):
        # print "[INFO] Found remotelink for " + jira
        return data[0]
    else:
        return
Beispiel #2
0
def lookup_remotelink(options, jira_id):
    data = shared.jiraquery(
        options, "/rest/api/latest/issue/" + jira_id + "/remotelink")
    if (data and len(data) >= 1):
        # print "[INFO] Found remotelink for " + jira
        return data[0]
    else:
        return
Beispiel #3
0
def lookup_proxy(options, bug):
    #TODO should keep a local cache from BZ->JIRA to avoid constant querying
    payload = {'jql': 'project = ' + ECLIPSE_PROJECT + ' and summary ~ \'EBZ#' + str(bug.id) +'\'', 'maxResults' : 5}
    data = shared.jiraquery(options, "/rest/api/latest/search?" + urllib.urlencode(payload))
    count = len(data['issues'])
    if (count == 0):
        return 
    elif (count == 1):
        return data['issues'][0]
    else:
        print "[WARN] Multiple issues found for " + str(bug.id)
        print data['issues']
        return 
Beispiel #4
0
def fetch_email(username, fallback, email_addresses):
    if username in email_addresses:
        return email_addresses[username]
    else:
        found = None
        payload = {'username': username}
        user_data = shared.jiraquery(options, "/rest/api/2/user?" + urllib.urlencode(payload))
        if 'emailAddress' in user_data:
            found = str(user_data['emailAddress'])
            email_addresses[username]=found
        else:
            print 'No email found for ' + username + ' using ' + str(fallback)
            found = fallback
            # don't cache if not found
        return found
def fetch_email(username, fallback, email_addresses):
    if username in email_addresses:
        return email_addresses[username]
    else:
        found = None
        payload = {'username': username}
        user_data = shared.jiraquery(options, "/rest/api/2/user?" + urllib.urlencode(payload))
        if 'emailAddress' in user_data:
            found = str(user_data['emailAddress'])
            email_addresses[username]=found
        else:
            print 'No email found for ' + username + ' using ' + str(fallback)
            found = fallback
            # don't cache if not found
        return found
Beispiel #6
0
def lookup_proxy(options, bug):
    #TODO should keep a local cache from BZ->JIRA to avoid constant querying
    payload = {
        'jql':
        'project = ' + ECLIPSE_PROJECT + ' and summary ~ \'EBZ#' +
        str(bug.id) + '\'',
        'maxResults':
        5
    }
    data = shared.jiraquery(
        options, "/rest/api/latest/search?" + urllib.urlencode(payload))
    count = len(data['issues'])
    if (count == 0):
        return
    elif (count == 1):
        return data['issues'][0]
    else:
        print "[WARN] Multiple issues found for " + str(bug.id)
        print data['issues']
        return
Beispiel #7
0
def listVersions(project,
                 pattern=".*",
                 released=None,
                 hasReleaseDate=None,
                 archived=None,
                 hasStartDate=None,
                 codefrozen=None,
                 lowerLimit=None,
                 upperLimit=None,
                 index=None):
    """Return list of versions for a specific project matching a pattern and a list of optional filters.

           arguments:
            project -- the jira project key (i.e. 'JBIDE') (required)
            pattern -- regular expression that the version name should match (i.e. '4.2.*') (default=.*)
            released -- boolean to state if the version should be released or not. (default=None)
            archived -- boolean to state if the version should be archived or not. (default=None)
            hasStartDate -- boolean to state if the version should have a start date. (default=None)
            hasReleaseDate -- boolean to state if the version should have a released date. (default=None)
            codefrozen -- boolean if description of version contains (codefreeze: <date>) and date has occurred true will include it otherwise exclude it. 
            upperLimit -- upper limit (default=None)
            lowerLimit -- lower limit (default=None)
            index -- integer to state which index to get (supports negative indexing too, -1=last element), if index out of range nothing is returned. (default=None)

            examples:
            listVersions("JBIDE", "4.2.*") -- versions in JBIDE starting with "4.2."
            listVersions("JBIDE", "4.2.*", upperLimit=2) -- first two version of 4.2.*
            listVersions("JBIDE", "4.2.*", released=False, upperLimit=2) -- first two version that are released in 4.2.*
            listVersions("JBIDE", "4.2.*", released=False) -- non-released 4.2.* versions
            listVersions("JBIDE", "4.2.*|4.3.*", released=False, hasReleaseDate=True) -- non-released that has release date in either 4.2 or 4.3 streams
            listVersions("JBIDE", "4.2.*|4.3.*", released=False, hasStartDate=True) -- non-released that has start date in either 4.2 or 4.3 streams
            listVersions("JBIDE", ".*", archived=True, hasReleaseDate=True, lowerLimit=2, lowerLimit=4)
    """

    versions = shared.jiraquery(
        options, "/rest/api/latest/project/" + project + "/versions")
    if options.verbose:
        print(pattern)
        #print codefrozen

    versionmatch = re.compile(pattern)
    foundversions = []
    for version in versions:
        if versionmatch.match(version['name']):
            foundversions.append(version)

    print "after versionmatch: " + dumpVersions(foundversions)

    if released is not None:
        foundversions = filter(lambda v: released == v['released'],
                               foundversions)
        #print "after released: " + dumpVersions(foundversions)

    if hasReleaseDate is not None:
        foundversions = filter(
            lambda v: hasFieldOrNot('releaseDate', hasReleaseDate, v),
            foundversions)
        #print "after hasReleaseDate: " + dumpVersions(foundversions)

    if hasStartDate is not None:
        foundversions = filter(
            lambda v: hasFieldOrNot('startDate', hasStartDate, v),
            foundversions)
        #print "after hasStartDate: " + dumpVersions(foundversions)

    if archived is not None:
        foundversions = filter(lambda v: archived == v['archived'],
                               foundversions)
        #print "after archived: " + dumpVersions(foundversions)

    if codefrozen is not None:
        foundversions = filter(lambda v: isCodefrozenToday(v, codefrozen),
                               foundversions)
        #print "after codefrozen: " + dumpVersions(foundversions)

    if upperLimit or lowerLimit:
        foundversions = foundversions[lowerLimit:upperLimit]
        #print "after limits: " + dumpVersions(foundversions)

    if index is not None:
        try:
            foundversions = [foundversions[index]]
        except IndexError:
            foundversions = []
        #print "after index: " + dumpVersions(foundversions)

    foundversions = map(lambda v: v['name'], foundversions)

    return ", ".join(foundversions)
def listVersions(project, pattern=".*", released=None, hasReleaseDate=None, archived=None, hasStartDate=None, codefrozen=None, lowerLimit=None, upperLimit=None, index=None):
    """Return list of versions for a specific project matching a pattern and a list of optional filters.

           arguments:
            project -- the jira project key (i.e. 'JBIDE') (required)
            pattern -- regular expression that the version name should match (i.e. '4.2.*') (default=.*)
            released -- boolean to state if the version should be released or not. (default=None)
            archived -- boolean to state if the version should be archived or not. (default=None)
            hasStartDate -- boolean to state if the version should have a start date. (default=None)
            hasReleaseDate -- boolean to state if the version should have a released date. (default=None)
            codefrozen -- boolean if description of version contains (codefreeze: <date>) and date has occurred true will include it otherwise exclude it. 
            upperLimit -- upper limit (default=None)
            lowerLimit -- lower limit (default=None)
            index -- integer to state which index to get (supports negative indexing too, -1=last element), if index out of range nothing is returned. (default=None)

            examples:
            listVersions("JBIDE", "4.2.*") -- versions in JBIDE starting with "4.2."
            listVersions("JBIDE", "4.2.*", upperLimit=2) -- first two version of 4.2.*
            listVersions("JBIDE", "4.2.*", released=False, upperLimit=2) -- first two version that are released in 4.2.*
            listVersions("JBIDE", "4.2.*", released=False) -- non-released 4.2.* versions
            listVersions("JBIDE", "4.2.*|4.3.*", released=False, hasReleaseDate=True) -- non-released that has release date in either 4.2 or 4.3 streams
            listVersions("JBIDE", "4.2.*|4.3.*", released=False, hasStartDate=True) -- non-released that has start date in either 4.2 or 4.3 streams
            listVersions("JBIDE", ".*", archived=True, hasReleaseDate=True, lowerLimit=2, lowerLimit=4)
    """

    versions = shared.jiraquery(options,"/rest/api/latest/project/" + project + "/versions")
    if options.verbose:
        print(pattern)
        #print codefrozen
        
    versionmatch = re.compile(pattern)
    foundversions = []
    for version in versions:
        if versionmatch.match(version['name']):
            foundversions.append(version)

    print "after versionmatch: " + dumpVersions(foundversions)
    
    if released is not None:
        foundversions = filter(lambda v: released == v['released'], foundversions)
        #print "after released: " + dumpVersions(foundversions)
    
    if hasReleaseDate is not None:
        foundversions = filter(lambda v: hasFieldOrNot('releaseDate', hasReleaseDate, v), foundversions)
        #print "after hasReleaseDate: " + dumpVersions(foundversions)
    
    if hasStartDate is not None:
        foundversions = filter(lambda v: hasFieldOrNot('startDate', hasStartDate, v), foundversions)
        #print "after hasStartDate: " + dumpVersions(foundversions)
    
    if archived is not None:
        foundversions = filter(lambda v: archived == v['archived'], foundversions)
        #print "after archived: " + dumpVersions(foundversions)

    if codefrozen is not None:
        foundversions = filter(lambda v: isCodefrozenToday(v, codefrozen), foundversions)
        #print "after codefrozen: " + dumpVersions(foundversions)
    
    if upperLimit or lowerLimit:
        foundversions = foundversions[lowerLimit:upperLimit]
        #print "after limits: " + dumpVersions(foundversions)
    
    if index is not None:
        try:
            foundversions = [foundversions[index]]
        except IndexError:
            foundversions = []
        #print "after index: " + dumpVersions(foundversions)
    
    foundversions = map(lambda v: v['name'], foundversions)
    
    return ", ".join(foundversions)
def render(issue_type, issue_description, jira_env, issues, jql, options,
           email_addresses, components):

    doc = Document()
    testsuite = doc.createElement("testsuite")
    doc.appendChild(testsuite)

    emails_to_send = {}

    if len(issues) > 0:
        for i, v in enumerate(issues):

            fields = v['fields']
            jira_key = v['key']

            # For available field names, see the variables in
            # src/java/com/atlassian/jira/rpc/soap/beans/RemoteIssue.java
            #logger.info('%s\t%s\t%s' % (v['key'], v['assignee'], v['summary']))
            # print fields['components']

            component_details = []
            component_lead_name = ""
            component_lead_email = ""
            component_lead_names = ""
            for component in fields['components']:
                # print component['id']
                # https://issues.jboss.org/rest/api/2/component/12311294
                if component['id'] in components:
                    component_data = components[component['id']]
                else:
                    # print 'Query ' + component['name'] + ' component lead'
                    component_data = shared.jiraquery(
                        options, "/rest/api/2/component/" + component['id'])
                    components[component['id']] = component_data

                component_name = str(component_data['name'])
                if not 'lead' in component_data.keys():
                    raise Exception(
                        '[ERROR] No component lead set for component = ' +
                        component_name + ' on issue ' + jira_key +
                        '.\n\n[ERROR] Contact an administrator to update https://issues.jboss.org/plugins/servlet/project-config/CRW/components'
                    )
                component_lead_name = str(component_data['lead']['name'])
                component_lead_names += "-" + xstr(component_lead_name)
                component_lead_email = fetch_email(component_lead_name,
                                                   options.unassignedjiraemail,
                                                   email_addresses)
                component_details.append({
                    'name': component_name,
                    'lead': component_lead_name,
                    'email': component_lead_email
                })
            fix_version = ""
            for version in fields['fixVersions']:
                fix_version += '_' + version['name']
            fix_version = fix_version[1:]
            if fix_version == "":
                if issue_type == "No fix version":
                    fix_version = ""
                else:
                    fix_version = ".nofixversion"
            else:
                fix_version = "." + xstr(fix_version)

            recipients = {}
            assignees = {}
            assignee_name = "Nobody"
            assignee_email = str(options.unassignedjiraemail)
            if fields['assignee']:
                assignee_name = str(fields['assignee']['name'])
                if 'emailAddress' in fields['assignee']:
                    assignee_email = str(fields['assignee']['emailAddress'])
                else:
                    assignee_email = fetch_email(assignee_name, None,
                                                 email_addresses)
                    if not assignee_email:
                        print 'No email found for assignee: ' + assignee_name
                assignees[assignee_name] = assignee_email
                recipients[assignee_name] = assignee_email
                if not assignee_name in email_addresses:
                    email_addresses[assignee_name] = assignee_email

            # TODO handle array of components
            elif component_details:
                for component_detail in component_details:
                    # print component_detail
                    recipients[
                        component_detail['lead']] = component_detail['email']
            else:
                # default assignee - send to mailing list if no component set
                recipients["Nobody"] = str(options.unassignedjiraemail)

            # print recipients

            testcase = doc.createElement("testcase")
            testcase.setAttribute("classname", jira_key)
            testcase.setAttribute(
                "name",
                issue_type.lower().replace(" ", "") + xstr(fix_version) + "." +
                assignee_name + xstr(component_lead_names))

            o = urlparse(v['self'])
            url = o.scheme + "://" + o.netloc + "/browse/" + jira_key

            error = doc.createElement("error")

            lastupdate = datetime.datetime.now() - datetime.datetime.strptime(
                fields['updated'][:-5],
                "%Y-%m-%dT%H:%M:%S.%f").replace(tzinfo=None)

            error.setAttribute(
                "message", "\n* [" + assignee_email + "] " + issue_type +
                " for " + jira_key)

            component_name = ""
            lead_info = ""
            assignee_info = ""
            if component_details:
                for component_detail in component_details:
                    component_name = component_name + (
                        ", "
                        if component_name else "") + component_detail['name']
                    lead_info = lead_info + (
                        ", " if lead_info else "") + component_detail[
                            'lead'] + " <" + component_detail['email'] + ">"

            assignee_info = email_array_to_string(assignees)
            # print assignee_info

            error_text = "\n" + url + "\n" + \
                "Summary: " + fields['summary'] + "\n\n" + \
                ("Assignee(s): " + assignee_info if assignee_info else "Assignee: None set.") + "\n" + \
                ("Lead(s): " + lead_info + "\n" if lead_info else "") + \
                ("Component(s): " + component_name if component_name else "Component: None set - please fix.") + "\n" + \
                "Problem: " + issue_type + " - " + issue_description + "\n" + \
                "Last Update: " + str(lastupdate) + "\n\n----------------------------\n\n"

            error_text_node = doc.createTextNode(error_text)
            error.appendChild(error_text_node)

            testcase.appendChild(error)
            testsuite.appendChild(testcase)

            subject = "\n* " + issue_type + " for " + jira_key

            # load email content into a dict(), indexed by email recipient & JIRA; for issues w/ more than one component, each component lead will get an email
            for name in recipients:
                if not recipients[name] in emails_to_send:
                    emails_to_send[recipients[name]] = {}
                emails_to_send[recipients[name]][jira_key] = {
                    'message': subject + '\n' + error_text,
                    'recipients': name + " <" + recipients[name] + ">"
                }
                #print emails_to_send[recipients[name]][jira_key]

    else:
        testcase = doc.createElement("testcase")
        testcase.setAttribute("classname", issue_type)
        testcase.setAttribute("name", "found.noissues")
        testsuite.appendChild(testcase)

    print('Write to ' + issue_type.lower().replace(" ", "") + "-test.xml")
    output = open(issue_type.lower().replace(" ", "") + "-test.xml", 'w')
    output.write(doc.toprettyxml(indent="  ").encode('utf8', 'replace'))

    # send emails & log to file
    log = ''
    if options.fromemail:
        if len(emails_to_send) > 0:
            for i, assignee_email in enumerate(emails_to_send):
                problem_count = str(len(emails_to_send[assignee_email]))
                # note: python uses `value if condition else otherValue`, which is NOT the same as `condition ? value : otherValue`
                entry = ("Prepare (but do not send)" if options.dryrun else "Send") + " email with " + problem_count + \
                    " issue(s) to: " + (options.toemail + " (not " + assignee_email + ")" if options.toemail else assignee_email)
                print entry
                log = log + entry + "\n\n"
                message = ''
                o = urlparse(v['self'])
                url = o.scheme + "://" + o.netloc + "/browse/"
                for j, jira_key in enumerate(emails_to_send[assignee_email]):
                    print " * " + url + jira_key
                    message = message + emails_to_send[assignee_email][
                        jira_key]['message']
                    log = log + emails_to_send[assignee_email][jira_key][
                        'message']
                    # print emails_to_send[assignee_email][jira_key]['recipients']

                # wrap generated message w/ header and footer
                message = "This email is the result of a query to locate stalled/invalid jiras. Please fix them. Thanks!" + \
                    "\n\nGlobal Query:   "  + options.jiraserver + "/issues/?jql=" + urllib.quote_plus(jql) +  \
                    "\n\nPersonal Query: "  + options.jiraserver + "/issues/?jql=" + urllib.quote_plus(jql + " AND assignee = currentUser()") + \
                    "\n\n----------------------------\n\n" + message
                # send to yourself w/ --toemail override, or else send to actual recipient
                # note: python uses `value if condition else otherValue`, which is NOT the same as `condition ? value : otherValue`
                mailsend(
                    options.smtphost, options.fromemail,
                    (options.toemail if options.toemail else assignee_email),
                    problem_count + ' issue' +
                    ('s' if len(emails_to_send[assignee_email]) > 1 else '') +
                    ' with ' + issue_type.lower(),
                    message.encode('utf8', 'replace'),
                    emails_to_send[assignee_email][jira_key]['recipients'],
                    options)

    if log:
        output = open(issue_type.lower().replace(" ", "") + ".log", 'w')
        output.write(log.encode('utf8', 'replace'))

    return email_addresses
        "Need to specify both --unassignedjiraemail and --smpthost to send mail"
    )

# store an array of username : email_address and componentid: component data we can use as a lookup table
email_addresses = {}
components = {}

if options.reportfile:
    print "Using reports defined in " + options.reportfile
    reports = json.load(open(options.reportfile, 'r'))

    for report in reports:
        for issue_type, fields in report.items():
            print("Check for '" + issue_type.lower() + "'")
            payload = {'jql': fields['jql'], 'maxResults': options.maxresults}
            data = shared.jiraquery(
                options, "/rest/api/2/search?" + urllib.urlencode(payload))
            print(
                str(len(data['issues'])) + " issues found with '" +
                issue_type.lower() + "'")
            if options.verbose:
                print data
                print options
            email_addresses = render(
                issue_type, fields['description'].encode('utf8', 'replace'),
                data, data['issues'], fields['jql'], options, email_addresses,
                components)
else:
    print "Generating based on .json found on standard in"
    data = json.load(sys.stdin)
    email_addresses = render('stdin', 'Query from standard in.', data,
                             data['issues'], None, options, email_addresses,
def render(issue_type, issue_description, jira_env, issues, jql, options, email_addresses, components):
        
    doc = Document()
    testsuite = doc.createElement("testsuite")
    doc.appendChild(testsuite)

    emails_to_send = {}

    if len(issues) > 0:
        for i, v in enumerate(issues):
            
            fields = v['fields']
            jira_key = v['key']

            # For available field names, see the variables in
            # src/java/com/atlassian/jira/rpc/soap/beans/RemoteIssue.java 
            #logger.info('%s\t%s\t%s' % (v['key'], v['assignee'], v['summary']))
            # print fields['components']

            component_details = []
            component_lead_name = ""
            component_lead_email = ""
            component_lead_names = ""
            for component in fields['components']:
                # print component['id']
                # https://issues.jboss.org/rest/api/2/component/12311294
                if component['id'] in components:
                    component_data = components[component['id']]
                else:
                    # print 'Query ' + component['name'] + ' component lead'
                    component_data = shared.jiraquery(options, "/rest/api/2/component/" + component['id'])
                    components[component['id']] = component_data
                    
                component_name = str(component_data['name'])
                component_lead_name = str(component_data['lead']['name'])
                component_lead_names += "-" + xstr(component_lead_name)
                component_lead_email = fetch_email(component_lead_name, options.unassignedjiraemail, email_addresses)
                component_details.append({'name': component_name, 'lead': component_lead_name, 'email': component_lead_email})
            fix_version = ""
            for version in fields['fixVersions']:
                fix_version += '_' + version['name']
            fix_version = fix_version[1:]
            if fix_version == "":
                if issue_type == "No fix version":
                    fix_version = ""
                else:
                    fix_version=".nofixversion"
            else:
                fix_version = "." + xstr(fix_version)

            recipients = {}
            assignees = {}
            assignee_name = "Nobody"
            assignee_email = str(options.unassignedjiraemail)
            if fields['assignee']:
                assignee_name = str(fields['assignee']['name'])
                if 'emailAddress' in fields['assignee']:
                    assignee_email = str(fields['assignee']['emailAddress'])
                else:
                    assignee_email = fetch_email(assignee_name, None, email_addresses)
                    if not assignee_email:
                        print 'No email found for assignee: ' + assignee_name
                assignees[assignee_name] = assignee_email
                recipients[assignee_name] = assignee_email
                if not assignee_name in email_addresses:
                    email_addresses[assignee_name] = assignee_email

            # TODO handle array of components
            elif component_details:
                for component_detail in component_details:
                    # print component_detail
                    recipients[component_detail['lead']] = component_detail['email']
            else:
                # default assignee - send to mailing list if no component set
                recipients["Nobody"] = str(options.unassignedjiraemail)

            # print recipients

            testcase = doc.createElement("testcase")
            testcase.setAttribute("classname", jira_key)
            testcase.setAttribute("name", issue_type.lower().replace(" ","") + xstr(fix_version) + "." + assignee_name + xstr(component_lead_names))

            o = urlparse(v['self'])
            url = o.scheme + "://" + o.netloc + "/browse/" + jira_key

            error = doc.createElement("error")

            lastupdate = datetime.datetime.now() - datetime.datetime.strptime(fields['updated'][:-5], "%Y-%m-%dT%H:%M:%S.%f" ).replace(tzinfo=None)

            error.setAttribute("message", "\n* [" + assignee_email + "] " + issue_type + " for " + jira_key)

            component_name = ""
            lead_info = ""
            assignee_info = ""
            if component_details:
                for component_detail in component_details:
                    component_name = component_name + (", " if component_name else "") + component_detail['name']
                    lead_info = lead_info + (", " if lead_info else "") + component_detail['lead'] + " <" + component_detail['email'] + ">"

            assignee_info = email_array_to_string(assignees)
            # print assignee_info

            error_text = "\n" + url + "\n" + \
                "Summary: " + fields['summary'] + "\n\n" + \
                ("Assignee(s): " + assignee_info if assignee_info else "Assignee: None set.") + "\n" + \
                ("Lead(s): " + lead_info + "\n" if lead_info else "") + \
                ("Component(s): " + component_name if component_name else "Component: None set - please fix.") + "\n" + \
                "Problem: " + issue_type + " - " + issue_description + "\n" + \
                "Last Update: " + str(lastupdate) + "\n\n----------------------------\n\n"

            error_text_node = doc.createTextNode(error_text)
            error.appendChild(error_text_node)

            testcase.appendChild(error)
            testsuite.appendChild(testcase)

            subject = "\n* " + issue_type + " for " + jira_key
  
            # load email content into a dict(), indexed by email recipient & JIRA; for issues w/ more than one component, each component lead will get an email
            for name in recipients:
                if not recipients[name] in emails_to_send:
                    emails_to_send[recipients[name]] = {}
                emails_to_send[recipients[name]][jira_key] = {'message': subject + '\n' + error_text, 'recipients': name + " <" + recipients[name] + ">"}
                #print emails_to_send[recipients[name]][jira_key]

    else:
        testcase = doc.createElement("testcase")
        testcase.setAttribute("classname", issue_type)
        testcase.setAttribute("name", "found.noissues")
        testsuite.appendChild(testcase)
 
    print('Write to ' + issue_type.lower().replace(" ","") + "-test.xml")
    output = open(issue_type.lower().replace(" ","") + "-test.xml", 'w')
    output.write(doc.toprettyxml(indent="  ").encode('utf8', 'replace'))

    # send emails & log to file
    log = ''
    if options.fromemail:
        if len(emails_to_send) > 0:
            for i, assignee_email in enumerate(emails_to_send):
                problem_count = str(len(emails_to_send[assignee_email]))
                # note: python uses `value if condition else otherValue`, which is NOT the same as `condition ? value : otherValue`
                entry = ("Prepare (but do not send)" if options.dryrun else "Send") + " email with " + problem_count + " issue(s) to: " + (options.toemail + " (not " + assignee_email + ")" if options.toemail else assignee_email)
                print entry
                log = log + entry + "\n\n"
                message = ''
                for j, jira_key in enumerate(emails_to_send[assignee_email]):
                    message = message + emails_to_send[assignee_email][jira_key]['message']
                    log = log + emails_to_send[assignee_email][jira_key]['message']
                    # print emails_to_send[assignee_email][jira_key]['recipients']

                # wrap generated message w/ header and footer
                message = "This email is the result of a query to locate stalled/invalid jiras. Please fix them. Thanks!\n\n " + message
                message = message + "\n\nQuery used: "  + options.jiraserver + "/issues/?jql=" + urllib.quote_plus(jql) + "\n"
                # send to yourself w/ --toemail override, or else send to actual recipient
                # note: python uses `value if condition else otherValue`, which is NOT the same as `condition ? value : otherValue`
                mailsend (options.smtphost, 
                    options.fromemail, 
                    (options.toemail if options.toemail else assignee_email), 
                    problem_count + ' issue' + ('s' if len(emails_to_send[assignee_email]) > 1 else '') + ' with ' + issue_type.lower(), 
                    message.encode('utf8','replace'),
                    emails_to_send[assignee_email][jira_key]['recipients'], 
                    options)
    
    if log:
        output = open(issue_type.lower().replace(" ","") + ".log", 'w')
        output.write(log.encode('utf8', 'replace'))

    return email_addresses
(options, args) = parser.parse_args()

if not options.username or not options.password:
    parser.error("Missing username or password")

if options.fromemail and (not options.unassignedjiraemail or not options.smtphost):
    parser.error("Need to specify both --unassignedjiraemail and --smpthost to send mail")


# store an array of username : email_address and componentid: component data we can use as a lookup table
email_addresses = {}
components = {}
    
if options.reportfile:
    print "Using reports defined in " + options.reportfile
    reports = json.load(open(options.reportfile, 'r'))

    
    for report in reports:
        for issue_type,fields in report.items():
            print("Check for '"  + issue_type.lower() + "'")
            payload = {'jql': fields['jql'], 'maxResults' : options.maxresults}
            data = shared.jiraquery(options, "/rest/api/2/search?" + urllib.urlencode(payload))
            print(str(len(data['issues'])) + " issues found with '" + issue_type.lower() + "'")
            email_addresses = render(issue_type, fields['description'].encode('utf8','replace'), data, data['issues'], fields['jql'], options, email_addresses, components)
else:
    print "Generating based on .json found on standard in"
    data = json.load(sys.stdin)
    email_addresses = render('stdin', 'Query from standard in.', data, data['issues'], None, options, email_addresses, components)

    for filterfile in filterfiles:
        print "Processing filters found in " + filterfile
        filters = json.load(open(filterfile, 'r'))

        newfilters = filters.copy()
        for name, fields in filters.items():
            try:
                print "filter " + name
                data = [{
                    'type': 'project',
                    'projectId': 10020
                }, {
                    'type': 'project',
                    'projectId': 12310500
                }]

                if 'id' in fields:
                    print 'Checking filter ' + name
                    filter = shared.jiraquery(
                        options, "/rest/api/latest/filter/" + fields['id'])
                    if len(filter['sharePermissions']) == 0:
                        print 'Updating filter ' + name
                        for permission in data:
                            shared.jirapost(
                                options, "/rest/api/latest/filter/" +
                                fields['id'] + "/permission", permission)
                else:
                    print 'Filter ' + name + ' already has some permissions'
            except urllib2.HTTPError, e:
                print "Problem with setting up filter %s" % (name)