Skip to content

Instantly share code, notes, and snippets.

@acrollet
Created April 24, 2014 22:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acrollet/11271227 to your computer and use it in GitHub Desktop.
Save acrollet/11271227 to your computer and use it in GitHub Desktop.
Unfuddle to Jira, with comments
// Original author Gabe Nell. Released under the Apache 2.0 License
// http://www.apache.org/licenses/LICENSE-2.0.html
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilderFactory;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class UnfuddleToJira {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("yyyyMMdd");
private final Document doc;
private final PrintStream output;
private final Map<String, String> milestones;
private final Map<String, String> people;
public UnfuddleToJira(Document doc, PrintStream output) {
this.doc = doc;
this.output = output;
this.milestones = parseMilestones(doc);
this.people = parsePeople(doc);
}
private static Map<String, String> parseMilestones(Document doc) {
Map<String, String> milestones = new HashMap<String, String>();
NodeList milestoneNodes = doc.getElementsByTagName("milestone");
for (int i = 0; i < milestoneNodes.getLength(); i++) {
Element elem = (Element)milestoneNodes.item(i);
String title = elem.getElementsByTagName("title").item(0).getTextContent().replace(" ", "");
String id = elem.getElementsByTagName("id").item(0).getTextContent();
milestones.put(id, title);
}
System.out.println("Found " + milestones.size() + " milestones: " + milestones);
return milestones;
}
private static Map<String, String> parsePeople(Document doc) {
Map<String, String> people = new HashMap<String, String>();
NodeList peopleNodes = doc.getElementsByTagName("person");
for (int i = 0; i < peopleNodes.getLength(); i++) {
Element elem = (Element)peopleNodes.item(i);
String name = elem.getElementsByTagName("username").item(0).getTextContent();
String id = elem.getElementsByTagName("id").item(0).getTextContent();
people.put(id, name);
}
System.out.println("Found " + people.size() + " people: " + people);
return people;
}
private static String prepareForCsv(String input) {
if (input == null) return "";
return "\"" + input.replace("\"", "\"\"") + "\"";
}
private static String convertDate(String input) {
return DATE_FORMATTER.print(new DateTime(input));
}
private String lookupUser(String id) {
String person = people.get(id);
/**
* Here you can transform a person's username if it changed between
* Unfuddle and JIRA. Eg: <tt>
* if ("gabe".equals(person)) {
* person = "gabenell";
* }
* </tt>
*/
return person;
}
private String lookupMilestone(String id) {
return milestones.get(id);
}
private void writeCsvHeader() {
StringBuilder builder = new StringBuilder(256);
builder.append("Summary, ");
builder.append("Status, ");
builder.append("Assignee, ");
builder.append("Reporter,");
builder.append("Resolution,");
builder.append("CreateTime,");
builder.append("ResolveTime,");
builder.append("Milestone,");
builder.append("Description");
output.println(builder.toString());
}
private void writeCsvRow(Ticket ticket) {
StringBuilder builder = new StringBuilder(256);
builder.append(prepareForCsv(ticket.summary)).append(", ");
builder.append(prepareForCsv(ticket.status)).append(", ");
builder.append(prepareForCsv(lookupUser(ticket.assigneeId))).append(", ");
builder.append(prepareForCsv(lookupUser(ticket.reporterId))).append(", ");
builder.append(prepareForCsv(ticket.resolution)).append(", ");
builder.append(prepareForCsv(convertDate(ticket.createdTime))).append(", ");
String resolveTime = ticket.resolution != null ? convertDate(ticket.lastUpdateTime) : null;
builder.append(prepareForCsv(resolveTime)).append(", ");
builder.append(prepareForCsv(lookupMilestone(ticket.milestoneId))).append(", ");
builder.append(prepareForCsv(ticket.description));
// JIRA doesn't have the notion of a resolution description, add it as a
// comment
if (ticket.resolutionDescription != null) {
builder.append(",").append(prepareForCsv(ticket.resolutionDescription));
}
builder.append(",").append(ticket.comments);
output.println(builder.toString());
}
public void writeCsv() throws Exception {
NodeList ticketNodes = doc.getElementsByTagName("ticket");
List<Ticket> tickets = new ArrayList<Ticket>();
for (int i = 0; i < ticketNodes.getLength(); i++) {
Node node = ticketNodes.item(i);
Element nodeElem = (Element)node;
Ticket ticket = new Ticket();
NodeList ticketElements = nodeElem.getChildNodes();
for (int j = 0; j < ticketElements.getLength(); j++) {
Node ticketSubNode = ticketElements.item(j);
String nodeName = ticketSubNode.getNodeName();
if ("id".equals(nodeName)) {
ticket.id = ticketSubNode.getTextContent();
} else if ("status".equals(nodeName)) {
ticket.status = ticketSubNode.getTextContent();
} else if ("summary".equals(nodeName)) {
ticket.summary = ticketSubNode.getTextContent();
} else if ("description".equals(nodeName)) {
ticket.description = ticketSubNode.getTextContent();
} else if ("milestone-id".equals(nodeName)) {
ticket.milestoneId = ticketSubNode.getTextContent();
} else if ("assignee-id".equals(nodeName)) {
ticket.assigneeId = ticketSubNode.getTextContent();
} else if ("reporter-id".equals(nodeName)) {
ticket.reporterId = ticketSubNode.getTextContent();
} else if ("resolution".equals(nodeName)) {
ticket.resolution = ticketSubNode.getTextContent();
} else if ("resolution-description".equals(nodeName)) {
ticket.resolutionDescription = ticketSubNode.getTextContent();
} else if ("created-at".equals(nodeName)) {
ticket.createdTime = ticketSubNode.getTextContent();
} else if ("updated-at".equals(nodeName)) {
ticket.lastUpdateTime = ticketSubNode.getTextContent();
} else if ("comments".equals(nodeName)) {
if (ticketSubNode instanceof Element) {
String commentText="";
Element commentsElement = (Element)ticketSubNode;
NodeList commentElements = commentsElement.getElementsByTagName("comment");
for (int k = 0; k < commentElements.getLength(); k++) {
Node commentSubNode = commentElements.item(k);
String commentNodeName = commentSubNode.getNodeName();
NodeList commentChildElements = commentSubNode.getChildNodes();
Comment comment = new Comment();
for (int l = 0; l < commentChildElements.getLength(); l++) {
Node commentChildSubNode = commentChildElements.item(l);
String commentChildNodeName = commentChildSubNode.getNodeName();
if ("id".equals(commentChildNodeName)) {
comment.id = commentChildSubNode.getTextContent();
}
else if ("author-id".equals(commentChildNodeName)) {
comment.authorId = commentChildSubNode.getTextContent();
}
else if ("created-at".equals(commentChildNodeName)) {
comment.createdTime = convertDate(commentChildSubNode.getTextContent());
}
else if ("body".equals(commentChildNodeName)) {
comment.description = commentChildSubNode.getTextContent();
}
}
if (comment.description.length() > 0) {
commentText += prepareForCsv(comment.createdTime + ";" + lookupUser(comment.authorId) + ";" + comment.description);
commentText += ",";
}
}
ticket.comments = commentText;
}
}
}
tickets.add(ticket);
}
System.out.println("Writing " + tickets.size() + " tickets...");
// Output to CSV in order of ticket number
writeCsvHeader();
Collections.sort(tickets);
for (Ticket ticket : tickets) {
writeCsvRow(ticket);
}
}
public static class Ticket implements Comparable<Ticket> {
public String id;
public String summary;
public String status;
public String description;
public String milestoneId;
public String assigneeId;
public String reporterId;
public String resolution;
public String resolutionDescription;
public String createdTime;
public String lastUpdateTime;
public String comments;
@Override
public int compareTo(Ticket other) {
return Integer.parseInt(id) - Integer.parseInt(other.id);
}
}
public static class Comment implements Comparable<Comment> {
public String id;
public String description;
public String authorId;
public String createdTime;
@Override
public int compareTo(Comment other) {
return Integer.parseInt(id) - Integer.parseInt(other.id);
}
}
public static void main(String[] args) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
if (args.length != 2) {
System.err.println("Usage: UnfuddleToJira /path/to/unfuddle/backup.xml /path/to/jira/output.csv");
return;
}
String inputFilename = args[0];
String outputFilename = args[1];
PrintStream output = new PrintStream(new FileOutputStream(outputFilename), true, "UTF-8");
UnfuddleToJira converter = new UnfuddleToJira(factory.newDocumentBuilder().parse(inputFilename), output);
converter.writeCsv();
output.close();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment