Example #1
0
class JIRAUtilities:
    # Translate state changes to JIRA transition values
    transition_ids = {'ToDo': '11', 'Prog': '21', 'Imped': '41', 'Done': '31'}

    user_story_dict_cloud = {
        #'project': 'SP',
        #'project': 'TTP',  # Tsafi, 15 Jan 2020
        'project':
        DEFAULT_JIRA_PARAMS[JIRA_INST]['PROJECT'],  # Tsafi 3 Feb 2020
        'issuetype': 'Story',
        'summary': "Story 302",
        'customfield_10322': [{
            'value': 'Dev Team 1'
        }]  # Team field ********cloud
    }

    user_story_dict_vm = {
        #'project': 'SP',
        #'project': 'TTP', #Tsafi 14 Jan 2020 - temp change to work with TTP project already exists on local Jira
        'project':
        DEFAULT_JIRA_PARAMS[JIRA_INST]['PROJECT'],  # Tsafi 3 Feb 2020
        'issuetype': 'Story',
        'summary': "Story 302",
        #'customfield_10201': [{'value': 'Dev Team 1'}]  # Team field vm
        'customfield_10200': [{
            'value': 'Dev Team 1'
        }]  # Tsafi 21 Jan 2020
    }

    # 10120 is the 'Epic Name' and it's a must have
    epic_dict_cloud = {
        #'project': 'SP',
        #'project': 'TTP',  # Tsafi, 15 Jan 2020
        'project':
        DEFAULT_JIRA_PARAMS[JIRA_INST]['PROJECT'],  # Tsafi 3 Feb 2020
        #'issuetype': 'Epic',
        'issuetype': {
            'name': 'Epic'
        },  # Tsafi 15 Jan 2020
        'summary': "Epic 1",
        'customfield_10120':
        "Epic 1",  # Epic name is the same as Epic summary ********cloud
        'customfield_10322': [{
            'value': 'Dev Team 1'
        }]  # Team field ********cloud
    }

    epic_dict_vm = {
        'project':
        DEFAULT_JIRA_PARAMS[JIRA_INST]['PROJECT'],  # Tsafi 3 Feb 2020
        'issuetype': 'Epic',
        'summary': "Epic 1",
        'customfield_10103':
        "Epic 1",  # Epic name is the same as Epic summary vm
        #'customfield_10102': "Epic 1",  # Tsafi 14 Jan 2020
        'customfield_10200': [{
            'value': 'Dev Team 1'
        }]  # Tsafi 21 Jan 2020 - Team field vm
    }

    def __init__(self, instance_type):
        self.instance_type = instance_type

        if instance_type == 'cloud':
            # Using Nela's Jira cloud account
            #self.jira_inst = JIRA(basic_auth=('*****@*****.**', 'FiJBeI3H81sceRofBcY4E84E'),
            #                      options={'server': 'https://dr-agile.atlassian.net'})
            # Using Tsafi's Jira cloud account
            #self.jira_inst = JIRA(basic_auth=('*****@*****.**', 'tDshA7M9zEhMkaiC13RA146E'),
            #                      options={'server': 'https://dr-agile.atlassian.net'})
            self.jira_inst = JIRA(
                basic_auth=(DEFAULT_JIRA_PARAMS['CLOUD']['USER'],
                            DEFAULT_JIRA_PARAMS['CLOUD']['PASS']),
                options={'server': DEFAULT_JIRA_PARAMS['CLOUD']['URL']})
        else:
            # Using Nela's Jira local VM
            #self.jira_inst = JIRA(basic_auth=('nela.g', 'q1w2e3r4'), options={'server': 'http://192.168.56.101:8080'})
            # Using Tsafi's Jira local VM
            #self.jira_inst = JIRA(basic_auth=('tsafrir.m', 'Sim1965'), options={'server': 'http://192.168.43.55:8080'})
            #self.jira_inst = JIRA(basic_auth=('tsafrir.m', 'Sim1965'), options={'server': 'http://192.168.43.41:8080'})
            #self.jira_inst = JIRA(basic_auth=('tsafrir.m', 'Sim1965'), options={'server': 'http://10.0.0.61:8080'})
            self.jira_inst = JIRA(
                basic_auth=(DEFAULT_JIRA_PARAMS['LOCAL']['USER'],
                            DEFAULT_JIRA_PARAMS['LOCAL']['PASS']),
                options={'server': DEFAULT_JIRA_PARAMS['LOCAL']['URL']})

    def __del__(self):
        del self.jira_inst

    # Creation
    def create_epic(self, name, team):
        if self.instance_type == 'cloud':
            dict = self.epic_dict_cloud
            dict['customfield_10120'] = name  # ********cloud
            dict['customfield_10322'] = [{'value': team.name}]  # ********cloud
        else:
            dict = self.epic_dict_vm
            dict['customfield_10103'] = name  # tsafi 21 Jan 2020
            #dict['customfield_10102'] = name # Tsafi 14 Jan 2020
            #dict['customfield_10201'] = {'value': team.name}
            dict['customfield_10200'] = {
                'value': team.name
            }  # Tsafi 21 Jan 2020

        dict['summary'] = name

        epic = self.jira_inst.create_issue(fields=dict)
        return epic

    def create_user_story_with_epic(self,
                                    user_story_name,
                                    team,
                                    epic_key=None):
        if self.instance_type == 'cloud':
            dict = self.user_story_dict_cloud
            dict['customfield_10322'] = [{'value': team.name}]  # ********cloud
        else:
            dict = self.user_story_dict_vm
            #dict['customfield_10201'] = {'value': team.name}
            dict['customfield_10200'] = {
                'value': team.name
            }  # Tsafi 21 Jan 2020

        dict['summary'] = user_story_name

        user_story = self.jira_inst.create_issue(fields=dict)

        if epic_key:
            self.jira_inst.add_issues_to_epic(epic_key, [user_story.key])

        # Note the performance impact, this is another fetch after
        # adding a story to epic
        user_story = self.jira_inst.issue(user_story.id)
        return user_story

    def create_list_of_epics(self, list_of_epics, team):
        print('\nCreating list of epics for team %s in JIRA' % team.name)
        for epic in list_of_epics:
            jira_epic = self.create_epic(epic.name, team)
            epic.key = jira_epic.key

    def create_list_of_user_stories(self, list_of_user_stories, team):
        print('\nCreating list of user stories for team %s in JIRA' %
              team.name)
        for user_story in list_of_user_stories:
            jira_user_story = self.create_user_story_with_epic(
                user_story.name, team, user_story.epic.key)
            user_story.key = jira_user_story.key

    # Create Sprint
    # Best would be to provide a board_id of a board that contains all issues
    def create_sprint(self, board_id, sprint_name, start_date, sprint_size):
        end_date = start_date + timedelta(days=sprint_size)

        start_date_str = start_date.strftime("%d/%b/%y %#I:%M %p")
        end_date_str = end_date.strftime("%d/%b/%y %#I:%M %p")
        print(start_date_str)
        print(end_date_str)

        new_sprint = self.jira_inst.create_sprint(sprint_name,
                                                  board_id,
                                                  startDate=start_date_str,
                                                  endDate=end_date_str)

        print("Created new Sprint: name = %s, id =  %s" %
              (new_sprint.name, new_sprint.id))
        print("Start date %s, end date %s" %
              (new_sprint.startDate, new_sprint.endDate))

        return new_sprint

    # Set up Sprints
    def start_sprint(self, sprint_id):
        print("Please start the sprint manually on a global board")
        key = input()

    def end_sprint(self, sprint_id):
        print("Please end the sprint manually on a global board")
        key = input()

    def add_issues_to_sprint(self, sprint_id, user_stories_keys):
        print("Adding %d issues to Sprint %d" %
              (len(user_stories_keys), sprint_id))
        self.jira_inst.add_issues_to_sprint(sprint_id, user_stories_keys)

    def update_one_issue(self, issue_key, transition):
        print("Updating issue %s, moving to %s" % (issue_key, transition))
        self.jira_inst.transition_issue(issue_key,
                                        self.transition_ids[transition])

    def advance_time_by_one_day(self):
        if self.instance_type != 'cloud':
            #Tsafi 20 Jan 2020, change path for Tsafi's local VM
            #path = "C:\\Windows\\WinSxS\\amd64_openssh-client-components-onecore_31bf3856ad364e35_10.0.17763.1_none_f0c3262e74c7e35c\\ssh.exe [email protected] \"cd /home/nelkag/Simulator/misc; python /home/nelkag/Simulator/misc/advanceoneday.py\""
            #path = "C:\\Windows\\System32\\OpenSSH\\ssh.exe [email protected] \"cd /home/tsafi/SAFESimulator; python3 ./advanceoneday.py\""
            #path = "C:\\Windows\\System32\\OpenSSH\\ssh.exe [email protected] python3 /home/tsafi/SAFESimulator/advanceoneday.py"
            #path = "C:\\Windows\\System32\\OpenSSH\\ssh.exe [email protected] date"
            #path = "C:\\Windows\\Sysnative\\OpenSSH\\ssh.exe [email protected] python3 ./SAFESimulator/advanceoneday.py"
            #path = "C:\\Windows\\Sysnative\\OpenSSH\\ssh.exe [email protected] python3 ./SAFESimulator/advanceoneday.py"
            path = 'C:\\Windows\\Sysnative\\OpenSSH\\ssh.exe tsafi@' + LOCAL_JIRA_IP + ' python3 ./SAFESimulator/advanceoneday.py'
            print(path)
            os.system(path)
Example #2
0
class JiraTool:
    def __init__(self):
        self.server = jira_url
        self.basic_auth = (usrer, password)
        self.jiraClinet = None

    def login(self):
        self.jiraClinet = JIRA(server=self.server, basic_auth=self.basic_auth)
        if self.jiraClinet != None:
            return True
        else:
            return False

    def findIssueById(self, issueId):
        if issueId:
            if self.jiraClinet == None:
                self.login()
            return self.jiraClinet.issue(issueId)
        else:
            return 'Please input your issueId'

    def deleteAllIssue(self, project):
        project_issues = self.jiraClinet.search_issues('project=' +
                                                       project.name)
        for issue in project_issues:
            logging.info('delete issue %s' % (issue))
            issue.delete()

    def deleteAllSprint(self, board):
        sprints = self.jiraClinet.sprints(board.id)
        for sprint in sprints:
            logging.info('delete sprint %s' % (sprint.name))
            sprint.delete()

    def getProject(self, name):
        projects = self.jiraClinet.projects()
        #logging.info("get project %s" %(name))
        for project in projects:
            #logging.info("project %s" %(project.name))
            if (name == project.name):
                return project

        return None
        #return self.jiraClinet.create_project(key='SCRUM', name=name, assignee='yujiawang', type="Software", template_name='Scrum')

    def getBoard(self, project, name):
        boards = self.jiraClinet.boards()
        for board in boards:
            if (name == board.name):
                logging.info("board:%s id:%d" % (board.name, board.id))
                return board

        return self.jiraClinet.create_board(name, [project.id])

    def createSprint(self, board, name, startDate=None, endDate=None):
        logging.info("==== create sprint[%s] in board[%s] ====>" %
                     (name, board.name))
        sprint = self.jiraClinet.create_sprint(name, board.id, startDate,
                                               endDate)
        return sprint

    def getSprint(self, board_id, sprint_name):
        sprints = self.jiraClinet.sprints(board_id)
        for sprint in sprints:
            if sprint.name == sprint_name:
                return sprint
        return None

    def createEpicTask(self, project, sprint, summary, description, assignee,
                       participant, duedate):
        issue_dict = {
            'project': {
                'key': project.key
            },
            'issuetype': {
                'id': issuetypes['Epic']
            },
            'customfield_10002': summary,  # epic 名称
            'summary': summary,
            'description': description,
            "customfield_10004": sprint.id,  # sprint
            'assignee': {
                'name': assignee
            },
            'customfield_10303': participant,
            'customfield_10302': '2018-08-24T05:41:00.000+0800'
        }

        logging.info(duedate)

        logging.info(issue_dict)  #juse for debug
        issue = self.jiraClinet.create_issue(issue_dict)
        self.jiraClinet.add_issues_to_sprint(sprint.id, [issue.raw['key']])
        logging.info(
            "===> add epic task[%s key:%s] to sprint [%s]" %
            (issue.raw['fields']['summary'], issue.raw['key'], sprint.name))
        #dumpIssue(issue) #juse for debug
        return issue

    def createTask(self, project, sprint, epic, summary, description, assignee,
                   participant):
        issue_dict = {
            'project': {
                'key': project.key
            },
            'issuetype': {
                'id': issuetypes['Task']
            },
            'summary': summary,
            'description': description,
            'assignee': {
                'name': assignee
            },
            'customfield_10303': participant
        }

        issue = self.jiraClinet.create_issue(issue_dict)
        logging.info("==> add task[%s key:%s] link epic [%s key: %s]" %
                     (issue.raw['fields']['summary'], issue.raw['key'],
                      epic.raw['fields']['summary'], epic.raw['key']))
        self.jiraClinet.add_issues_to_epic(epic.id, [issue.raw['key']])
        self.jiraClinet.add_issues_to_sprint(sprint.id, [issue.raw['key']])
        return issue

    def createSubTask(self, project, parent, summary, description, assignee,
                      participant):
        issue_dict = {
            'project': {
                'key': project.key
            },
            'parent': {
                'key': parent.raw['key']
            },
            'issuetype': {
                'id': issuetypes['Sub-Task']
            },
            'summary': summary,
            'description': description,
            'assignee': {
                'name': assignee
            },
            'customfield_10303': participant
        }
        issue = self.jiraClinet.create_issue(issue_dict)
        logging.info("=> add sub task[%s key:%s] to task [%s key: %s]" %
                     (issue.raw['fields']['summary'], issue.raw['key'],
                      parent.raw['fields']['summary'], parent.raw['key']))
        return issue
from jira import JIRA
from jira.resources import Sprint
import time
from datetime import datetime, timedelta



analytics_jira = JIRA(server="SERVER URL",\
    basic_auth=("USERNAME", "PASSWORD"))

teams = {"DS Sprint": 3, "DAO Sprint": 8}

start_date = (time.strftime("%m/%d"))
date = datetime.strptime(start_date, "%m/%d")
modified_startdate = date + timedelta(days=46)
new_start = datetime.strftime(modified_startdate, "%m/%d")
modified_enddate = modified_startdate + timedelta(days=13)
end_date = datetime.strftime(modified_enddate, "%m/%d")

for the_key, the_value in teams.iteritems():
    a = the_key
    b = the_value
    c = 42
    d = '(' + str(new_start) + '-' + str(end_date) + ')'

    analytics_jira.create_sprint(name='{0} {1} {2}'.format(a, c, d),
                                 board_id='{0}'.format(b))

print("Success")
Example #4
0
class JiraAPI(object):
    """Access Jira api using python-jira project."""
    def __init__(self, user, passwd, logger):
        """Init JiraApi object and logger."""
        self.logger = logger
        self.jira = JIRA(server=config.JIRA_HOST, basic_auth=(user, passwd))

    def get_boards(self):
        """Get Jira boards list as json."""
        self.logger.info("Getting Jira boards")
        json = {'boards': []}
        boards = self.jira.boards()
        for board in boards:
            json.get('boards').append({'id': board.id, 'name': board.name})
        return json

    # ###############
    # ### Sprints ###
    # ###############

    def _get_board_sprints(self, board_id):
        """Get sprints of a board as json."""
        self.logger.info("Getting board {} sprints".format(board_id))
        json = {'sprints': []}
        sprints = self.jira.sprints(board_id, extended=True)
        for sprint in sprints:
            # self.logger.debug("Sprint content: {}".format(sprint.__dict__))
            json.get('sprints').append({
                'id': sprint.id,
                'key': sprint.id,
                'name': sprint.name,
                'start': sprint.startDate,
                'end': sprint.endDate
            })
        return json

    def search_sprint(self, board_key, sprint_name):
        """Search a sprint in a board and returns its info."""
        self.logger.info("Searching board {} sprint '{}'".format(
            board_key, sprint_name))
        found_sprint = None
        sprints = self._get_board_sprints(board_key)
        for sprint in sprints.get('sprints'):
            if (sprint.get('name') == sprint_name):
                found_sprint = sprint
                break
        return found_sprint

    def create_sprint(self, board_id, sprint_name, start, end):
        """Create a sprint in a board with given start and end dates.

        Dates must be in Jira format.
        """
        self.logger.info("Creating sprint {}".format(sprint_name))
        sprint = self.jira.create_sprint(name=sprint_name,
                                         board_id=board_id,
                                         startDate=start,
                                         endDate=end)
        # self.logger.debug("Created sprint content {}".format(sprint.__dict__))
        return sprint.id

    def start_sprint(self, sprint_id, sprint_name, start, end):
        """Start a Jira sprint with given dates. *** Does not work because Jira API call fails with 'state' param. ***

        Dates must be in Jira format.
        """
        self.logger.info("Starting sprint {} [Not working]".format(sprint_id))
        # self.jira.update_sprint(sprint_id, name=sprint_name, startDate=start, endDate=end, state="active")

    def close_sprint(self, sprint_id, sprint_name, start, end):
        """Closes a Jira sprint with given dates. *** Does not work because Jira API call fails with 'state' param. ***

        Dates must be in Jira format.
        """
        self.logger.info("Closing sprint {} [Not working]".format(sprint_id))
        # self.jira.update_sprint(sprint_id, name=sprint_name, startDate=start, endDate=end, state=None)

    def delete_board_sprints(self, board_id):
        """Delete all sprints of a board."""
        self.logger.info("Deleting board {} sprints".format(board_id))
        sprints = self.jira.sprints(board_id, extended=False)
        for sprint in tqdm(sprints):
            self.logger.info("Deleting sprint {}".format(sprint.name))
            sprint.delete()

    def _get_board_project_id(self, board_id):
        """Get the project id of a board."""
        self.logger.debug("Getting project id of board {}".format(board_id))
        project_id = None
        boards = self.jira.boards()
        for board in boards:
            # self.logger.debug("Comparing boards {}-{}".format(board.id, board_id))
            if str(board.id) == str(board_id):
                # self.logger.debug("Found matching board {}".format(board.raw))
                project_id = board.raw.get("filter").get("queryProjects").get(
                    "projects")[0].get("id")
                break
        return project_id

    # ####################
    # ### User stories ###
    # ####################

    def _get_project_user_stories(self, project_key):
        """Get all user stories of a project."""
        self.logger.info("Getting project {} user stories".format(project_key))
        issues = self.jira.search_issues('project=' + project_key +
                                         ' and issuetype=' +
                                         config.JIRA_USER_STORY_TYPE,
                                         maxResults=200)
        return issues

    def delete_all_project_user_stories(self, project_key):
        """Delete all user stories of a project."""
        self.logger.info(
            "Deleting project {} user stories".format(project_key))
        issues = self._get_project_user_stories(project_key)
        for issue in tqdm(issues):
            self.logger.info("Deleting user story {}".format(issue))
            issue.delete()

    def _create_user_story(self, project_id, subject, description, tags,
                           points):
        """Create a user story with provided information."""
        story_fields = {
            "project": project_id,
            "issuetype": config.JIRA_USER_STORY_TYPE,
            "summary": subject,
            "description": description,
            "labels": tags,
            config.JIRA_ESTIMATION_FIELD: points
        }
        created_story = self.jira.create_issue(fields=story_fields)
        self.logger.info("Created user story {} # {}".format(
            created_story.id, created_story.key))
        # self.logger.debug("Created user story details: {}".format(created_story.__dict__))
        return created_story

    def update_user_story_status(self, user_story_key, is_closed, status):
        """Update the status of a user story to Done or Not Done only if it's closed.

        A translation is made between the status given, which is the status in Taiga and the Jira status as
        defined in JIRA_USER_STORY_STATUS_DONE, JIRA_USER_STORY_STATUS_NOT_DONE and TAIGA_USER_STORY_STATUS_NOT_DONE
        constants of config.py file.
        """
        self.logger.info(
            "Updating user story {} with Taiga status={} ({})".format(
                user_story_key, status, is_closed))
        if is_closed:
            task_status = config.JIRA_USER_STORY_STATUS_DONE
            if status == config.TAIGA_USER_STORY_STATUS_NOT_DONE:
                task_status = config.JIRA_USER_STORY_STATUS_NOT_DONE
            self.jira.transition_issue(user_story_key, task_status)
            self.logger.info("Updated user story {} status to {}".format(
                user_story_key, task_status))
        else:
            self.logger.warn(
                "Not updated user story {} beacuse is not closed: is_closed={}"
                .format(user_story_key, is_closed))

    # ########################
    # ### User story tasks ###
    # ########################

    def _create_user_story_task(self, project_id, user_story_id, subject,
                                description, finished_date):
        """Create a task inside a user story.

        The story task type is defined in config.py in JIRA_USER_STORY_TASK_TYPE constant. default is 'Sub-type'.
        """
        self.logger.info("Creating user story task {}".format(subject))
        created_task = self.jira.create_issue(
            project=project_id,
            parent={"id": user_story_id},
            issuetype=config.JIRA_USER_STORY_TASK_TYPE,
            summary=subject)
        self.logger.info("Created user story task {} # {}".format(
            created_task.id, created_task.key))
        # self.logger.debug("Created story task details: {}".format(created_task.__dict__))
        return created_task

    def _update_task_status(self, task_key, status):
        """Update task status with Done or not Done status depending on incoming Taiga status.

        Taiga done status is defined in TAIGA_TASK_STATUS_DONE constant inside config.py file.
        Jira done and not done statuses are defined in JIRA_TASK_STATUS_DONE and JIRA_TASK_STATUS_NOT_DONE inside
        config.py file.
        """
        self.logger.info(
            "Updating user story task {} with Taiga status={}".format(
                task_key, status))
        task_status = config.JIRA_TASK_STATUS_DONE
        if status != config.TAIGA_TASK_STATUS_DONE:
            task_status = config.JIRA_TASK_STATUS_NOT_DONE
        self.jira.transition_issue(task_key, task_status)
        self.logger.info("Updated user story task {} status to {}".format(
            task_key, task_status))

    # #######################
    # ### Sprint creation ###
    # #######################

    def _add_comment(self, issue_id, comment):
        """Add a comment to an issue."""
        self.logger.debug("Adding comment to issue {}".format(issue_id))
        self.jira.add_comment(issue_id,
                              comment,
                              visibility={'group': 'jira-users'})

    def create_sprint_stories(self, board_id, sprint_id, user_stories):
        """Create user stories in a board and link them to a sprint.

        A json array with user story key, is_closed and status info is returned.
        User stories subtasks are also added to the story.
        User story tasks finished date and current taiga status are added as comments.
        User story finished date and current taiga status are added as comments.
        """
        project_id = self._get_board_project_id(board_id)
        self.logger.info(
            "Creating user stories in project {} - sprint {}".format(
                project_id, sprint_id))
        created_stories = []
        for user_story in user_stories:
            self.logger.info("Creating user story {}".format(
                user_story.get("subject")))
            created_story = self._create_user_story(
                project_id, user_story.get("subject"),
                user_story.get("description"), user_story.get("tags"),
                user_story.get("total_points"))
            self.logger.info("Adding user story {} to sprint {}".format(
                created_story.key, sprint_id))
            self.jira.add_issues_to_sprint(sprint_id, [created_story.key])
            for task in user_story.get("tasks"):
                created_task = self._create_user_story_task(
                    project_id, created_story.id, task.get("subject"),
                    task.get("description"), task.get("finished_date"))
                # Add as comment user story finished date
                self._add_comment(
                    created_task.id,
                    "Finished date: '{}'".format(task.get('finished_date')))
                self._add_comment(
                    created_task.id,
                    "Taiga status: '{}'".format(task.get("status")))
                # Update task status
                self._update_task_status(created_task.key, task.get("status"))
            created_stories.append({
                "key": created_story.key,
                "is_closed": user_story.get("is_closed"),
                "status": user_story.get("status")
            })
            # Add as comment user story finished date
            self._add_comment(
                created_story.id,
                "Finished date: '{}'".format(user_story.get('finish_date')))
            self._add_comment(
                created_story.id,
                "Taiga status: '{}'".format(user_story.get("status")))
        return created_stories

    def add_backlogs_stories(self, board_id, user_stories):
        """Add user stories to the backlog of the board.

        Taiga original status and backlog order are added as comments.
        """
        project_id = self._get_board_project_id(board_id)
        self.logger.info(
            "Creating user stories in project {} backlog".format(project_id))
        created_stories = []
        for user_story in user_stories:
            self.logger.info("Creating user story {}".format(
                user_story.get("subject")))
            created_story = self._create_user_story(
                project_id, user_story.get("subject"),
                user_story.get("description"), user_story.get("tags"),
                user_story.get("total_points"))
            created_stories.append({"key": created_story.key})
            # Add as comment user story finished date
            self._add_comment(
                created_story.id,
                "Taiga status: '{}'".format(user_story.get("status")))
            self._add_comment(
                created_story.id, "Taiga backlog order: '{}'".format(
                    user_story.get("backlog_order")))
        return created_stories