示例#1
0
def pivotalSource(details):
    """Produce a CoreJet XML file with stories for epics from Pivotal.

    The parameter should be a comma-separated string with the following
    parameters:

    <epic>,<epic>,... - optional cfg section names to retrieve options per epic
    token=<token> - default pivotal token to use in authentication
    project=<project> - default pivotal project id to retrieve stories from
    filter=<filter> - default pivotal filter string to retrieve stories
    title=<title> - optional title for the requirements catalog
                    (defaults to the first found pivotal project title)
    """

    sections = []
    defaults = {}

    for option in details.split(","):
        try:
            key, value = option.split("=", 1)
        except ValueError:
            # values without keys are interpreted as cfg-sections
            value = option.strip()
            if value:
                sections.append(value)
            continue
        defaults[key.strip().lower()] = value.strip()

    defaults = config.read("defaults", defaults)

    if not sections and "epics" in defaults:
        sections = [name.strip() for name in defaults["epics"].split(",")]

    if not sections:
        sections = ["defaults"]

    epics = []

    for section in sections:
        options = config.read(section, defaults)

        assert options.get("token", False),\
            u"Pivotal token is a mandatory option."
        assert options.get("project", False),\
            u"Pivotal project id is a mandatory option."
        assert options.get("filter", False),\
            u"Pivotal filter string is a mandatory option."

        # append filter from command line (when found)
        if defaults.get("filter", "") not in options["filter"]:
            options["filter"] += " %s" % defaults["filter"]

        # set includedone:true if it's not explicitly set otherwise
        if not "includedone:" in options["filter"]:
            options["filter"] += " includedone:true"

        try:
            pv = pivotal.Pivotal(options["token"], use_https=True)
        except TypeError:
            # Support HTTPS on pivotal_py == 0.1.3
            pivotal.BASE_URL = "https://www.pivotaltracker.com/services/v3/"
            pv = pivotal.Pivotal(options["token"])

        project = pv.projects(options["project"])
        project_etree = project.get_etree()
        project_title =\
            options.get("title", project_etree.findtext("name"))
        if not type(project_title) == unicode:  # ensure unicode
            project_title = unicode(project_title, "utf-8", "ignore")

        if not "title" in defaults:
            defaults["title"] = project_title

        epic_title = options.get("title", project_title)
        if not type(epic_title) == unicode:  # ensure unicode
            epic_title = unicode(epic_title, "utf-8", "ignore")

        epic = Epic(name=section != "defaults" and section
                    or str(sections.index(section) + 1),
                    title=epic_title)

        stories = project.stories(filter=options["filter"])
        stories_etree = stories.get_etree()

        for node in stories_etree:
            story = Story(node.findtext("id"), node.findtext("name"))
            story.status = node.findtext("current_state")
            if story.status in ["accepted", "rejected"]:
                story.resolution = story.status
            story.points = max(1, int(node.findtext("estimate", 0) or 0))

            appendScenariosFromPivotalStory(story, node, options)

            if story.scenarios:
                epic.stories.append(story)
        epics.append(epic)

    catalogue = RequirementsCatalogue(project=defaults["title"],
                                      extractTime=datetime.now())
    for epic in epics:
        catalogue.epics.append(epic)

    return catalogue
示例#2
0
def jiraSource(details):
    """Produce a CoreJet XML file from JIRA. The parameter should be a
    comma-separated string with the following parameters:
    
    username=<username> - username to use to connect
    password=<password> - password to use to connect
    url=<url> - url of JIRA instance
    project=<name> - project name
    filter=<id> - id of filter that returns stories
    pointsField=<id> - id of field containing story points
    epicField=<id> - id of field indicating epic for a story
    acceptanceCriteriaField=<id> - id of field containing acceptance criteria
    """

    parameters = extractParameters(details)

    username = parameters["username"]
    password = parameters["password"]
    url = parameters["url"]
    projectName = parameters["project"]

    filterId = parameters["filter"]
    pointsFieldId = parameters["pointsfield"]
    epicFieldId = parameters["epicfield"]
    acFieldId = parameters["acceptancecriteriafield"]

    catalogue = RequirementsCatalogue(project=projectName, extractTime=datetime.now())

    # Open web service connection
    wsdl = url + "/rpc/soap/jirasoapservice-v2?wsdl"
    client = Client(wsdl)

    # Log into JIRA

    print "Fetching repository from JIRA instance at", url

    securityToken = client.service.login(username, password)

    epicCache = {}
    statuses = {}
    resolutions = {}

    try:

        # Look up statuses and resolutions

        for status in client.service.getStatuses(securityToken):
            statuses[status.id] = status.name

        for resolution in client.service.getResolutions(securityToken):
            resolutions[resolution.id] = resolution.name

        # Fetch all issues
        issues = client.service.getIssuesFromFilter(securityToken, filterId)

        for issue in issues:
            story = Story(issue.key, issue.summary)

            try:
                story.points = int(singleValueCustomFieldForIssue(issue, pointsFieldId))
            except (ValueError, TypeError):
                pass

            story.status = statuses.get(issue.status, None)
            story.resolution = resolutions.get(issue.resolution, None)

            epicName = singleValueCustomFieldForIssue(issue, epicFieldId)

            # See if this epic is a reference to an issue
            epic = epicCache.get(epicName, None)
            if epic is None:
                epicTitle = epicName
                try:
                    epicIssue = client.service.getIssue(securityToken, epicName)
                    epicTitle = epicIssue.summary
                except WebFault:
                    # Can't find the issue? We use the epic name as its title
                    pass

                # Create a new epic, cache it, and add it to the catalogue
                epicCache[epicName] = epic = Epic(epicName, epicTitle)
                catalogue.epics.append(epic)

            epic.stories.append(story)
            story.epic = epic

            acceptanceCriteria = singleValueCustomFieldForIssue(issue, acFieldId)
            try:
                appendScenarios(story, acceptanceCriteria)
            except ValueError, e:
                print "Error parsing acceptance criteria for", issue.key, "-", str(e)

    finally:
        client.service.logout(securityToken)

    return catalogue