Forums

Articles
Create
cancel
Showing results for 
Search instead for 
Did you mean: 

problem with a python script to export all my issue in an excel file (free;-)

Martine Bombardelli
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
September 12, 2025

I have cretae a python script that working but for only 50 issues.

Can someone help me to modify it because I have 5000 issues to download.

I have try with chatgpt, but withut success :-)

Thanks for your help !

import requests
import base64
from openpyxl import Workbook
from dateutil import parser
import sys
import time

# Redirige stdout/stderr vers un log
sys.stdout = open(r"C:\Users\xxx\Documents\jira_export.log", "w", encoding="utf-8")
sys.stderr = sys.stdout

# -------------------- CONFIG --------------------
JIRA_URL = "https://xxx.

atlassian.net"
EMAIL = "xxx"
API_TOKEN = "xxx
FILTER_ID = "10249"
OUTPUT_PATH = r"C:\Users\xxx\Documents\jira_export_all.xlsx"
MAX_RESULTS = 50 # Nombre d'issues par requête pour rester stable

# -------------------- CHAMPS A GARDER --------------------
FIELDS_TO_KEEP = [
"key","summary","status","assignee","created","updated","priority","reporter","id",
"customfield_10030","issuetype","duedate","resolutiondate","customfield_10029",
"customfield_10056","customfield_10015","customfield_10088",
"customfield_10010","customfield_10097","timetracking","timespent",
"customfield_10093","creator","customfield_10091"
]

# -------------------- AUTH --------------------
def get_auth_header(email, token):
token_bytes = f"{email}:{token}".encode("utf-8")
base64_token = base64.b64encode(token_bytes).decode("utf-8")
return {
"Authorization": f"Basic {base64_token}",
"Accept": "application/json"
}

# -------------------- FETCH ALL ISSUE IDS --------------------
def fetch_all_issue_ids(jql):
start_at = 0
all_ids = []
print("Début de la récupération des IDs d'issues...")

while True:
payload = {"jql": jql, "startAt": start_at, "maxResults": MAX_RESULTS, "fields": ["id"]}
try:
response = requests.post(f"{JIRA_URL}/rest/api/3/search/jql",
headers=get_auth_header(EMAIL, API_TOKEN),
json=payload)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"Erreur HTTP à startAt={start_at}: {e}")
break

data = response.json()
issues = data.get("issues", [])
if not issues:
break

all_ids.extend([issue["id"] for issue in issues])
start_at += MAX_RESULTS
print(f"IDs récupérés : {len(all_ids)}")
time.sleep(0.2) # petit delay pour éviter les limites d'API

print(f"Total IDs récupérés : {len(all_ids)}")
return all_ids

# -------------------- FETCH DETAILED ISSUE --------------------
def fetch_issue_details(issue_id):
url = f"{JIRA_URL}/rest/api/3/issue/{issue_id}?fields=*all"
response = requests.get(url, headers=get_auth_header(EMAIL, API_TOKEN))
response.raise_for_status()
return response.json()

# -------------------- EXPORT EXCEL --------------------
def export_to_excel(issues_details, output_path):
wb = Workbook()
ws = wb.active
ws.title = "Jira Issues"

# En-têtes
ws.append(FIELDS_TO_KEEP)

# Fonction pour formater les valeurs
def format_value(value):
if isinstance(value, dict):
if not value:
return ""
return value.get("displayName") or value.get("name") or value.get("value") or str(value)
elif isinstance(value, list):
if not value:
return ""
return ", ".join([format_value(v) for v in value])
elif isinstance(value, str):
try:
return parser.parse(value).strftime("%Y-%m-%d")
except:
return value
else:
return str(value) if value is not None else ""

# Ajouter les lignes
for issue in issues_details:
fields = issue.get("fields", {})
row = []
for f in FIELDS_TO_KEEP:
if f == "key":
row.append(issue.get("key", ""))
elif f == "id":
row.append(issue.get("id", ""))
elif f == "timespent":
ts = fields.get("timespent") or (fields.get("timetracking", {}).get("timeSpentSeconds"))
row.append(ts if ts is not None else "")
elif f == "timetracking":
row.append(format_value(fields.get("timetracking", {})))
else:
row.append(format_value(fields.get(f, "")))
ws.append(row)

wb.save(output_path)
print(f"{len(issues_details)} issues exportées vers {output_path}")

# -------------------- MAIN --------------------
def main():
print("Début du script")
jql = f"filter={FILTER_ID}"
issue_ids = fetch_all_issue_ids(jql)

if not issue_ids:
print("Aucune issue récupérée, arrêt du script.")
return

issues_details = []
for idx, issue_id in enumerate(issue_ids, start=1):
print(f"Récupération de l'issue {idx}/{len(issue_ids)} : {issue_id}")
try:
details = fetch_issue_details(issue_id)
issues_details.append(details)
except requests.exceptions.RequestException as e:
print(f"Erreur lors de la récupération de l'issue {issue_id}: {e}")
time.sleep(0.1) # petit delay pour limiter la charge

export_to_excel(issues_details, OUTPUT_PATH)

if __name__ == "__main__":
main()

 

1 answer

0 votes
Matteo Vecchiato
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.
September 12, 2025

Hi @Martine Bombardelli ,

Welcome to Atlassian community. It's a common issue with the new v3 search api.

Please have a look to this thread for a solution: https://community.atlassian.com/forums/Jira-questions/Feature-change-during-CHANGE-2046/qaq-p/3103678 

Suggest an answer

Log in or Sign up to answer
DEPLOYMENT TYPE
CLOUD
PRODUCT PLAN
PREMIUM
TAGS
AUG Leaders

Atlassian Community Events