I'm back with another helpful Python script designed to retrieve and analyze Jira project statuses. This script offers valuable insights into your project workflows by extracting all available statuses for specified projects, providing a clear overview of their configurations.
This script leverages Jira Cloud REST API endpoints (this and this) to fetch project details and then retrieve all associated statuses. The collected data is logged directly to the console and saved to a detailed log file (.log) and a structured CSV file (.csv) for easy analysis and filtering.
Here's what the script focuses on:
requests library: pip install requestsimport requests
from requests.auth import HTTPBasicAuth
from getpass import getpass
import logging
import os
import csv
import json
from datetime import datetime
log_filename = f"jira_statuses_retrieval_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
file_handler = logging.FileHandler(log_filename)
file_handler.setLevel(logging.INFO)
file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)
logging.getLogger().addHandler(file_handler)
def get_jira_auth():
"""Prompts the user for Jira Cloud domain, email, and API token."""
jira_domain = input("Enter your Jira Cloud domain (e.g., your-domain.atlassian.net): ")
email = input("Enter your Jira email: ")
api_token = getpass("Enter your Jira API token: ")
return jira_domain, HTTPBasicAuth(email, api_token)
def get_project_details(jira_domain, auth, project_key):
"""
Retrieves the project ID and name (key) for a given project key.
"""
url = f"https://{jira_domain}/rest/api/3/project/{project_key}"
headers = {"Accept": "application/json"}
logging.info(f"Attempting to retrieve project details for key: {project_key}")
try:
response = requests.get(url, headers=headers, auth=auth)
response.raise_for_status()
project_data = response.json()
project_id = project_data.get('id')
project_name = project_data.get('key')
if project_id and project_name:
logging.info(f"Successfully retrieved project details for '{project_key}': ID={project_id}, Name={project_name}")
return project_id, project_name
else:
logging.error(f"Could not find project ID or name for project key '{project_key}'. Response: {project_data}")
return None, None
except requests.exceptions.RequestException as e:
logging.error(f"Error fetching project details for '{project_key}': {e}")
if response is not None:
if response.status_code == 401:
logging.error("Authentication failed. Please verify your email and API token.")
elif response.status_code == 404:
logging.error(f"Project with key '{project_key}' not found.")
return None, None
def get_project_statuses(jira_domain, auth, project_id):
"""
Retrieves all statuses associated with a given project ID.
Handles pagination if necessary, though Jira's /statuses/search often returns all in one go.
"""
base_url = f"https://{jira_domain}/rest/api/3/statuses/search"
headers = {"Accept": "application/json"}
all_statuses = []
start_at = 0
max_results = 200 # Default max results, can be adjusted
logging.info(f"Searching for statuses for project ID: {project_id}")
while True:
params = {
"projectId": project_id,
"startAt": start_at,
"maxResults": max_results
}
try:
response = requests.get(base_url, headers=headers, auth=auth, params=params)
response.raise_for_status()
status_search_results = response.json()
statuses = status_search_results.get('values', [])
all_statuses.extend(statuses)
total = status_search_results.get('total', 0)
is_last = status_search_results.get('isLast', True)
logging.info(f"Retrieved {len(all_statuses)} of {total} statuses so far for project ID {project_id}.")
if is_last or (start_at + max_results) >= total:
break
else:
start_at += max_results
except requests.exceptions.RequestException as e:
logging.error(f"Error fetching statuses for project ID {project_id}: {e}")
if response is not None and response.status_code == 401:
logging.error("Authentication failed. Please verify your email and API token.")
elif response is not None and response.status_code == 404:
logging.error(f"Statuses for project ID '{project_id}' not found.")
return None
logging.info(f"Successfully retrieved {len(all_statuses)} statuses for project ID {project_id}.")
return all_statuses
def save_to_csv(data, filename="jira_project_statuses.csv"):
"""Saves the processed status data to a CSV file."""
if not data:
logging.warning("No status data to save to CSV.")
return
fieldnames = ["Status Name", "Status ID", "Status Category", "Project Key", "Project ID"]
try:
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(data)
logging.info(f"Crafting CSV file with the retrieved data.")
logging.info(f"Operation completed, CSV file saved in {os.path.abspath(filename)}")
except Exception as e:
logging.error(f"Error saving data to CSV file: {e}")
def main():
jira_domain, auth = get_jira_auth()
project_keys_input = input("Enter the Jira project key(s) separated by comma (e.g., PROJ1,PROJ2): ").strip()
project_keys = [key.strip().upper() for key in project_keys_input.split(',') if key.strip()]
if not project_keys:
logging.error("No project keys provided. Exiting.")
return
all_statuses_for_csv = []
processed_project_ids = set()
for project_key in project_keys:
logging.info(f"\n--- Processing project: {project_key} ---")
project_id, project_name = get_project_details(jira_domain, auth, project_key)
if project_id and project_name:
if project_id in processed_project_ids:
logging.info(f"Project ID {project_id} for key '{project_key}' already processed. Skipping redundant status retrieval.")
continue
project_statuses = get_project_statuses(jira_domain, auth, project_id)
if project_statuses:
for status in project_statuses:
all_statuses_for_csv.append({
"Status Name": status.get('name', ''),
"Status ID": status.get('id', ''),
"Status Category": status.get('statusCategory', ''),
"Project Key": project_name,
"Project ID": project_id
})
processed_project_ids.add(project_id)
else:
logging.warning(f"No statuses found or an error occurred for project '{project_name}' (ID: {project_id}).")
else:
logging.error(f"Skipping project key '{project_key}' due to inability to retrieve project ID/name.")
if all_statuses_for_csv:
all_statuses_for_csv.sort(key=lambda x: (x['Project Key'], x['Status Name']))
save_to_csv(all_statuses_for_csv)
else:
logging.info("No status data retrieved across all selected projects. CSV file will not be created.")
logging.info(f"\nAnalysis complete. Detailed log saved to {os.path.abspath(log_filename)}")
if __name__ == "__main__":
main()
This script is provided "as is" and "as available" without warranties and is not officially supported or endorsed by Atlassian. Use at your own risk. Jira API changes may impact functionality.
Cheers!
Delfino Rosales
Senior Cloud Support Engineer
Amsterdam, NL
0 comments