Python/Automation/4 → Automate JIRA task & sub-task creation using Python

integratio
7 min readApr 21, 2024

--

This is a post on how to prepare JIRA tasks and subtasks using python fetching details of tasks from an excel for each users.

Automation using Python

(1) Problem: Manually creating JIRA tasks for users is a tedious task, if there are umpteen number of tasks performed by a used on daily basis. There might be requirement in Development team to log each day’s task in JIRA. To work on the problem, we are going to build a solution.

(2) Solution:

To automate the tedious JIRA task/subtask creating process we are going to build a python solution that will fetch user task details from an excel file (where each user is logging their efforts) and select only that user specific details from the excel. As part of the automation process , a parent task would be created against the Development team, under which each subtask will represent the specific task that user has performed. So if the user has done suppose n number of tasks, then n number of subtasks will get created in JIRA under the parent task. So first user will create the task and subtasks. And next onwards other users can just provide the parent task id and create their own subtasks.

Pre-Requisite:

Sign up for an account from : https://www.atlassian.com/software/jira with your personal mail id eg. gmail id and note it down.

After creating an account, go to Manage Account → Security → API Tokens and create a token. Save the token details.

From Project Templates, select the below.

Use the below template.

Create Project.

Create a dummy task.

Create a subtask.

Before writing the Python file we need to create a “jira_api_config.properties” file that parameterize the necessary fields in the python script.

Place the file in some path in your machine and note the path,as it will be required to configure in the python script to pull the values.

###################################################
# Logger Details as properties ;
###################################################
#### Daily Tracker Details ####
create_task_subtask.tracker_excel_file_dir=C:\\TestDir\\Python\\JIRA_API_Integration
create_task_subtask.tracker_excel_file_name=Task_Tracker_v0.1.xlsx
create_task_subtask.tracker_excel_col_name=Task,Member,%Done,Status,End_Date,Comment,Type
create_task_subtask.tracker_user_name=Pratik
###################################################
#### JIRA API Details ####
api.jira_user_name=<your_mail_id>@gmail.com
api.jira_password=<your_api_token>
create_task_subtask.jira_task_subtask_url=https://<user_name>.atlassian.net/rest/api/latest/issue/
create_task_subtask.jira_proj_key=OP
create_task_subtask.jira_proj_name=ORG_PROJ
create_task_subtask.fix_version=ORG_PROJ_Tasks
create_task_subtask.task_issue_type=Task
create_task_subtask.task_label=ORG_Team_Worklog
create_task_subtask.task_summary=ORG_Miscellaneous-${date_1}
create_task_subtask.task_description=ORG_Task_as_on-${date_2}
create_task_subtask.assignee=<user_id>
create_task_subtask.subtask_issue_type=Sub-task
###################################################

Also, user specific task entries are captured in an excel file, “Task_Tracker_v0.1.xlsx” as below.

Excel Columns → Task, Member, %Done, Status, End_Date, Comment, Type, User_1, User_2, User_3.

Suppose User_1 need to create the Jira tasks in an automated way. He will run the python script and provide his account details, user, access_token (alternate to passworded approach).

Tracker Excel with list of user tasks and time spent

Now we will try to create a python script that will create a task and sub-tasks under it. Please create a .py file: “JIRA_API_Integration_v1.0.py” file in your IDE of your choice.

import json
import requests
import datetime
from jproperties import Properties
import pandas as pd

def get_tracker_details(prop_dict, curr_dt_3):
excel_dir = prop_dict.get('create_task_subtask.tracker_excel_file_dir')
excel_name = prop_dict.get('create_task_subtask.tracker_excel_file_name')
sheet_name = str(curr_dt_3)
excel_file = excel_dir + "\\" + excel_name

task_user = prop_dict.get('create_task_subtask.tracker_user_name')
wb_col_name = prop_dict.get('create_task_subtask.tracker_excel_col_name')
col_name_list = wb_col_name.split(",")
col_name_list.append(task_user)

data = pd.read_excel(excel_file, sheet_name=sheet_name)
df = pd.DataFrame(data, columns=col_name_list)

row_count = 0
task_row_list = []

# iterate over each row
for index, rows in df.iterrows():
if str(rows.get(task_user)) != 'nan':
# assign current row to row list
curr_row_list = [rows.get(col_name_list[0]), rows.get(col_name_list[1]), rows.get(col_name_list[2]), rows.get(col_name_list[3]), rows.get(col_name_list[4]), rows.get(col_name_list[5]), rows.get(col_name_list[6]), rows.get(task_user)]
# append the row list to the final row lists
task_row_list.append(curr_row_list)

row_count = row_count + 1

return task_row_list

def create_task(prop_dict, curr_dt_1, curr_dt_2):
username = prop_dict.get('api.jira_user_name')
password = prop_dict.get('api.jira_password')
api_url = prop_dict.get('create_task_subtask.jira_task_subtask_url')
proj_key = prop_dict.get('create_task_subtask.jira_proj_key')
fix_version = prop_dict.get('create_task_subtask.fix_version')
task_issue_type = prop_dict.get('create_task_subtask.task_issue_type')
task_label = []
task_label.append(prop_dict.get('create_task_subtask.task_label'))
task_summary = prop_dict.get('create_task_subtask.task_summary').replace('${date_1}',curr_dt_1)
task_description = prop_dict.get('create_task_subtask.task_description').replace('${date_2}',curr_dt_2)
task_priority = "Low"
task_assignee = prop_dict.get('create_task_subtask.assignee')
out_file_dir = prop_dict.get('create_task_subtask.tracker_excel_file_dir')

headers = {"Content-Type": "application/json"}
data = {"fields":
{
"labels": task_label,
"project": {"key": proj_key},
"summary": task_summary,
"description": task_description,
"priority": {"name": task_priority},
"issuetype": {"name": task_issue_type},
"assignee": {"name": task_assignee},
}
}

response = requests.post(api_url,
headers=headers, data=json.dumps(data),
auth=(username, password))
task_out = response.json()
print(task_out)

parent_task_id = str(task_response.get('key'))

out_file = open(out_file_dir + "\\out_" + curr_dt_1 + ".txt", 'a')
out_file.writelines("JIRA PARENT ID Created :" + str(task_out) + "\n")

return task_out

def create_subtask(prop_dict, parent_issue_id, tracker_task_list, curr_dt_1):
user = prop_dict.get('api.jira_user_name')
password = prop_dict.get('api.jira_password')
subtask_api_url = prop_dict.get('create_task_subtask.jira_task_subtask_url')
proj_key = prop_dict.get('create_task_subtask.jira_proj_key')
fix_version = prop_dict.get('create_task_subtask.fix_version')
subtask_issue_type = prop_dict.get('create_task_subtask.subtask_issue_type')
subtask_assignee = prop_dict.get('create_task_subtask.assignee')
out_file_dir = prop_dict.get('create_task_subtask.tracker_excel_file_dir')

headers = {"Content-Type": "application/json"}

subtask_init_count = 0
subtask_max_count = len(tracker_task_list)

out_file = open(out_file_dir + "\\out_" + curr_dt_1 + ".txt", 'a')

while (subtask_init_count < subtask_max_count):
subtask_summary = tracker_task_list[subtask_init_count][0]
subtask_description = tracker_task_list[subtask_init_count][5]
subtask_label = tracker_task_list[subtask_init_count][6]
subtask_label_list = []
subtask_label_list.append(subtask_label)
subtask_priority = "Low"
subtask_effort = tracker_task_list[subtask_init_count][7]

subtask_init_count = subtask_init_count + 1

data = {"fields":
{
"project": {"key": proj_key},
"parent": {"key": parent_issue_id},
"summary": subtask_summary,
"description": subtask_description,
"labels": subtask_label_list,
"priority": {"name": subtask_priority},
"issuetype": {"name": subtask_issue_type},
"assignee": {"name": subtask_assignee},
}
}

response = requests.post(subtask_api_url,
headers=headers, data=json.dumps(data), auth=(user, password))
subtask_out = response.json()
print(subtask_out)

out_file.writelines(str(subtask_out) + "\n")

return subtask_out

if __name__ == '__main__':
print('--------------------Script Started--------------------')

today_date = datetime.date.today()
day_suffix = {'1': 'st', '2': 'nd', '3': 'rd'}.get(str(today_date.day)[-1:], 'th')

curr_dt_1 = today_date.strftime("%d-%m-%Y")
curr_dt_2 = today_date.strftime(f"%d{day_suffix} %B %Y")
curr_dt_3 = today_date.strftime(f"%d{day_suffix} %b")

