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 requests
import 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