Python & The Jira Rest API

Recently, while having to work a lot more in Jira than normal I got annoyed with the Jira Web GUI. So I wrote a script to do simple management of our jira issues.

Here is the basic usage

(env) ➜  jira git:(master) ✗ ./jira-ctl.py
usage: jira-ctl.py [-h] [-lp] [-li LIST_PROJECT_ISSUES] [-ua UPDATE_ASSIGNEE]
                   [-ud UPDATE_DATE] [-usts UPDATE_STATUS]
                   [-usum UPDATE_SUMMARY]

optional arguments:
  -h, --help            show this help message and exit
  -lp, --list-projects  List all projects
  -li LIST_PROJECT_ISSUES, --list-issues LIST_PROJECT_ISSUES
                        List Issues for a specific project
  -ua UPDATE_ASSIGNEE, --update-assignee UPDATE_ASSIGNEE
                        Update an issues assignee format: <issue
                        number>,<first_last>
  -ud UPDATE_DATE, --update-date UPDATE_DATE
                        Update an issues due date format: <issue number
                        >,<yyyy-mm-dd>
  -usts UPDATE_STATUS, --update-status UPDATE_STATUS
                        Update an issues status format: <issue
                        number>,'<Open|In Progress|Resolved>'
  -usum UPDATE_SUMMARY, --update-summary UPDATE_SUMMARY
                        Update an issues summary format: <issue
                        number>,'<summary>'
(env) ➜  jira git:(master) ✗

Here is the code…

from jira.client import JIRA
import os, sys
import prettytable
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-lp', '--list-projects', action="store_true", dest="list_projects", required=False, help="List all projects")
parser.add_argument('-li', '--list-issues', action="store", dest="list_project_issues", required=False, help="List Issues for a specific project")
parser.add_argument('-ua', '--update-assignee', action="store", dest="update_assignee", required=False, help="Update an issues assignee format: <issue number>,<first_last>")
parser.add_argument('-ud', '--update-date', action="store", dest="update_date", required=False, help="Update an issues due date format: <issue number>,<yyyy-mm-dd>")
parser.add_argument('-usts', '--update-status', action="store", dest="update_status", required=False, help="Update an issues status format: <issue number>,'<Open|In Progress|Resolved>'")
parser.add_argument('-usum', '--update-summary', action="store", dest="update_summary", required=False, help="Update an issues summary format: <issue number>,'<summary>'")
args = parser.parse_args()

def get_pass():
    if os.environ.get('JIRA_PASS') == None:
        print "you must first export your JIRA_PASS in the shell by running: source getpass.sh"
        print 'or "export JIRA_PASS=<jira_password>'
        sys.exit()
    jira_password = str(os.environ['JIRA_PASS'])

    return jira_password

def connect_jira(jira_server, jira_user, jira_password):
    '''
    Connect to JIRA. Return None on error
    '''
    try:
        #print "Connecting to JIRA: %s" % jira_server
        jira_options = {'server': jira_server}
        jira = JIRA(options=jira_options,
                    # Note the tuple
                    basic_auth=(jira_user,
                                jira_password))
        return jira
    except Exception,e:
        print "Failed to connect to JIRA: %s" % e
        return None

def list_projects():
    projects = jira.projects()
    header = ["Projects"]
    table = prettytable.PrettyTable(header)
    for project in projects:
        row = [str(project)]
        table.add_row(row)
    print table

def list_project_issues(project):
    query = 'project = %s and status != Resolved order by due asc' % (project)
    #query = 'project = %s and status != Resolved order by due desc' % (project)
    #query = 'project = %s order by due desc' % (project)
    issues = jira.search_issues(query, maxResults=100)

    if issues:
        header = ["Ticket", "Summary", "Assignee", "Status", "Due Date"]
        table = prettytable.PrettyTable(header)

        for issue in issues:
                summary = issue.fields.summary
                st = str(issue.fields.status)
                dd = issue.fields.duedate
                assignee = issue.fields.assignee
                row = [issue, summary, assignee, st, dd]
                table.add_row(row)
        print table

def update_issue_assignee(issue_number, assignee):
    issue = jira.issue(issue_number)
    issue.update(assignee=assignee)
    print "updated %s with new Assignee: %s" % (issue_number, assignee)

def update_issue_duedate(issue_number, dd):
    issue = jira.issue(issue_number)
    issue.update(duedate=dd)
    print "updated %s with new Due Date: %s" % (issue_number, dd)

def update_issue_summary(issue_number, summary):
    issue = jira.issue(issue_number)
    issue.update(summary=summary)
    print "updated %s with new Summary: %s" % (issue_number, summary)

def update_issue_status(issue_number, status):
    # To figure out transitions view the following URL
    # https://jira.yourcompany.com/rest/api/2/issue/XXXXXX-22/transitions?expand=transitions.fields

    transitions = {
                    'Resolved': 11,
                    'Open': 41,
                    'In Progress': 21,
                    'Testing': 71,
                    'Transition': 81,
                    'Closed': 91,
    }

    issue = jira.issue(issue_number)
    jira.transition_issue(issue, transitions[status])
    print "updated %s with new Status: %s" % (issue_number, status)

if __name__ == "__main__":
    if not args:
        parser.print_help()
    else:
        jira_user = '<your_login>'
        jira_password = get_pass()
        jira_server = 'https://jira.yourcompany.com'

        jira = connect_jira(jira_server, jira_user, jira_password)

        if args.list_projects:
            list_projects()
        elif args.list_project_issues:
            project = args.list_project_issues
            list_project_issues(project)
        elif args.update_assignee:
            (issue_number, assignee) = args.update_assignee.split(',')
            update_issue_assignee(issue_number, assignee)
        elif args.update_date:
            (issue_number, dd) = args.update_date.split(',')
            update_issue_duedate(issue_number, dd)
        elif args.update_status:
            (issue_number, status) = args.update_status.split(',')
            update_issue_status(issue_number, status)
        elif args.update_summary:
            (issue_number, summary) = args.update_summary.split(',')
            update_issue_summary(issue_number, summary)
        else:
            parser.print_help()

Later, I found out that there is a Jira command line utility, I didn’t check and see what functionality it provided, but I enjoyed writing this anyway.

Happy coding !