Last active
April 19, 2024 21:53
-
-
Save klokie/2cca58329a64aa2371e673aa196aff32 to your computer and use it in GitHub Desktop.
Transform a Jira "Back up for cloud" export into a directory of Markdown files including metadata
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
**Title:** JIRA XML to Markdown Converter | |
**Description:** | |
This script processes an XML file exported from JIRA to extract issue details and | |
generates markdown files for each issue. It specifically handles attributes and | |
linked nodes to gather comprehensive issue metadata, including project details, | |
priorities, statuses, and more. The script: | |
- Maps `CustomFieldOption` values by their IDs to resolve human-readable values | |
for issue statuses and types. | |
- Retrieves metadata from `IssueView` nodes linked by issue IDs, such as | |
assignee, reporter, creation, and update dates. | |
- Handles variations in the storage of issue descriptions (either as attributes | |
or child nodes). | |
- Outputs markdown files organized by project keys, with filenames that include | |
project keys and slugified titles for easy identification. | |
**Usage:** | |
Update the `file_path` variable with the path to your XML file and run the script. | |
Ensure that all dependencies are installed and the XML structure corresponds to | |
the expected format used in the script. | |
""" | |
import xml.etree.ElementTree as ET | |
import re | |
# Helper function to convert strings to kebab-case | |
def slugify(text): | |
return re.sub(r"\W+", "-", text).strip("-").lower() | |
# Load the XML data | |
FILE_PATH = "path_to_your_file.xml" # Update this path to your actual XML file location | |
tree = ET.parse(FILE_PATH) | |
root = tree.getroot() | |
# Mapping CustomFieldOption values by their IDs | |
custom_field_options = {} | |
for option in root.findall(".//CustomFieldOption"): | |
option_id = option.get("id") | |
option_value = option.get("value") | |
if option_id and option_value: | |
custom_field_options[option_id] = option_value | |
# Mapping IssueView details by their issue IDs | |
issue_view_map = {} | |
for issue_view in root.findall(".//IssueView"): | |
issue_id = issue_view.get("issue") | |
if issue_id: | |
issue_view_map[issue_id] = { | |
"assignee": issue_view.find("assignee").text | |
if issue_view.find("assignee") is not None | |
else "Assignee not specified", | |
"created": issue_view.find("created").text | |
if issue_view.find("created") is not None | |
else "Creation date not specified", | |
"reporter": issue_view.find("reporter").text | |
if issue_view.find("reporter") is not None | |
else "Reporter not specified", | |
"updated": issue_view.find("updated").text | |
if issue_view.find("updated") is not None | |
else "Update date not specified", | |
} | |
# Create markdown files with extracted and mapped details | |
markdown_files = [] | |
for issue in root.findall(".//Issue"): | |
issue_id = issue.get("id") | |
project_key = issue.get("projectKey", "Unknown Project") | |
title = issue.get("summary", "Untitled Issue") | |
priority = issue.get("priority", "No priority assigned") | |
status_id = issue.get("status") | |
type_id = issue.get("type") | |
status = custom_field_options.get(status_id, "Status not found") | |
type_issue = custom_field_options.get(type_id, "Type not found") | |
description = ( | |
issue.get("description") | |
if "description" in issue.attrib | |
else ( | |
issue.find("description").text | |
if issue.find("description") is not None | |
else "Description not provided" | |
) | |
) | |
issue_view_details = issue_view_map.get(issue_id, {}) | |
assignee = issue_view_details.get("assignee", "Assignee not specified") | |
reporter = issue_view_details.get("reporter", "Reporter not specified") | |
created = issue_view_details.get("created", "Creation date not specified") | |
updated = issue_view_details.get("updated", "Update date not specified") | |
markdown_content = ( | |
f"## {title}\n\n" | |
f"**Project:** {project_key}\n" | |
f"**Priority:** {priority}\n" | |
f"**Status:** {status}\n" | |
f"**Type:** {type_issue}\n" | |
f"**Assignee:** {assignee}\n" | |
f"**Reporter:** {reporter}\n" | |
f"**Created:** {created}\n" | |
f"**Updated:** {updated}\n\n" | |
f"**Description:**\n{description}\n" | |
) | |
markdown_files.append( | |
{ | |
"filename": f"{project_key}/{project_key}-{issue_id}-{slugify(title)}.md", | |
"content": markdown_content, | |
} | |
) | |
# Optionally, save these markdown files to disk | |
for file in markdown_files: | |
directory = os.path.dirname(file["filename"]) | |
if not os.path.exists(directory): | |
os.makedirs(directory) | |
with open(file["filename"], "w", encoding="utf-8") as f: | |
f.write(file["content"]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment