List custom fields for a Jira Cloud Project with Python

Hi Atlassian Community!

I'm excited to share another handy Python script that simplifies some of your Jira Admin tasks. This time, I'm providing a script that crafts a comprehensive list of all custom fields used in a specified Jira project. This can be particularly useful for administrators managing extensive projects with numerous custom fields.

The solution

The script leverages the Jira REST API to identify and list all custom fields associated with a specified project. It provides details such as field IDs, names, and types, and outputs the data into a CSV file for easy reference and documentation.

Key features

  • Comprehensive listing of custom fields
  • Detailed custom field metadata (ID, name, type)
    (Jira custom field types)
  • CSV export for easy sharing and documentation
  • Projects might have different issue types and different custom fields for each issue type, the script will consider this and query each of the project's issue types to find the related custom fields

Preparation

Install the necessary python libraries by running:

pip install requests pandas

Prepare your Jira Cloud site URL (your-domain.atlassian.net), email address, and API token. You can generate an API token from Atlassian's account security page.

Have the project key ready for the project you wish to analyze

The script

import requests
from requests.auth import HTTPBasicAuth
from getpass import getpass
import pandas as pd
import logging
import os

# Configure logging to show INFO level messages
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def get_jira_auth():
    jira_domain = input("Enter your Jira 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_issue_types(jira_domain, auth, project_key):
    url = f"https://{jira_domain}/rest/api/3/project/{project_key}"
    response = requests.get(url, auth=auth)
    response.raise_for_status()
    data = response.json()
    return [issue_type['name'] for issue_type in data['issueTypes']]

def get_example_issue_key(jira_domain, auth, project_key, issue_type):
    jql = f"project={project_key} AND issuetype={issue_type}"
    url = f"https://{jira_domain}/rest/api/3/search"
    params = {"jql": jql, "maxResults": 1}
    response = requests.get(url, auth=auth, params=params)
    response.raise_for_status()
    issues = response.json().get('issues', [])
    return issues[0]['key'] if issues else None

def get_issue_fields(jira_domain, auth, issue_key):
    url = f"https://{jira_domain}/rest/api/3/issue/{issue_key}?expand=names"
    response = requests.get(url, auth=auth)
    response.raise_for_status()
    return response.json()['names']

def get_all_fields(jira_domain, auth):
    url = f"https://{jira_domain}/rest/api/3/field"
    response = requests.get(url, auth=auth)
    response.raise_for_status()
    return response.json()

def translate_field_type(field_type):
    translation_map = {
        "option": "Select List (single choice) or Checkbox",
        "user": "User Picker",
        "string": "Text Field",
        "array": "Labels",
        "datetime": "Date Time Picker",
        "number": "Number Field",
        "date": "Date Picker",
        "team": "Team Field",
        "group": "Group Picker",
        "option-with-child": "Select List (cascading)"
    }
    return translation_map.get(field_type, "N/A")

def main():
    jira_domain, auth = get_jira_auth()
    project_key = input("Enter the project key: ")

    try:
        issue_types = get_project_issue_types(jira_domain, auth, project_key)
        logging.info(f"Issue types found: {issue_types}")

        example_issues = {}
        for issue_type in issue_types:
            issue_key = get_example_issue_key(jira_domain, auth, project_key, issue_type)
            if issue_key:
                example_issues[issue_type] = issue_key
                logging.info(f"Example issue for {issue_type}: {issue_key}")
            else:
                logging.warning(f"No example issue found for {issue_type}")

        custom_field_names = {}
        for issue_type, issue_key in example_issues.items():
            fields = get_issue_fields(jira_domain, auth, issue_key)
            custom_field_names.update(fields)

        all_fields = get_all_fields(jira_domain, auth)
        custom_fields_info = [
            {
                'Cloud URL': f"https://{jira_domain}",
                'Project Key': project_key,
                'Field ID': field['id'],
                'Field Name': custom_field_names.get(field['id'], field['name']),
                'Field Type': field['schema']['type'] if 'schema' in field else 'N/A',
                'Translated Field Type': translate_field_type(field['schema']['type'] if 'schema' in field else 'N/A')
            }
            for field in all_fields if field['id'].startswith('customfield_') and field['id'] in custom_field_names
        ]

        df = pd.DataFrame(custom_fields_info)
        csv_file = f"{project_key}_custom_fields.csv"
        df.to_csv(csv_file, index=False)
        absolute_path = os.path.abspath(csv_file)
        logging.info(f"Custom fields report generated and saved to {absolute_path}")

    except Exception as e:
        logging.critical(f"An unexpected error occurred: {e}")

if __name__ == "__main__":
    main()

 

Sneak Peek

Zight 2025-02-20 at 1.15.55 PM.jpg.jpg

image (4).png

Disclaimer:

While this script is designed to facilitate certain interactions with JIRA Software Cloud as a convenience, it is essential to understand that its functionality is subject to change due to updates to JIRA Software Cloud’s API or other conditions that could affect its operation.

Please note that this script is provided on an "as is" and "as available" basis without any warranties of any kind. This script is not officially supported or endorsed by Atlassian, and its use is at your own discretion and risk.

Cheers!

2 comments

Matt Doar _Adaptavist_
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 20, 2025

Thank you for the sample code. I'm interested in why you chose to use requests rather than the commonly used Jira Python library at https://jira.readthedocs.io/ ? Any thoughts welcome

Delfino Rosales
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
February 21, 2025

Hi Matt!

I'm not that familiar/comfortable with the Jira Python library referenced but I'll definitely give it a try in future scripts.

Cheers!~

Comment

Log in or Sign up to comment
TAGS
AUG Leaders

Atlassian Community Events