# TO MODIFY as per path & file name for respective user
cfg_dir = "C:\\TestDir\\Python\\JIRA_API_Integration"
cfg_file_name = "jira_api_config.properties"
cfg_file = cfg_dir + "\\" + cfg_file_name

prop_dict = {}
configs = Properties()
task_response = {}

# loading the contents of the config file using jproperties
with open(cfg_file, 'rb') as cfg_prop:
configs.load(cfg_prop)

prop_items = configs.items()

in_oper = input('Please enter 1/2 for operations : (1)CreateTask/(2)CreateSubtask : ')
oper_prefix = ""
task_id = ""

if str(in_oper) == "1":
print("Preparing for JIRA Create Task.")
oper_prefix = "create_task_subtask."

for prop_key_val in prop_items:
if str(prop_key_val[0]).startswith(oper_prefix) or str(prop_key_val[0]).startswith('api.'):
prop_key = prop_key_val[0]
prop_val = prop_key_val[1].data
prop_dict[prop_key] = prop_val

out_file_dir = prop_dict.get('create_task_subtask.tracker_excel_file_dir')
out_file = open(out_file_dir + "\\out_" + curr_dt_1 + ".txt", 'w')
out_file.writelines(".................................................................\n")

task_response = create_task(prop_dict, curr_dt_1, curr_dt_2)
task_id = str(task_response.get('key'))
print('Task create in JIRA with id : ' + task_id)
tracker_task_list = []

in_option = input('Want to proceed with SubTasks? Type - y/n : ')

if str(in_option).upper() == "Y":
tracker_task_list = get_tracker_details(prop_dict, curr_dt_3)
'''print(tracker_task_list)'''
subtask_response = create_subtask(prop_dict, task_id, tracker_task_list, curr_dt_1)
else:
print('JIRA Task ID created.')


elif str(in_oper) == "2":
print('Preparing for JIRA Create Sub-Task.')
oper_prefix = 'create_task_subtask.'

for prop_key_val in prop_items:
if str(prop_key_val[0]).startswith(oper_prefix) or str(prop_key_val[0]).startswith('api.'):
prop_key = prop_key_val[0]
prop_val = prop_key_val[1].data
prop_dict[prop_key] = prop_val

out_file_dir = prop_dict.get('create_task_subtask.tracker_excel_file_dir')
out_file = open(out_file_dir + "\\out_" + curr_dt_1 + ".txt", 'w')
out_file.writelines(".................................................................\n")

tracker_task_list = get_tracker_details(prop_dict, curr_dt_3)

in_parent_issue_id = input('Enter Parent Issue ID : ')
subtask_response = create_subtask(prop_dict, in_parent_issue_id, tracker_task_list, curr_dt_1)

else:
print("Enter a valid operation.")

out_file_dir = prop_dict.get('create_task_subtask.tracker_excel_file_dir')
out_file = open(out_file_dir + "\\out_" + curr_dt_1 + ".txt", 'a')
out_file.writelines(".................................................................")

Now we can run the python script.

You can directly create Task and Subtasks in one run (option 1), or you can create task first in run 1 (option 1) and then run next time passing the Parent Task Id to create only the sub-tasks (option 2). Both will work.

So, in our run OP-24 is the parent task id created under which OP-25 and OP-26 these two sub-tasks are created.

Let's validate from the Atlassian JIRA window.

Tasks & Subtasks Successfully created
Subtasks under the Parent Task

Next Suppose another user wants to log his effort.

He needs to change the details in config file and provide his JIRA details along with the user name as present in excel file. Modify the property in config file.

create_task_subtask.tracker_user_name=Souvik

Run the python script. Select teh 2nd option.

New Subtask for the user created
Parent task will have another Subtask under it

Note: Due to some bug the Assignee is not getting set from Python, which need to set manually, using “Assign to me” option. Also, automation to set the time against the task is also an issue found, which required manual logging.

There is scope of improvement to the code snippet, but it will serve the basic purpose.

Share your thoughts/finding in the comment section.

--

--

integratio

📌 IT Backend enthusiast 📌 Blogging about Oracle FMW, Python, Cloud related technologies 📌 Non monetized, non hustle 📌 Knowledge sharing sole purpose