Skip to content

Instantly share code, notes, and snippets.

@lancearlaus
Last active June 10, 2017 14:37
Show Gist options
  • Save lancearlaus/9286e7035a71f29639f239acbd52ce96 to your computer and use it in GitHub Desktop.
Save lancearlaus/9286e7035a71f29639f239acbd52ce96 to your computer and use it in GitHub Desktop.
XMind to JIRA CSV Import Mapping Utility
{
"config.version" : "2.0",
"config.project.from.csv" : "false",
"config.encoding" : "UTF-8",
"config.file.id" : "0255121b-e22c-4833-8f8f-6cdfade1c986",
"config.email.suffix" : "@",
"config.file.name" : "XMind.csv",
"config.field.mappings" : {
"Issue Type" : {
"jira.field" : "issuetype",
"userChanged" : "true",
"manualMapping" : "false"
},
"Description" : {
"jira.field" : "description",
"userChanged" : "true",
"manualMapping" : "false"
},
"Related Project" : {
"userChanged" : "true",
"manualMapping" : "false",
"existing.custom.field" : "12109"
},
"Summary" : {
"jira.field" : "summary",
"userChanged" : "true",
"manualMapping" : "false"
},
"Component" : {
"jira.field" : "components",
"userChanged" : "true",
"manualMapping" : "false"
},
"Development Team" : {
"userChanged" : "true",
"manualMapping" : "false",
"existing.custom.field" : "10400"
}
},
"config.value.mappings" : { },
"config.delimiter" : ",",
"config.project" : {
"project.type" : null,
"project.key" : "PLAN",
"project.description" : null,
"project.url" : null,
"project.name" : "Project Planning",
},
"config.date.format" : "dd/MMM/yy h:mm a"
}
#!/bin/zsh
# Pre-requisites: XMLStarlet
# brew install xmlstarlet
# See usage for required document structure
EXPORTMARKER=arrow-left
EXPORTEDMARKER=arrow-up
usage() {
cat << EOF
Usage: ${0##*/} [FILE]
Extract marked XMind topics to CSV for JIRA import
FILE XMind file from which to extract JIRA issues
To prepare topics for export:
1. Create a detached topic titled "JIRA" on each sheet with topics to be exported
2. Create sub-topics under this detached topic for the common export CSV columns
Columns for Issue Type, Component, etc.
3. Mark each topic for export with the $EXPORTMARKER marker
An output directory for the resulting CSV files will created, if not already present
A file for each sheet containing marked topic(s) will be created in the output directory
Each marked topic will be exported as a row in the corresponding CSV file
Title of the topic will be used as the issue Summary
Sub-topics will form headings within the issue Description
The original XMind file will be updated with the marker for each exported topic updated to $EXPORTEDMARKER
EOF
}
if [ ! -f "$1" ]; then
usage
exit 1
fi
XMINDFILE="$1"
EXPORTDIR="${XMINDFILE%.*} JIRA CSV"
XMLSEL="xml sel -N x=urn:xmind:xmap:xmlns:content:2.0"
XMLED="xml ed -P -L -N x=urn:xmind:xmap:xmlns:content:2.0"
TMPDIR=$(mktemp -d)
TMPCONTENT=$TMPDIR/content.xml
unzip "$XMINDFILE" content.xml -d $TMPDIR
# Search for topics that have been marked for export
ISSUECOUNT=`$=XMLSEL -t -v "count(//x:topic//x:marker-ref[@marker-id=\"$EXPORTMARKER\"])" $TMPCONTENT`
if [[ $ISSUECOUNT -eq 0 ]]; then
>&2 echo No JIRA issue topics found, please mark issues to be exported with $EXPORTMARKER marker
exit 1
else
ISSUESHEETS=`$=XMLSEL -T -t -m "/*/x:sheet/x:title[..//x:topic//x:marker-ref[@marker-id=\"$EXPORTMARKER\"]]" -v 'text()' -n -b $TMPCONTENT`
SHEETCOUNT=$(wc -l <<< "$ISSUESHEETS")
>&2 echo Found $ISSUECOUNT JIRA issue topics across $SHEETCOUNT sheets marked with $EXPORTMARKER marker
fi
# Start of export process
EXPORTTIMESTAMP=`date +%Y-%m-%d-%H%M%S`
[[ -d "$EXPORTDIR" ]] || mkdir -p "$EXPORTDIR"
# Export marked topics to sheet-specific JIRA CSV files
while read -r sheet; do
EXPORTFILE="$EXPORTDIR/$sheet $EXPORTTIMESTAMP.csv"
>&2 echo Exporting marked topics in sheet \"$sheet\" to \"$EXPORTFILE\"...
# Search for a detached topic named "JIRA" with field defaults
# The following extracts properties in name=value format
JIRATOPIC=`$=XMLSEL -T -t -m "/*/x:sheet[x:title[text()=\"$sheet\"]]//x:topics[@type=\"detached\"]/x:topic[x:title/text()=\"JIRA\"]/x:children/x:topics/x:topic" -v "concat(x:title, '=', (x:children//x:title)[1])" -n $TMPCONTENT`
if [[ -z "$JIRATOPIC" ]]; then
>&2 echo ERROR: Missing or empty detached "JIRA" topic on sheet \"$sheet\" containing topics marked for export
>&2 echo Skipping processing of sheet \"$sheet\"
continue
fi
FIELDNAMES=()
FIELDVALUES=()
# Thanks: https://unix.stackexchange.com/questions/296644/how-to-read-a-properties-file-into-an-associative-array
while IFS=$'=' read name value; do
case "$name" in
"" | "Summary" | "Description")
# Ignore empty or special fields
;;
*)
FIELDNAMES+=("$name")
FIELDVALUES+=("$value")
;;
esac
done <<< "$JIRATOPIC"
# Output direct children topics of an issue as headings in the Description field and concatenate their descendant titles as bullet points
COLS=($FIELDNAMES "Summary" "Description")
read -d '' ISSUETEMPLATE << EOF
-o "${(j:,:)COLS}"
-m "/*/x:sheet[x:title/text()='$sheet']//x:topic[x:marker-refs/x:marker-ref[@marker-id='$EXPORTMARKER']]" -n
-o "${(j:,:)FIELDVALUES},"
-v 'x:title'
-o ',"'
-m 'x:children/x:topics/x:topic'
-n -o 'h2.' -v 'x:title/text()' -n
-m 'x:children/x:topics/x:topic'
-n -o '* ' -v 'x:title' -m 'x:children//x:title' -o ' - ' -v 'text()' -b
-b -n
-b -n
-o '"'
-n -b
EOF
ISSUETEMPLATE=`tr '\n' ' ' <<< $ISSUETEMPLATE`
eval $XMLSEL -T -t $ISSUETEMPLATE $TMPCONTENT > $EXPORTFILE
# Update markers to exported for all affected issues
>&2 echo Updating markers from $EXPORTMARKER to $EXPORTEDMARKER for all exported issues on sheet \"$sheet\"
$=XMLED -u "/*/x:sheet[x:title/text()='$sheet']//x:topic/x:marker-refs/x:marker-ref[@marker-id='$EXPORTMARKER']/@marker-id" -v $EXPORTEDMARKER $TMPCONTENT
done <<< "$ISSUESHEETS"
# Store the updated content in the original XMind file
>&2 echo Storing updated content in original file
zip -fjT "$XMINDFILE" $TMPCONTENT
>&2 echo Cleaning up temporary work directory $TMPDIR
rm -rf $TMPDIR
# =========================================================================================
# CODE GRAVEYARD
# ISSUEFIELDS=()
# ISSUEDEFAULTS=()
# # Thanks: https://unix.stackexchange.com/questions/296644/how-to-read-a-properties-file-into-an-associative-array
# while IFS=$'=' read field value; do
# case "$field" in
# "" | "Summary" | "Description")
# # Ignore empty or special fields
# ;;
# *)
# ISSUEFIELDS+=("$field")
# ISSUEDEFAULTS+=("$field" "$value")
# ;;
# esac
# done <<< "$JIRATOPIC"
# FIELDSEXPR=""
# for field value in ${(kv)ISSUEDEFAULTS}; do
# # Use the field value from the issue
# FIELDSEXPR+="-v '(.//x:title[text()=\"$field\"]/../x:children//x:title)[1]' "
# # Otherwise, fall back to default value specified on the sheet
# FIELDSEXPR+="-i 'count(.//x:title[text()=\"$field\"]) = 0' -o '$value' -b -o ',' "
# done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment