Runbook: Bulk Delete Worklogs

Goal

This page aims to share script to Bulk delete worklogs that are no longer necessary on your Jira Cloud instance. 

Documentation 

Get Issue Worklogs API

Bulk Delete Issue Worklogs API

Get Data 

Fetch the worklog IDs you plan to delete using Get Issue Worklogs API

  • use startedAfter and startedBefore parameters to narrow the timeframe (timestamps in milliseconds) 

Example script in Python 3

def get_worklog_ids(issue_key, after=None, before=None):
    url = f"{SITENAME}/rest/api/3/issue/{issue_key}/worklog?" \
          f"maxResults=5000&" \
          f"startedAfter={get_timestamp_in_milliseconds(after)}&" \
          f"startedBefore={get_timestamp_in_milliseconds(before)}"
    response = requests.get(url,
                            auth=(USER, PWD),
                            timeout=30)
    return [worklog['id'] for worklog in response.json()['worklogs']]

Get Data by author

To delete worklogs authored by a specific user, custom filtering of results must be done, as the Get Issue Worklogs API does not accept a user parameter.

Example script in Python 3 (by email address)

def get_worklog_ids_by_emails(issue_key, emails, after=None, before=None):
    url = f"{SITENAME}/rest/api/3/issue/{issue_key}/worklog?" \
          f"maxResults=5000&" \
          f"startedAfter={get_timestamp_in_milliseconds(after)}&" \
          f"startedBefore={get_timestamp_in_milliseconds(before)}"
    response = requests.get(url,
                            auth=(USER, PWD),
                            timeout=30)

    worklog_ids = []
    for worklog in response.json()['worklogs']:
        if 'emailAddress' in worklog['author'] and worklog['author']['emailAddress'] in emails:
            worklog_ids.append(worklog['id'])
    return worklog_ids


Delete Data

Delete the worklogs by calling Bulk Delete Issue Worklogs API with IDs received from the previous call

You can specify either auto or leave option for the adjustEstimate API parameter

  • leave Leaves the estimate unchanged.

  • auto Reduces the estimate by the value of timeSpent in the worklogs.

Example script in Python 3

def bulk_remove_worklogs(issue_key, worklog_ids, adjust_estimate="auto"):
    url = f"{SITENAME}/rest/api/3/issue/{issue_key}/worklog?adjustEstimate={adjust_estimate}"
    body = {"ids": worklog_ids}
    response = requests.delete(url, json=body, auth=(USER, PWD), timeout=30)
    return response.status_code, response.text

Full script

The script will delete all worklogs on issue ISSUE_KEY started after STARTED_AFTER and before STARTED_BEFORE

import json
from datetime import datetime
import requests

USER = 'admin@yahoo.com'
PWD = 'prod_token'
SITENAME = "the instance address"
ISSUE_KEY = 'TEST-1'

# Adjust if you'd prefer to use a different format
DATE_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S%z'

# Can be set to None
STARTED_AFTER = '2023-01-01T11:00:00+1100'
STARTED_BEFORE = '2024-01-01T11:00:00+1100'

def get_worklog_ids(issue_key, after=None, before=None):
    url = f"{SITENAME}/rest/api/3/issue/{issue_key}/worklog?" \
          f"maxResults=5000&" \
          f"startedAfter={get_timestamp_in_milliseconds(after)}&" \
          f"startedBefore={get_timestamp_in_milliseconds(before)}"
    response = requests.get(url,
                            auth=(USER, PWD),
                            timeout=30)
    return [worklog['id'] for worklog in response.json()['worklogs']]

def get_worklog_ids_by_emails(issue_key, emails, after=None, before=None):
    url = f"{SITENAME}/rest/api/3/issue/{issue_key}/worklog?" \
          f"maxResults=5000&" \
          f"startedAfter={get_timestamp_in_milliseconds(after)}&" \
          f"startedBefore={get_timestamp_in_milliseconds(before)}"
    response = requests.get(url,
                            auth=(USER, PWD),
                            timeout=30)
    worklog_ids = []
    for worklog in response.json()['worklogs']:
        if 'emailAddress' in worklog['author'] and worklog['author']['emailAddress'] in emails:
            worklog_ids.append(worklog['id'])
    return worklog_ids

def get_timestamp_in_milliseconds(date):
    if not date:
        return ''
    return 1000 * int(datetime.strptime(date, DATE_TIME_FORMAT).timestamp())

def bulk_remove_worklogs(issue_key, worklog_ids, adjust_estimate="auto"):
    url = f"{SITENAME}/rest/api/3/issue/{issue_key}/worklog?adjustEstimate={adjust_estimate}"
    body = {"ids": worklog_ids}
    response = requests.delete(url, json=body, auth=(USER, PWD), timeout=30)
    return response.status_code, response.text

while True:
    worklog_ids = get_worklog_ids(ISSUE_KEY, STARTED_AFTER,  STARTED_BEFORE)
    if len(worklog_ids) == 0:
        print("No more worklogs to delete")
        break
    status, body = bulk_remove_worklogs(ISSUE_KEY, worklog_ids)
    if status == 200:
        print(f"Successfully deleted *some* worklogs, response \n\"{body}\"")
    elif status == 204:
        print(f"Successfully deleted {len(worklog_ids)} worklogs on issue {ISSUE_ID}")
    else:
        print(f"Received different status than HTTP 200/204 from the Bulk Delete endpoint, status {status}, response body \n\"{body}\"")
        break

FAQ 

Q. How many worklogs, at max, can be deleted at a time?

A. The limit, as described in the API docs, is 5000 worklogs.


Q. What are the various criteria that can be used to identify the worklogs to delete?

A. The Bulk Delete API can only accept worklog IDs. Those IDs must be fetched using one of the available APIs, e.g. the Get Issue Worklogs API


Q. What is the maximum time range to get worklog IDs?

A. The Get Issue Worklogs API does not limit the time range, as long as the timestamps are valid.


Q. Are worklogs deleted from a single issue at a time?

A. The worklog must be from a single issue, specified in the {issueIDOrKey} API parameter.


Q. What if I delete worklogs by mistake? Can i restore them back?

A. The worklogs cannot be restored so the delete operations must be performed cautiously.


Q. Does the Bulk Delete API update issue change history?

A. Yes, issue change history will be updated.

Last modified on Aug 22, 2024

Was this helpful?

Yes
No
Provide feedback about this article
Powered by Confluence and Scroll Viewport.