Exemplo n.º 1
0
def push_event_hook():
    push_event = request.json
    app.logger.debug(push_event)
    user_name = push_event['user_name']
    repo_name = push_event['repository']['name']
    repo_url = push_event['repository']['url']
    repo_homepage = push_event['repository']['homepage']
    refspec = push_event['ref']
    app.logger.debug('Received push event by %s in branch %s on repository %s',
                     user_name, refspec, repo_url)

    for commit in push_event['commits']:
        app.logger.debug('Processing commit %s by %s (%s) in %s', commit['id'],
                         commit['author']['name'], commit['author']['email'],
                         commit['url'])
        commit_time = dateutil.parser.parse(commit['timestamp'])
        issues = re.findall(app.config['REGEX'], commit['message'],
                            re.MULTILINE)
        if not issues:
            app.logger.debug(
                '''Didn't find any referenced issues in commit %s''',
                commit['id'])
        else:
            app.logger.debug('Found %d referenced issues in commit %s',
                             len(issues), commit['id'])
            yt = Connection(app.config['YOUTRACK_URL'],
                            app.config['YOUTRACK_USERNAME'],
                            app.config['YOUTRACK_PASSWORD'])

            user_login = get_user_login(yt, commit['author']['email'])
            if user_login is None:
                app.logger.warn(
                    "Couldn't find user with email address %s. Using default user.",
                    commit['author']['email'])
                default_user = yt.getUser(app.config['DEFAULT_USER'])
                user_login = default_user['login']

            for issue_id in issues:
                app.logger.debug('Processing reference to issue %s', issue_id)
                try:
                    yt.getIssue(issue_id)
                    comment_string = 'Commit [%(url)s %(id)s] on branch %(refspec)s in [%(repo_homepage)s %(repo_name)s] made by %(author)s on %(date)s\n{quote}%(message)s{quote}' % {
                        'url': commit['url'],
                        'id': commit['id'],
                        'author': commit['author']['name'],
                        'date': str(commit_time),
                        'message': commit['message'],
                        'repo_homepage': repo_homepage,
                        'repo_name': repo_name,
                        'refspec': refspec
                    }
                    app.logger.debug(comment_string)
                    yt.executeCommand(issueId=issue_id,
                                      command='comment',
                                      comment=comment_string.encode('utf-8'),
                                      run_as=user_login.encode('utf-8'))
                except YouTrackException:
                    app.logger.warn("Couldn't find issue %s", issue_id)
    return Response('Push event processed. Thanks!', mimetype='text/plain')
 def process_commits(secret=None):
     if context_secret and secret != context_secret:
         abort(403)
     yt = Connection(yt_url, yt_login, yt_password)
     try:
         cmd_pattern = re.compile(
             r'#((?:%s)-\d+)(?:\s+(.+))?' %
             '|'.join(yt.getProjects().keys()),
             re.IGNORECASE | re.MULTILINE)
     except YouTrackException:
         app.logger.warning('Cannot get projects from YT')
         cmd_pattern = re.compile(r'#([A-z]+-\d+)(?:\s+(.+))?',
                                  re.MULTILINE)
     payload = json.loads(request.form.get('payload'))
     commits_url_template = get_commits_url_template(payload)
     for commit in payload['commits']:
         message = commit['message'].encode('utf-8')
         issue_refs = cmd_pattern.findall(message)
         if not issue_refs:
             continue
         commit_node = commit['node']
         commit_url = commits_url_template % commit['raw_node']
         timestamp = commit['utctimestamp']
         author = commit['author'].encode('utf-8')
         match = re.search(r'<(.+?)>', commit['raw_author'])
         if not match:
             app.logger.error("Cannot get author's email address.")
             abort(400)
         users = yt.getUsers(params={'q': match.group(1)})
         if not users:
             app.logger.error('Cannot find user with email ' +
                              match.group(1))
             abort(400)
         if len(users) != 1:
             app.logger.error('Not unique email address ' + match.group(1))
             abort(400)
         comment = "Commit [%s %s] made by '''%s''' on ''%s''\n{quote}%s{quote}" \
                   % (commit_url, commit_node, author, timestamp, message)
         cmd_exec_result = True
         for issue_id, command in issue_refs:
             if command is None:
                 command = ''
             try:
                 app.logger.info(
                     "Adding commit %s to issue %s (command: %s)" %
                     (commit_node, issue_id, command))
                 yt.executeCommand(issue_id,
                                   command,
                                   comment,
                                   run_as=users[0].login)
             except YouTrackException as e:
                 cmd_exec_result = False
                 app.logger.error(
                     'Failed to add commit %s to issue %s: %s' %
                     (commit_node, issue_id, e.message))
         if not cmd_exec_result:
             abort(500)
     return 'success'
Exemplo n.º 3
0
 def submit(self):
     try:
         connection = Connection(settings.YOUTRACK_URL, settings.YOUTRACK_LOGIN, settings.YOUTRACK_PASSWORD)
         response, content = connection.createIssue(self.project, assignee=None,
                                                    summary=u'Issue from feedback form',
                                                    description=self.cleaned_data['description'])
         print response
         issue_id = response['location'].split('/')[-1]
         connection.executeCommand(issue_id, 'Customer email ' + self.cleaned_data['email'])
         return True
     except YouTrackException:
         return False
Exemplo n.º 4
0
def push_event_hook(room=None):
    push_event = request.json
    app.logger.debug(push_event)
    app.logger.debug("slack_room:%s" % room)

    # process mergerequest
    if 'object_kind' in push_event and push_event['object_kind'] == 'merge_request':
        obj = push_event['object_attributes']
        user = push_event['user']
        slack = Slacker(app.config['SLACK_TOKEN'])
        url = "%s/%s/%s/merge_requests/%d" % (app.config['GITLAB_HOME'], obj['target']['namespace'], obj['target']['name'], obj['iid'])
        print url
        event = "Pull Request(%s): %s - %s\n%s" % (obj['state'], url, obj['title'], obj['description'])
        slack.chat.post_message('#%s' % room, event, username=user['username'], icon_url=user['avatar_url'])
        return Response('Push event processed. Thanks!', mimetype='text/plain')

    user_name = push_event['user_name']
    repo_name = push_event['repository']['name']
    repo_url = push_event['repository']['url']
    repo_homepage = push_event['repository']['homepage']
    refspec = push_event['ref']
    app.logger.debug('Received push event by %s in branch %s on repository %s', user_name, refspec, repo_url)

    for commit in push_event['commits']:
        app.logger.debug('Processing commit %s by %s (%s) in %s', commit['id'], commit['author']['name'], commit['author']['email'], commit['url'])
        commit_time = dateutil.parser.parse(commit['timestamp'])
        issues = re.findall(app.config['REGEX'], commit['message'], re.MULTILINE)
        if not issues:
            app.logger.debug('''Didn't find any referenced issues in commit %s''', commit['id'])
        else:
            app.logger.debug('Found %d referenced issues in commit %s', len(issues), commit['id'])
            yt = Connection(app.config['YOUTRACK_URL'], app.config['YOUTRACK_USERNAME'], app.config['YOUTRACK_PASSWORD'])

            user_login = get_user_login(yt, commit['author']['email'])
            if user_login is None:
                app.logger.warn("Couldn't find user with email address %s. Using default user.", commit['author']['email'])
                default_user = yt.getUser(app.config['DEFAULT_USER'])
                user_login = default_user['login']

            for issue_id in issues:
                app.logger.debug('Processing reference to issue %s', issue_id)
                try:
                    yt.getIssue(issue_id)
                    comment_string = 'Commit [%(url)s %(id)s] on branch %(refspec)s in [%(repo_homepage)s %(repo_name)s] made by %(author)s on %(date)s\n{quote}%(message)s{quote}' % {'url': commit['url'], 'id': commit['id'], 'author': commit['author']['name'], 'date': str(commit_time), 'message': commit['message'], 'repo_homepage': repo_homepage, 'repo_name': repo_name, 'refspec': refspec}
                    app.logger.debug(comment_string)
                    yt.executeCommand(issueId=issue_id, command='comment', comment=comment_string.encode('utf-8'),
                                      run_as=user_login.encode('utf-8'))
                except YouTrackException:
                    app.logger.warn("Couldn't find issue %s", issue_id)
    return Response('Push event processed. Thanks!', mimetype='text/plain')
def add_tags(tag_file,target_url, target_login, target_password):
    """
    Add tags from the tag file (run after known creation)
    :param tag_file:
    :return:
    """
    target = Connection(target_url, target_login, target_password)
    with open(tag_file) as tag_o_file:
        reader = csv.reader(tag_o_file)
        for row in reader:
            issue = row[0]
            for t in row[1:]:
                print "executing tag for %s" %(issue)
                target.executeCommand(issue, "tag %s" % (t))
 def process_commits(secret=None):
     if context_secret and secret != context_secret:
         abort(403)
     yt = Connection(yt_url, yt_login, yt_password)
     try:
         cmd_pattern = re.compile(
             r'#((?:%s)-\d+)(?:\s+(.+))?' % '|'.join(yt.getProjects().keys()),
             re.IGNORECASE | re.MULTILINE)
     except YouTrackException:
         app.logger.warning('Cannot get projects from YT')
         cmd_pattern = re.compile(r'#([A-z]+-\d+)(?:\s+(.+))?', re.MULTILINE)
     payload = json.loads(request.form.get('payload'))
     commits_url_template = get_commits_url_template(payload)
     for commit in payload['commits']:
         message = commit['message'].encode('utf-8')
         issue_refs = cmd_pattern.findall(message)
         if not issue_refs:
             continue
         commit_node = commit['node']
         commit_url = commits_url_template % commit['raw_node']
         timestamp = commit['utctimestamp']
         author = commit['author'].encode('utf-8')
         match = re.search(r'<(.+?)>', commit['raw_author'])
         if not match:
             app.logger.error("Cannot get author's email address.")
             abort(400)
         users = yt.getUsers(params={'q': match.group(1)})
         if not users:
             app.logger.error('Cannot find user with email ' + match.group(1))
             abort(400)
         if len(users) != 1:
             app.logger.error('Not unique email address ' + match.group(1))
             abort(400)
         comment = "Commit [%s %s] made by '''%s''' on ''%s''\n{quote}%s{quote}" \
                   % (commit_url, commit_node, author, timestamp, message)
         cmd_exec_result = True
         for issue_id, command in issue_refs:
             if command is None:
                 command = ''
             try:
                 app.logger.info("Adding commit %s to issue %s (command: %s)" %
                                 (commit_node, issue_id, command))
                 yt.executeCommand(issue_id, command, comment, run_as=users[0].login)
             except YouTrackException as e:
                 cmd_exec_result = False
                 app.logger.error('Failed to add commit %s to issue %s: %s' %
                                  (commit_node, issue_id, e.message))
         if not cmd_exec_result:
             abort(500)
     return 'success'
Exemplo n.º 7
0
 def submit(self):
     try:
         connection = Connection(settings.YOUTRACK_URL, settings.YOUTRACK_LOGIN, settings.YOUTRACK_PASSWORD)
         response, content = connection.createIssue(self.project, assignee=None,
                                                    summary=self.get_summary().encode('utf-8'),
                                                    description=self.cleaned_data['description'].encode('utf-8'))
         issue_id = response['location'].split('/')[-1]
         commands = ''
         if self.subsystem is not None:
             commands += ' Subsystem %s' % self.subsystem
         commands += ' Customer email ' + self.cleaned_data['email']
         connection.executeCommand(issue_id, commands)
         return True
     except YouTrackException:
         return False
Exemplo n.º 8
0
 def submit(self):
     try:
         connection = Connection(settings.YOUTRACK_URL,
                                 settings.YOUTRACK_LOGIN,
                                 settings.YOUTRACK_PASSWORD)
         response, content = connection.createIssue(
             self.project,
             assignee=None,
             summary=u'Issue from feedback form',
             description=self.cleaned_data['description'])
         print response
         issue_id = response['location'].split('/')[-1]
         connection.executeCommand(
             issue_id, 'Customer email ' + self.cleaned_data['email'])
         return True
     except YouTrackException:
         return False
Exemplo n.º 9
0
class YtClient(object):

    def __init__(self, server, username, password):
        self.logger = LoggerFactory.getLogger("com.xebialabs.yourtrack.Server")
        if server is None:
            sys.exit("No server provided.")
        if username is not None:
            youtrackUsername = username
        else:
            youtrackUsername = server['username']
        if password is not None:
            youtrackPassword = password
        else:
            youtrackPassword = server['password']

        if server['token'] is not None:
            # authentication request with permanent token
            self.youtrack = YouTrack(server['url'], token=server['token'])
        else:
            # authentication with username and password
            self.youtrack = YouTrack(server['url'], login=youtrackUsername, password=youtrackPassword)

    def getIssue(self, issueId):
        return self.youtrack.getIssue(issueId)

    def getAllIssues(self, query, withFields):
        self.logger.debug("getAllIssues")
        self.logger.debug("Query = %s" % query)
        self.logger.debug("Fields = %s" % withFields)
        return self.youtrack.getAllIssues(filter=query, withFields=withFields)

    def updateIssuesByQuery(self, query, fieldsToUpdate, comment):
        command = ""
        for key in fieldsToUpdate:
            command += key + " " + fieldsToUpdate[key]
        foundIssues = self.youtrack.getAllIssues(filter=query, withFields=[])
        for issue in foundIssues:
            self.youtrack.executeCommand(issue['id'], command, comment)

    def getIssuesByQuery(self, query, fieldList=[]):
        self.logger.debug("getIssuesByQuery")
        return self.youtrack.getAllIssues(filter=query, withFields=[])
Exemplo n.º 10
0
def push_event_hook():
    push_event = request.json
    app.logger.debug(push_event)
    user_name = push_event['user_name']
    repo_name = push_event['repository']['name']
    repo_url = push_event['repository']['url']
    repo_homepage = push_event['repository']['homepage']
    refspec = push_event['ref']
    app.logger.debug('Received push event by %s in branch %s on repository %s', user_name, refspec, repo_url)

    for commit in push_event['commits']:
        app.logger.debug('Processing commit %s by %s (%s) in %s', commit['id'], commit['author']['name'], commit['author']['email'], commit['url'])
        commit_time = dateutil.parser.parse(commit['timestamp'])
        issues = re.findall(app.config['REGEX'], commit['message'], re.MULTILINE)
        if not issues:
            app.logger.debug('''Didn't find any referenced issues in commit %s''', commit['id'])
        else:
            app.logger.debug('Found %d referenced issues in commit %s', len(issues), commit['id'])
            yt = Connection(app.config['YOUTRACK_URL'], app.config['YOUTRACK_USERNAME'], app.config['YOUTRACK_PASSWORD'])

            default_user = yt.getUser(app.config['DEFAULT_USER'])
            user_login = default_user['login']

            users = yt.getUsers({ 'q': commit['author']['email'] })
            if not users:
                app.logger.warn('''Couldn't find user with email address %s. Using default user.''', commit['author']['email'])
            elif len(users) > 1:
                app.logger.warn('''Found more than one user with email address %s. Using default user.''', commit['author']['email'])
            else:
                user_login = users[0]['login']

            for issue_id in issues:
                app.logger.debug('Processing reference to issue %s', issue_id)
                try:
                    issue = yt.getIssue(issue_id)
                    comment_string = 'Commit [%(url)s %(id)s] on branch %(refspec)s in [%(repo_homepage)s %(repo_name)s] made by %(author)s on %(date)s\n{quote}%(message)s{quote}' % {'url': commit['url'], 'id': commit['id'], 'author': commit['author']['name'], 'date': str(commit_time), 'message': commit['message'], 'repo_homepage': repo_homepage, 'repo_name': repo_name, 'refspec': refspec}
                    app.logger.debug(comment_string)
                    yt.executeCommand(issueId=issue_id, command='comment', comment=comment_string.encode('utf-8'),
                                      run_as=user_login.encode('utf-8'))
                except YouTrackException:
                    app.logger.warn('''Couldn't find issue %s''', ref)
    return Response('Push event processed. Thanks!', mimetype='text/plain')
Exemplo n.º 11
0
def issue_actualizar_estado(bot, update, user_data):
    bot.sendChatAction(chat_id=update.callback_query.from_user.id,
                       action=ChatAction.TYPING)
    if update.callback_query.data == 'issue_estado_cerrar':
        estado = 'Fixed'
    else:
        estado = 'In Progress'

    logger.info('Actualizar estado issue {} {}'.format(user_data['issue'],
                                                       estado))
    try:
        connection = Connection(user_data['host']['host'],
                                user_data['host']['username'],
                                user_data['host']['pass'])
        command = 'State ' + estado
        connection.executeCommand(user_data['issue'], command)

        update.callback_query.edit_message_text("Gracias amego!")

    except Exception as e:
        logger.error(e)
        update.callback_query.edit_message_text("No eh!")
    return ConversationHandler.END
Exemplo n.º 12
0
def receive_hook():
    payload = request.json
    user_name = payload['user_name']
    repo_url = payload['repository']['url']
    app.logger.debug('Received payload for a push by %s on repository %s', user_name, repo_url)

    for commit in payload['commits']:
        app.logger.debug('Processing commit %s by %s (%s) in %s', commit['id'], commit['author']['name'], commit['author']['email'], commit['url'])
        commit_time = dateutil.parser.parse(commit['timestamp'])
        refs = re.findall(app.config['REGEX'], commit['message'], re.MULTILINE)
        if not refs:
            app.logger.info('''Didn't find any referenced issues in commit %s''', commit['id'])
        else:
            app.logger.info('Found %d referenced issues in commit %s', len(refs), commit['id'])

            yt = Connection(app.config['YOUTRACK_URL'], app.config['YOUTRACK_USERNAME'], app.config['YOUTRACK_PASSWORD'])

            user = app.config['DEFAULT_USER']
            users = yt.getUsers({ 'q': commit['author']['email'] })
            if not users:
                app.logger.warn('''Couldn't find user with email address %s. Using default user.''', commit['author']['email'])
            elif len(users) > 1:
                app.logger.warn('''Found more than one user with email address %s. Using default user.''', commit['author']['email'])
            else:
                user = users[0]['login']

            for ref in refs:
                app.logger.info('Processing reference to issue %s', ref)
                try:
                    issue = yt.getIssue(ref)
                    comment_string = 'Commit [%(url)s %(id)s] made by %(author)s on %(date)s\n{quote}%(message)s{quote}' % {'url': commit['url'], 'id': commit['id'], 'author': commit['author']['name'], 'date': str(commit_time), 'message': commit['message']}
                    app.logger.debug(comment_string)
                    yt.executeCommand(issueId=ref, command='comment', comment=comment_string, run_as=user)
                except YouTrackException:
                    app.logger.warn('''Couldn't find issue %s''', ref)
    return Response('Payload processed. Thanks!', mimetype='text/plain')
Exemplo n.º 13
0
                    if params.get('add_new_comments'):
                        target_comments = dict()
                        max_id = 0
                        for c in target_issue.getComments():
                            target_comments[c.created] = c
                            if max_id < c.created:
                                max_id = c.created
                        for c in issue.getComments():
                            if c.created > max_id or c.created not in target_comments:
                                group = None
                                if hasattr(c, 'permittedGroup'):
                                    group = c.permittedGroup
                                try:
                                    target.executeCommand(
                                        issue.id, 'comment', c.text, group,
                                        c.author)
                                except youtrack.YouTrackException, e:
                                    print 'Cannot add comment to issue '
                                    print e

                    if params.get('sync_custom_fields'):
                        skip_fields = []
                        if tt_settings and tt_settings.Enabled and tt_settings.TimeSpentField:
                            skip_fields.append(tt_settings.TimeSpentField)
                        skip_fields = [name.lower() for name in skip_fields]
                        for pcf in [
                                pcf for pcf in project_custom_fields
                                if pcf.name.lower() not in skip_fields
                        ]:
                            target_cf_value = None
                for issue in issues:
                    try:
                        target_issue = target.getIssue(issue.id)
                    except youtrack.YouTrackException, e:
                        print "Cannot get target issue"
                        print e
                        continue

                    if params.get('sync_tags') and issue.tags:
                        try:
                            for tag in issue.tags:
                                tag = re.sub(r'[,&<>]', '_', tag)
                                try:
                                    target.executeCommand(
                                        issue.id,
                                        'tag ' + tag,
                                        disable_notifications=True)
                                except youtrack.YouTrackException:
                                    tag = re.sub(r'[\s-]', '_', tag)
                                    target.executeCommand(
                                        issue.id,
                                        'tag ' + tag,
                                        disable_notifications=True)
                        except youtrack.YouTrackException, e:
                            print "Cannot sync tags for issue " + issue.id
                            print e

                    if params.get('add_new_comments'):
                        target_comments = dict()
                        max_id = 0
                        for c in target_issue.getComments():
def trac2youtrack(target_url, target_login, target_password, project_ID, project_name, env_path):
    # creating connection to trac to import issues to
    client = Client(env_path)
    # creating connection to util to import issues in
    target = Connection(target_url, target_login, target_password)

    # create project
    print("Creating project[%s]" % project_name)
    try:
        target.getProject(project_ID)
    except youtrack.YouTrackException:
        target.createProjectDetailed(project_ID, project_name, client.get_project_description(), target_login)

    # importing users
    trac_users = client.get_users()
    print("Importing users")
    yt_users = list([])

    # converting trac users to yt users
    registered_users = set([])
    for user in trac_users :
        print("Processing user [ %s ]" % user.name)
        registered_users.add(user.name)
        yt_users.append(to_youtrack_user(user))
        # adding users to yt project
    target.importUsers(yt_users)
    print("Importing users finished")

    print("Creating project custom fields")

    create_yt_custom_field(target, project_ID, "Priority", client.get_issue_priorities())

    create_yt_custom_field(target, project_ID, "Type", client.get_issue_types())

    trac_resolution_to_yt_state = lambda track_field, yt_bundle : to_youtrack_state(track_field, yt_bundle)
    create_yt_bundle_custom_field(target, project_ID, "Resolution", client.get_issue_resolutions(), trac_resolution_to_yt_state)

    trac_version_to_yt_version = lambda trac_field, yt_bundle : to_youtrack_version(trac_field, yt_bundle)

    trac_versions = client.get_versions()
    create_yt_bundle_custom_field(target, project_ID, "Affected versions", trac_versions, trac_version_to_yt_version)

    trac_milestones = client.get_milestones()
    create_yt_bundle_custom_field(target, project_ID, "Fix versions", trac_milestones, trac_version_to_yt_version)

    trac_components = client.get_components()
    for cmp in trac_components :
        if cmp.owner not in registered_users :
            cmp.owner, registered_users = process_non_authorised_user(target, registered_users, cmp.owner)
    trac_component_to_yt_subsystem = lambda trac_field, yt_bundle : to_youtrack_subsystem(trac_field, yt_bundle)
    create_yt_bundle_custom_field(target, project_ID, "Component", trac_components, trac_component_to_yt_subsystem)

    create_yt_custom_field(target, project_ID, "Severity", client.get_severities())

    trac_custom_fields = client.get_custom_fields_declared()
    check_box_fields = dict([])
    for elem in trac_custom_fields:
        print("Processing custom field [ %s ]" % elem.name)
        if elem.type == "checkbox":
            if len(elem.label) > 0:
                opt = elem.label
            else:
                opt = elem.name
            options = list([opt])
            check_box_fields[elem.name] = opt
        else:
            options = elem.options

        values = None
        if len(options):
            values = options

        field_name = elem.name
        if field_name in youtrackutils.tracLib.FIELD_NAMES.keys() :
            field_name = youtrackutils.tracLib.FIELD_NAMES[field_name]

        field_type = youtrackutils.tracLib.CUSTOM_FIELD_TYPES[elem.type]
        if field_name in youtrackutils.tracLib.FIELD_TYPES.keys():
            field_type = youtrackutils.tracLib.FIELD_TYPES[field_name]

        process_custom_field(target, project_ID, field_type, field_name, trac_values_to_youtrack_values(field_name, values))
        print("Creating project custom fields finished")

    print("Importing issues")
    trac_issues = client.get_issues()
    yt_issues = list([])
    counter = 0
    max = 100
    for issue in trac_issues:
        print("Processing issue [ %s ]" % (str(issue.id)))
        counter += 1
        if not (issue.reporter in registered_users):
            yt_user, registered_users = process_non_authorised_user(target, registered_users, issue.reporter)
            if yt_user is None :
                issue.reporter = "guest"
            else:
                issue.reporter = yt_user
        if not (issue.owner in registered_users):
            yt_user, registered_users = process_non_authorised_user(target, registered_users, issue.owner)
            if yt_user is None :
                issue.owner = ""
            else:
                issue.owner = yt_user
        legal_cc = set([])
        for cc in issue.cc:
            if cc in registered_users:
                legal_cc.add(cc)
        issue.cc = legal_cc

        yt_issues.append(to_youtrack_issue(project_ID, issue, check_box_fields))
        if counter == max:
            counter = 0
            print(target.importIssues(project_ID, project_name + ' Assignees', yt_issues))
            yt_issues = list([])
    print(target.importIssues(project_ID, project_name + ' Assignees', yt_issues))
    print('Importing issues finished')

    # importing tags
    print("Importing keywords")
    for issue in trac_issues:
        print("Importing tags from issue [ %s ]" % (str(issue.id)))
        tags = issue.keywords
        for t in tags:
            target.executeCommand(str(project_ID) + "-" + str(issue.id), "tag " + t.encode('utf-8'))
    print("Importing keywords finished")

    print("Importing attachments")
    for issue in trac_issues:
        print("Processing issue [ %s ]" % (str(issue.id)))
        issue_attach = issue.attachment
        for attach in issue_attach:
            print("Processing attachment [ %s ]" % attach.filename.encode('utf-8'))
            if not (attach.author_name in registered_users):
                yt_user, registered_users = process_non_authorised_user(target, registered_users, attach.author_name)
                if yt_user is None:
                    attach.author_name = "guest"
                else:
                    attach.author_name = yt_user
            content = open(urllib.quote(attach.filename.encode('utf-8')))
            target.createAttachment(str(project_ID) + "-" + str(issue.id), attach.name, content, attach.author_name,
                                    created=attach.time)
    print("Importing attachments finished")

    print("Importing workitems")
    tt_enabled = False
    for issue in trac_issues:
        if issue.workitems:
            if not tt_enabled:
                tt_settings = target.getProjectTimeTrackingSettings(str(project_ID))
                if not tt_settings.Enabled:
                    print("Enabling TimeTracking for the project")
                    target.setProjectTimeTrackingSettings(str(project_ID), enabled=True)
                tt_enabled = True
            print("Processing issue [ %s ]" % (str(issue.id)))
            workitems = [to_youtrack_workitem(w) for w in issue.workitems]
            target.importWorkItems(str(project_ID) + "-" + str(issue.id), workitems)
    print("Importing workitems finished")
def mantis2youtrack(
    target_url,
    target_login,
    target_pass,
    mantis_db_name,
    mantis_db_host,
    mantis_db_port,
    mantis_db_login,
    mantis_db_pass,
    mantis_project_names,
):
    print "target_url             : " + target_url
    print "target_login           : "******"target_pass            : "******"mantis_db_name         : " + mantis_db_name
    print "mantis_db_host         : " + mantis_db_host
    print "mantis_db_port         : " + mantis_db_port
    print "mantis_db_login        : "******"mantis_db_pass         : "******"mantis_project_names   : " + repr(mantis_project_names)

    # connacting to yt
    target = Connection(target_url, target_login, target_pass)
    # connacting to mantis
    client = MantisClient(
        mantis_db_host, int(mantis_db_port), mantis_db_login, mantis_db_pass, mantis_db_name, mantis.CHARSET
    )
    if not len(mantis_project_names):
        print "You should declarer at least one project to import"
        sys.exit()

    value_sets = dict([])

    print "Importing users"
    yt_users = []
    value_sets["user"] = set([])
    for user in client.get_mantis_users():
        print "Processing user [ %s ]" % user.user_name
        value_sets["user"].add(user.user_name)
        yt_users.append(to_yt_user(user))
    target.importUsers(yt_users)
    print "Importing users finished"

    print "Creating custom fields definitions"
    value_sets.update(create_custom_fields(target, u"priority", mantis.PRIORITY_VALUES.values()))
    value_sets.update(create_custom_fields(target, u"severity", mantis.SEVERITY_VALUES.values()))
    value_sets.update(create_custom_fields(target, u"category", ["No subsystem"], "1"))
    value_sets.update(create_custom_fields(target, u"version", [], "1"))
    value_sets.update(create_custom_fields(target, u"fixed_in_version", [], "1"))
    value_sets.update(create_custom_fields(target, u"build", [], "1"))
    value_sets.update(create_custom_fields(target, u"platform"))
    value_sets.update(create_custom_fields(target, u"os"))
    value_sets.update(create_custom_fields(target, u"os_build"))
    value_sets.update(create_custom_fields(target, u"due_date"))
    value_sets.update(create_custom_fields(target, u"Reproducibility", mantis.REPRODUCIBILITY_VALUES.values()))

    # create custom field for target version
    field = None
    try:
        field = target.getCustomField("Fix versions")
    except YouTrackException:
        pass
    if field is not None:
        if hasattr(field, "defaultBundle"):
            bundle = field.defaultBundle
            for name in get_yt_cf_name_from_mantis_cf_name("target_version"):
                try:
                    target.createCustomFieldDetailed(
                        name,
                        mantis.FIELD_TYPES[name],
                        False,
                        True,
                        True,
                        {"defaultBundle": bundle, "attachBundlePolicy": "1"},
                    )
                except YouTrackException:
                    pass

    value_sets.update(
        create_auto_attached_bundle_custom_fields(
            target,
            u"status",
            mantis.STATUS_VALUES.values(),
            lambda status, bundle, value_mapping: to_yt_state(status, bundle, value_mapping),
        )
    )

    value_sets.update(
        create_auto_attached_bundle_custom_fields(
            target,
            u"resolution",
            mantis.RESOLUTION_VALUES.values(),
            lambda resolution, bundle, value_mapping: to_yt_state(resolution, bundle, value_mapping),
        )
    )

    if mantis.CREATE_CF_FOR_SUBPROJECT:
        value_sets.update(create_custom_fields(target, u"subproject", [], "1"))

    handler_field_name = u"handler"
    value_sets.update(create_custom_fields(target, handler_field_name, [], "1"))
    for name in get_yt_cf_name_from_mantis_cf_name(handler_field_name):
        value_sets[name] = value_sets["user"]

    # adding some custom fields that are predefined in mantis
    project_ids = []
    for name in mantis_project_names:
        project_ids.append(client.get_project_id_by_name(name))

    custom_fields = client.get_mantis_custom_fields(project_ids)

    for cf_def in custom_fields:
        print "Processing custom field [ %s ]" % cf_def.name.encode("utf-8")
        value_sets.update(process_mantis_custom_field(target, cf_def))

    print "Creating custom fields definitions finished"

    for name in mantis_project_names:
        project_id = int(client.get_project_id_by_name(name))
        print "Creating project [ %s ] with name [ %s ]" % (project_id, name)
        try:
            target.getProject(str(project_id))
        except YouTrackException:
            target.createProjectDetailed(
                str(project_id), name, client.get_project_description(project_id), target_login
            )

        print "Importing components to project [ %s ]" % project_id
        value_sets.update(
            add_values_to_fields(
                target,
                project_id,
                u"category",
                client.get_mantis_categories(project_id),
                lambda component, yt_bundle, value_mapping: to_yt_subsystem(component, yt_bundle, value_mapping),
            )
        )
        print "Importing components to project [ %s ] finished" % project_id

        print "Importing versions to project [ %s ]" % project_id
        mantis_versions = client.get_mantis_versions(project_id)
        value_sets.update(
            add_values_to_fields(
                target,
                project_id,
                u"version",
                mantis_versions,
                lambda version, yt_bundle, value_mapping: to_yt_version(version, yt_bundle, value_mapping),
            )
        )

        value_sets.update(
            add_values_to_fields(
                target,
                project_id,
                u"fixed_in_version",
                mantis_versions,
                lambda version, yt_bundle, value_mapping: to_yt_version(version, yt_bundle, value_mapping),
            )
        )

        for name in get_yt_cf_name_from_mantis_cf_name("target_version"):
            value_sets[name] = value_sets["Fix versions"]

        print "Importing versions to project [ %s ] finished" % project_id

        print "Attaching custom fields to project [ %s ]" % project_id
        cf_ids = client.get_custom_fields_attached_to_project(project_id)

        ignore = []
        for cf in custom_fields:
            if cf.field_id in cf_ids:
                attach_field_to_project(target, project_id, cf.name)
            else:
                ignore.append(cf.name)

        if mantis.CREATE_CF_FOR_SUBPROJECT:
            value_sets.update(
                add_values_to_fields(
                    target,
                    project_id,
                    u"subproject",
                    client.get_mantis_subprojects(project_id),
                    lambda sp_name, yt_bundle, value_mapping: yt_bundle.createElement(sp_name)
                    if sp_name not in value_mapping
                    else yt_bundle.createElement(value_sets[sp_name]),
                )
            )
        print "Attaching custom fields to project [ %s ] finished" % project_id

        print "Importing issues to project [ %s ]" % project_id
        mantis_issues = client.get_mantis_issues(project_id)
        yt_issues = []
        max_count = 100
        for issue in mantis_issues:
            # print "Processing issue [ %s ]" % str(issue.id)
            yt_issues.append(to_yt_issue(issue, value_sets, ignore))
            if len(yt_issues) >= max_count:
                print target.importIssues(str(project_id), name + "Assignees", yt_issues)
                yt_issues = []
        target.importIssues(str(project_id), str(project_id) + "Assignees", yt_issues)
        print "Importing issues to project [ %s ] finished" % project_id

        print "Importing issue attachments to project [ %s ]" % project_id
        mantis_attachments = client.get_mantis_attachments(project_id)
        for attachment in mantis_attachments:
            print "Processing issue attachment [ %s ]" % str(attachment.id)
            content = StringIO(attachment.content)
            authorLogin = client.get_user_name_by_id(attachment.user_id)
            target.createAttachment(
                "%s-%s" % (project_id, attachment.bug_id),
                attachment.filename,
                content,
                authorLogin,
                attachment.file_type,
                None,
                str(attachment.date_added * 1000),
            )
        print "Importing issue attachments to project [ %s ] finished" % project_id

        print "Importing tags to issues from project [ %s ]" % project_id
        for issue in mantis_issues:
            print "Processing tags for issue [ %s ]" % str(issue.id)
            for tag in issue.tags:
                print "Processing tag [ %s ]" % tag.encode("utf8")
                target.executeCommand(str(project_id) + "-" + str(issue.id), "tag " + tag.encode("utf8"))
        print "Importing tags to issues from project [ %s ] finished" % project_id

    print "Importing issue links"
    mantis_issue_links = client.get_issue_links()
    yt_issue_links = []
    for link in mantis_issue_links:
        print "Processing issue link for source issue [ %s ]" % str(link.source)
        yt_issue_links.append(to_yt_link(link))
    print target.importLinks(yt_issue_links)
    print "Importing issue links finished"
Exemplo n.º 17
0
class RedmineImporter(object):
    def __init__(self, params):
        self._params = params
        self._projects = None
        self._max_issue_ids = {}
        self._issue_ids = {}
        self._relations = {}
        self._users = {}
        self._groups = {}
        self._subsystems = {}
        self._versions = {}
        self._tags = {}
        self._all_users_group = None

        # Connecting to Redmine
        rm_api_key = params.get('rm_api_key')
        if not rm_api_key and 'rm_api_key_file' in params:
            try:
                with open(params['rm_api_key_file'], 'r') as f:
                    rm_api_key = f.read().strip()
            except (OSError, IOError) as e:
                print(("Cannot load redmine api key from file: " + str(e)))
                sys.exit(1)

        if rm_api_key:
            self._source = youtrackutils.redmine.RedmineClient(
                rm_api_key, params['rm_url'])
        elif 'rm_login' in params:
            self._source = youtrackutils.redmine.RedmineClient(
                None, params['rm_url'], params.get('rm_login'),
                params.get('rm_password'))
        else:
            print("You have to provide Redmine API key or login/password")
            sys.exit(1)

        # Connecting to YouTrack
        token = params.get('token')
        if not token and 'token_file' in params:
            try:
                with open(params['token_file'], 'r') as f:
                    token = f.read().strip()
            except (OSError, IOError) as e:
                print(("Cannot load token from file: " + str(e)))
                sys.exit(1)
        if token:
            self._target = Connection(params['yt_url'], token=token)
        elif 'yt_login' in params:
            self._target = Connection(params['yt_url'], params.get('yt_login'),
                                      params.get('yt_password'))
        else:
            print("You have to provide YouTrack token or login/password")
            sys.exit(1)

        if not params.get('project_lead_login'):
            project_lead = params.get('yt_login')
            if not project_lead:
                for login in ('root', 'admin', 'administrator', 'guest'):
                    try:
                        project_lead = self._target.getUser(login).login
                        break
                    except youtrack.YouTrackException:
                        continue
            self._params['project_lead_login'] = project_lead

    def do_import(self, project_ids):
        try:
            projects2import = self._get_projects(project_ids)
        except youtrackutils.redmine.RedmineException as e:
            print(('FATAL:', e))
            sys.exit(1)

        print('===> Import Roles')
        self._import_roles()

        for project in list(projects2import.values()):
            self._import_project(project)

        print('===> Apply Relations')
        self._apply_relations()

    def _get_projects(self, project_ids=None, by_internal_id=False):
        if by_internal_id:
            by = 'by_iid'
        else:
            by = 'by_pid'
        if self._projects is None:
            self._projects = {'by_iid': {}, 'by_pid': {}}
        if project_ids:
            new_projects = [
                pid for pid in project_ids if pid not in self._projects[by]
            ]
        else:
            new_projects = None
        if new_projects is None or new_projects:
            for project in self._source.get_projects(new_projects):
                project.identifier = re.sub('\W', '', project.identifier)
                self._projects['by_iid'][project.id] = project
                self._projects['by_pid'][project.identifier] = project
        if project_ids:
            for pid in project_ids:
                if by == 'by_pid':
                    pid = re.sub('\W', '', pid)
                if pid not in self._projects[by]:
                    raise youtrackutils.redmine.RedmineException(
                        "Project '%s' doesn't exist in Redmine" % pid)
        return self._projects[by]

    def _get_project(self, project_id, by_internal_id=False):
        return self._get_projects([project_id], by_internal_id)[project_id]

    def _get_project_name(self, project):
        name = project.name
        while True:
            if not hasattr(project, 'parent'):
                break
            name = project.parent.name + ' :: ' + name
            project = self._get_project(project.parent.id, True)
        return name

    def _import_project(self, project):
        project_id = project.identifier
        project_name = self._get_project_name(project)
        project_desc = ''
        project_lead = self._params['project_lead_login']

        if hasattr(project, 'description') and project.description is not None:
            project_desc = project.description

        print(
            ("===> Importing Project '%s' (%s)" % (project_name, project_id)))
        try:
            print('Creating project...')
            self._target.getProject(project_id)
            print('Project already exists')
        except youtrack.YouTrackException:
            self._target.createProjectDetailed(project_id, project_name,
                                               project_desc, project_lead)
            print('Project successfully created')
        print('Import project members...')
        self._import_members(project)
        print('Import issues...')
        self._import_issues(project)

    def _to_yt_user(self, redmine_user):
        if isinstance(redmine_user, str):
            user_id = redmine_user
        else:
            user_id = redmine_user.id
        if user_id not in self._users:
            try:
                redmine_user = self._source.get_user(user_id)
            except ResourceNotFound:
                redmine_user = None
            user = youtrack.User()

            if redmine_user != None:
                try:
                    user.email = redmine_user.mail
                except AttributeError:
                    pass

                if not user.email and redmine_user.id == 2:
                    user.email = '*****@*****.**'

                try:
                    # In some cases redmine user login can be empty or missing.
                    # So, both cases should be handled.
                    user.login = redmine_user.login
                except AttributeError:
                    pass

                if not user.login and redmine_user.id == 2:
                    user.login = '******'

            if not hasattr(user, 'login') or not user.login:
                if hasattr(user, 'email') and user.email:
                    user.login = user.email
                else:
                    user.login = '******'
                print(('Cannot get login for user id=%s, set it to "%s"' %
                       (user_id, user.login)))

            # user.login = redmine_user.login or 'guest'
            # user.email = redmine_user.mail or '*****@*****.**'
            if redmine_user != None and user.login != 'guest':
                if redmine_user.firstname is None and redmine_user.lastname is None:
                    user.fullName = user.login
                elif redmine_user.firstname is None:
                    user.fullName = redmine_user.lastname
                elif redmine_user.lastname is None:
                    user.fullName = redmine_user.firstname
                else:
                    user.fullName = redmine_user.firstname + ' ' + redmine_user.lastname
            else:
                user.created = True

            if redmine_user != None and hasattr(redmine_user, 'groups'):
                user.groups = [
                    self._to_yt_group(g) for g in redmine_user.groups
                ]

            self._users[user_id] = user
        return self._users[user_id]

    def _to_yt_group(self, redmine_group, users=None):
        if not isinstance(redmine_group, str):
            redmine_group = redmine_group.name
        if redmine_group not in self._groups:
            group = youtrack.Group()
            group.name = redmine_group
            if users is None:
                users = []
            group.users = users
            self._groups[redmine_group] = group
        return self._groups[redmine_group]

    def _to_yt_role(self, name, projects=None):
        role = youtrack.UserRole()
        role.name = name
        if projects:
            if isinstance(projects, list):
                role.projects.extend(projects)
            else:
                role.projects.append(projects)
        return role

    def _to_yt_version(self, version):
        if isinstance(version, str):
            vid = version
        else:
            vid = version.id
        if vid not in self._versions:
            redmine_version = self._source.get_version(vid)
            version = youtrack.Version()
            version.name = redmine_version.name
            version.description = redmine_version.description
            if getattr(redmine_version, 'due_date', None):
                version.releaseDate = str(to_unixtime(
                    redmine_version.due_date))
            version.released = str(redmine_version.status == 'closed').lower()
            version.archived = 'false'
            self._versions[vid] = version
        return self._versions[vid]

    def _to_yt_subsystem(self, category):
        if isinstance(category, str):
            cid = category
        else:
            cid = category.id
        if cid not in self._subsystems:
            redmine_cat = self._source.get_category(cid)
            subsystem = youtrack.Subsystem()
            subsystem.name = redmine_cat.name
            if hasattr(redmine_cat, 'assigned_to'):
                subsystem.login = self._create_user(
                    redmine_cat.assigned_to).login
            self._subsystems[cid] = subsystem
        return self._subsystems[cid]

    def _get_assignee_group_name(self, project_id):
        return '%s Assignees' % project_id.upper()

    def _get_yt_issue_id(self, issue, as_number_in_project=False):
        project_id = self._projects['by_iid'][issue.project.id].identifier
        new_id = self._max_issue_ids.get(project_id, 0) + 1
        rid = int(issue.id)
        if rid not in self._issue_ids:
            self._max_issue_ids[project_id] = new_id
            self._issue_ids[rid] = {'id': new_id, 'project_id': project_id}
        if as_number_in_project:
            return self._issue_ids[rid]['id']
        return self._to_yt_issue_id(rid)

    def _to_yt_issue_id(self, iid):
        issue = self._issue_ids[iid]
        return '%s-%d' % (issue['project_id'], issue['id'])

    def _get_yt_issue_number(self, issue):
        return self._get_yt_issue_id(issue, True)

    def _import_members(self, project):
        project_id = project.identifier
        members = self._source.get_project_members(project.id)
        users_by_role = {}
        groups_by_role = {}
        if members:
            for member in members:
                # Sometimes roles can be duplicated
                if hasattr(member, 'roles'):
                    roles = set([r.name for r in member.roles])
                    if hasattr(member, 'group'):
                        group = self._to_yt_group(member.group.name)
                        for role in roles:
                            if role not in groups_by_role:
                                groups_by_role[role] = []
                            groups_by_role[role].append(group)
                    else:
                        user = self._to_yt_user(member.user)
                        for role in roles:
                            if role not in users_by_role:
                                users_by_role[role] = []
                            users_by_role[role].append(user)
        for role_name, users in list(users_by_role.items()):
            group = self._to_yt_group('%s %s' %
                                      (project_id.upper(), role_name))
            self._create_group(group)
            self._target.addUserRoleToGroup(
                group, self._to_yt_role(role_name, project_id))
            self._target.importUsers(users)
            for user in users:
                self._target.setUserGroup(user.login, group.name)
        for role_name, groups in list(groups_by_role.items()):
            for group in groups:
                self._create_group(group)
                self._target.addUserRoleToGroup(
                    group, self._to_yt_role(role_name, project_id))

    def _import_roles(self):
        existed_roles = [role.name for role in self._target.getRoles()]
        new_roles = {}
        for role in self._source.get_roles():
            if role.name in existed_roles:
                continue
            permissions = None
            if hasattr(role, 'permissions'):
                permissions = []
                for perm in role.permissions:
                    if isinstance(perm, str):
                        rm_perm = perm
                    else:
                        rm_perm = perm.name
                    yt_perm = \
                        youtrackutils.redmine.Mapping.PERMISSIONS.get(rm_perm)
                    if not yt_perm:
                        continue
                    if isinstance(yt_perm, list):
                        permissions.extend(yt_perm)
                    else:
                        permissions.append(yt_perm)
            new_roles[role.name] = permissions
        for role_name, role_permissions in list(new_roles.items()):
            role = self._to_yt_role(role_name)
            self._target.createRole(role)
            if role_permissions:
                for perm_name in role_permissions:
                    perm = youtrack.Permission()
                    perm.name = perm_name
                    self._target.addPermissionToRole(role, perm)

    def _import_issues(self, project):
        project_id = project.identifier
        limit = self._params.get('batch_size', DEFAULT_BATCH_SIZE)
        offset = 0
        assignee_group = self._get_assignee_group_name(project_id)
        while True:
            print(("---> Issues from {offset} to {limit}".format(
                offset=offset + 1, limit=offset + limit)))
            issues = self._source.get_project_issues(
                project.id, limit, offset,
                self._params.get('skip_on_error', False))
            if not issues:
                break
            issues = [
                issue for issue in issues if issue.project.id == project.id
            ]
            self._target.importIssues(
                project_id, assignee_group,
                [self._make_issue(issue, project_id) for issue in issues])
            for issue in issues:
                self._add_tags(issue)
                self._collect_relations(issue)
                self._add_attachments(issue)
                if self._params.get('import_time_entries', False):
                    self._enable_timetracking(project)
                    self._add_work_items(issue)
            offset += limit

    def _make_issue(self, redmine_issue, project_id):
        issue = youtrack.Issue()
        issue['comments'] = []
        print(("--- ISSUE #{0}".format(redmine_issue.id)))
        if self._params.get('use_markdown'):
            issue['markdown'] = "true"
        try:
            if not hasattr(redmine_issue, 'author'):
                redmine_issue.author = 2  # set to Anonymous
            if self._params.get('create_redmine_linkage', False):
                self._add_field_to_issue(project_id, issue, "redmine_id",
                                         int(redmine_issue.id))
            checklist_value = None
            for name, value in list(redmine_issue.attributes.items()):
                if name in ('project', 'attachments', 'tags'):
                    continue
                if name == 'assigned_to' and value.name in self._groups:
                    continue
                if name == 'id':
                    value = str(self._get_yt_issue_number(redmine_issue))
                if name == 'checklist':
                    checklist_value = str(value)
                    continue
                if name == 'custom_fields':
                    for field in value:
                        self._add_field_to_issue(project_id, issue, field.name,
                                                 getattr(field, 'value', None))
                elif name == 'journals':
                    self._add_journals(issue, value)
                else:
                    if name == 'category':
                        value = self._to_yt_subsystem(value)
                    self._add_field_to_issue(project_id, issue, name, value)
            if checklist_value:
                issue['description'] = issue[
                    'description'] + "\n\n#### Checklist\n" + checklist_value

        except Exception as e:
            print('Failed to process issue:')
            print(redmine_issue)
            traceback.print_exc()
            raise e
        return issue

    def _convert_value(self, field_name, value):
        conv_map = youtrackutils.redmine.Mapping.CONVERSION.get(field_name)
        if conv_map:
            if hasattr(value, 'value'):
                if value.value in conv_map:
                    value.value = conv_map[value.value]
            elif hasattr(value, 'name'):
                if value.name in conv_map:
                    value.name = conv_map[value.name]
        return value

    def _get_yt_field_name(self, field_name):
        return youtrackutils.redmine.Mapping.FIELD_NAMES.get(
            field_name, field_name)

    def _get_yt_field_type(self, field_name):
        return youtrackutils.redmine.Mapping.FIELD_TYPES.get(
            field_name, youtrack.EXISTING_FIELD_TYPES.get(field_name))

    def _add_field_to_issue(self, project_id, issue, name, value):
        if value is None:
            return
        field_name = self._get_yt_field_name(name)
        field_type = self._get_yt_field_type(field_name)
        if field_type is None:
            return
        value = self._convert_value(field_name, value)
        if isinstance(value, list):
            if not value:
                return
            issue[field_name] = []
            for v in value:
                v = self._create_field_value(project_id, field_name,
                                             field_type, v)
                issue[field_name].append(
                    self._get_value_presentation(field_type, v))
        else:
            value = self._create_field_value(project_id, field_name,
                                             field_type, value)
            issue[field_name] = self._get_value_presentation(field_type, value)

    def _create_field(self, project_id, field_name, field_type):
        project_fields = self._target.getProjectCustomFields(project_id)
        if field_name.lower() not in [f.name.lower() for f in project_fields]:
            all_fields = self._target.getCustomFields()
            if field_name.lower() not in [f.name.lower() for f in all_fields]:
                self._target.createCustomFieldDetailed(field_name, field_type,
                                                       False, True, False, {})
            if field_type in ('string', 'text', 'date', 'integer', 'float',
                              'period'):
                self._target.createProjectCustomFieldDetailed(
                    project_id, field_name, 'No ' + field_name)
            else:
                bundle_name = field_name + ' bundle'
                create_bundle_safe(self._target, bundle_name, field_type)
                self._target.createProjectCustomFieldDetailed(
                    project_id, field_name, 'No ' + field_name,
                    {'bundle': bundle_name})

    def _create_field_value(self, project_id, field_name, field_type, value):
        if field_type.startswith('user'):
            if hasattr(value, 'name'):
                value.name = self._create_user(value).login
            else:
                value = self._create_user(value).login
        elif field_type.startswith('version'):
            value = self._to_yt_version(value)
        if field_name == 'Assignee':
            return value
        if field_name in youtrack.EXISTING_FIELDS:
            return value
        self._create_field(project_id, field_name, field_type)
        if field_type in ('string', 'text', 'date', 'integer', 'float',
                          'period'):
            return value
        field = self._target.getProjectCustomField(project_id, field_name)
        bundle = self._target.getBundle(field_type, field.bundle)
        try:
            if hasattr(value, 'value'):
                value = value.value
            elif hasattr(value,
                         'name') and not (field_type.startswith('version') or
                                          field_type.startswith('ownedField')):
                value = value.name
            self._target.addValueToBundle(bundle, value)
        except youtrack.YouTrackException as e:
            if e.response.status != 409 or e.response.reason.lower(
            ) != 'conflict':
                print(e)
        return value

    def _get_value_presentation(self, field_type, value):
        if field_type == 'date':
            return str(to_unixtime(value))
        if field_type == 'integer':
            return '%d' % int(float(value))
        if field_type == 'float':
            return '%.5f' % float(value)
        if field_type == 'string':
            return value
        if field_type == 'text':
            return value
        if field_type == 'period':
            return '%d' % int(float(value) * 60)
        if hasattr(value, 'value'):
            return value.value
        elif hasattr(value, 'name'):
            return value.name
        return value

    def _create_user(self, user):
        user = self._to_yt_user(user)
        if not hasattr(user, 'created'):
            self._target.createUser(user)
            user.created = True
            if hasattr(user, 'groups'):
                for group in user.groups:
                    self._create_group(group)
                    self._target.setUserGroup(user.login, group.name)
        return user

    def _create_group(self, group):
        if isinstance(group, str):
            group = self._to_yt_group(group)
        if not hasattr(group, 'created'):
            try:
                self._target.getGroup(group.name)
            except youtrack.YouTrackException:
                self._target.createGroup(group)
            group.created = True
        return group

    def _add_journals(self, issue, journals):
        if not journals:
            return
        for rec in journals:
            if hasattr(rec,
                       'notes') and rec.notes is not None and rec.notes != '':
                comment = youtrack.Comment()
                if self._params.get('use_markdown'):
                    comment.markdown = "true"
                comment.text = rec.notes
                comment.author = self._create_user(rec.user).login
                comment.created = str(to_unixtime(rec.created_on))
                issue['comments'].append(comment)

    def _enable_timetracking(self, project):
        self._target.setProjectTimeTrackingSettings(project.identifier,
                                                    enabled=True)

    def _add_work_items(self, issue):
        import_data = []
        work_items = self._source.get_time_entries(issue.id)
        flatten_work_items = []
        for item in work_items:
            if 'time_entries' in item.attributes:
                time_entries = item.time_entries
                for time_entry in time_entries:
                    flatten_work_items.append(time_entry)
            else:
                flatten_work_items.append(item)

        for t in sorted(flatten_work_items, key=lambda tt: tt.spent_on):
            work_item = youtrack.WorkItem()
            work_item.authorLogin = self._create_user(t.user).login
            work_item.date = str(to_unixtime(t.spent_on))
            work_item.description = t.comments
            work_item.duration = int(float(t.hours) * 60)
            import_data.append(work_item)
        if import_data:
            self._target.importWorkItems(self._get_yt_issue_id(issue),
                                         import_data)

    def _add_tags(self, issue):
        if not hasattr(issue, 'tags') or not issue.tags:
            return
        try:
            if not self._tags:
                self._tags = {t.name: t for t in self._target.getTags()}
        except youtrack.YouTrackException as e:
            print(str(e))
            return

        try:
            yt_issue_id = self._get_yt_issue_id(issue)
        except youtrack.YouTrackException as e:
            print(str(e))
            return

        assigned_tags = []
        for r_tag in issue.tags:
            try:
                tag = self._get_or_create_tag(r_tag.name)
                self._target.executeCommand(yt_issue_id,
                                            command='tag ' + tag.name,
                                            disable_notifications=True)
                assigned_tags.append(tag.name)
            except youtrack.YouTrackException as e:
                print(str(e))
                return

        print("Issue [ %s ] assigned tags: %s" %
              (yt_issue_id, ', '.join(assigned_tags)))

    def _get_or_create_tag(self, tag_name):
        if tag_name not in self._tags:
            tag = youtrack.Tag()
            tag.name = tag_name
            tag.untagOnResolve = False
            tag.visibleForGroup = self._get_all_users_group().name
            tag.updatableByGroup = None
            self._target.createTag(tag)
            self._tags[tag_name] = tag
        return self._tags[tag_name]

    def _get_all_users_group(self):
        if not self._all_users_group:
            self._all_users_group = next(
                (g for g in self._target.getGroups()
                 if g.name in ['All users', 'Все пользователи']),
                {'name': None})
        return self._all_users_group

    def _add_attachments(self, issue):
        if not hasattr(issue, 'attachments'):
            return
        max_attempts = 5
        for attach in issue.attachments:
            attach.author.login = self._create_user(attach.author).login
            if not attach.author.login:
                attach.author.login = '******'
            attempts = max_attempts
            while attempts:
                attempts -= 1
                try:
                    self._target.createAttachmentFromAttachment(
                        self._get_yt_issue_id(issue),
                        RedmineAttachment(attach, self._source))
                    break
                except Exception as e:
                    print(e)
                    if attempts:
                        delay = 10 + (max_attempts - attempts - 1) * 10
                        print(("Can't import attachment: %s. Retry in %d s." %
                               (attach.filename, delay)))
                        time.sleep(delay)
                    else:
                        print(('Failed to import attachment: %s. Skipped.' %
                               attach.filename))

    def _collect_relations(self, issue):
        link_types = {
            'duplicates': 'duplicate',
            'relates': 'relates',
            'blocks': 'depend',
            'precedes': 'depend'
        }
        if hasattr(issue, 'relations'):
            for rel in issue.relations:
                if rel.relation_type not in link_types:
                    print(('Unsuitable link type: %s. Skipped' %
                           rel.relation_type))
                    continue
                from_id = rel.issue_id
                to_id = rel.issue_to_id
                if rel.relation_type == 'duplicates':
                    from_id, to_id = to_id, from_id
                self._push_relation(from_id, to_id,
                                    link_types[rel.relation_type])
        if hasattr(issue, 'children'):
            for child in issue.children:
                self._push_relation(issue.id, child.id, 'subtask')

    def _push_relation(self, from_iid, to_iid, relation_type):
        from_iid = int(from_iid)
        to_iid = int(to_iid)
        if relation_type not in self._relations:
            self._relations[relation_type] = {}
        relations = self._relations[relation_type]
        if from_iid not in relations:
            relations[from_iid] = {}
        relations[from_iid][to_iid] = None

    def _apply_relations(self, limit=DEFAULT_BATCH_SIZE):
        links = []
        for link_type, ids in list(self._relations.items()):
            for from_id, to_ids in list(ids.items()):
                for to_id in to_ids:
                    link = youtrack.Link()
                    link.typeName = link_type
                    try:
                        link.source = self._to_yt_issue_id(from_id)
                        link.target = self._to_yt_issue_id(to_id)
                    except KeyError:
                        print(("Cannot apply link (%s) to issues: %d and %d" %
                               (link_type, from_id, to_id)))
                        if hasattr(link, 'source') and link.source:
                            print("The second issue was not imported")
                            continue
                        else:
                            print("The first issues was not imported")
                            break
                    links.append(link)
                    if len(links) >= limit:
                        self._target.importLinks(links)
                        del links[0:]
        if links:
            self._target.importLinks(links)
def fb2youtrack(target_url, target_login, target_password, source_url, source_login, source_password, project_names, max_issue_id) :
    #encoding = 'utf-8'
    source = FBClient(source_url, source_login, source_password)
    target = Connection(target_url, target_login, target_password)
    accessible_projects = source.list_project_names()
    for p_name in project_names :
        if not(p_name in  accessible_projects.keys()) :
            print 'Unknown project names. Exiting...'
            sys.exit()

#    for p_name in accessible_projects :
#        if (p_name.encode('utf-8') in project_names_str) :
#            project_names_str.remove(p_name.encode('utf-8'))
#            project_names.append(p_name)
#
#    if (len(project_names_str) != 0) :
#        print 'Unknown project names!'

    print('Creating custom fields')
#
#    for field_name in ['Category', 'Priority', 'Status']:
#        field_name = get_yt_name_from_fb__field_name(field_name)
#        create_custom_field(target, fbugz.CF_TYPES[field_name], field_name, False)

    fb_category_bundle_name = u'FB Categories'
    fb_priorities_bundle_name = u'FB Priorities'
    fb_statuses_bundle_name = u'FB Statuses'

    common_fields = {
        u'category'  :   fb_category_bundle_name,
        u'priority'  :   fb_priorities_bundle_name,
        u'status'    :   fb_statuses_bundle_name
    }

    field_name = u'category'
    create_bundle_with_values(target,fbugz.CF_TYPES[get_yt_name_from_fb_field_name(field_name)],
                              common_fields[field_name],
                              source.list_categories(),
                              lambda bundle, value : bundle.createElement(to_yt_field_value(field_name, value)))
    field_name = u'priority'
    create_bundle_with_values(target, fbugz.CF_TYPES[get_yt_name_from_fb_field_name(field_name)],
                              common_fields[field_name],
                              [elem[0] + '-' + elem[1] for elem in source.list_priorities()],
                              lambda bundle, value : bundle.createElement(to_yt_field_value(field_name, value)))

    field_name = u'status'
    statuses = [(to_yt_field_value(field_name, value), resolved) for (value, resolved) in source.list_statuses()]
    create_bundle_with_values(target, fbugz.CF_TYPES[get_yt_name_from_fb_field_name(field_name)],
                              common_fields[field_name],
                              statuses, lambda bundle, value : to_yt_status(bundle, value))

    simple_fields = [u'original_title', u'version', u'computer', u'due', u'estimate']

    for name in simple_fields:
        name = get_yt_name_from_fb_field_name(name)
        create_custom_field(target, fbugz.CF_TYPES[name], name, False)

    print 'Importing users'
    for name in ['Normal', 'Deleted', 'Community', 'Virtual'] :
        group = Group()
        group.name = name
        try :
            target.createGroup(group)
            print 'Group with name [ %s ] successfully created' % name
        except:
            print "Can't create group with name [ %s ] (maybe because it already exists)" % name


    users_to_import = []
    max = 100
    for user in source.get_users() :
        yt_user = _to_yt_user(user)
        print 'Importing user [ %s ]' % yt_user.login
        users_to_import.append(yt_user)
        if len(users_to_import) >= max:
            _do_import_users(target, users_to_import)
            users_to_import = []
    _do_import_users(target, users_to_import)
    print 'Importing users finished'

    # to handle linked issues
    try :
        target.createIssueLinkTypeDetailed('parent-child', 'child of', 'parent of', True)
    except YouTrackException:
        print "Can't create issue link type [ parent-child ] (maybe because it already exists)"
    links_to_import = []

    for project_name in project_names :
        value_sets = dict([])

        project_id = accessible_projects[project_name]
        print 'Importing project [ %s ]' % project_name
        target.createProjectDetailed(project_id, project_name.encode('utf-8'), 'no description', 'root')

        print 'Creating custom fields in project [ %s ]' % project_name

        for cf_name in common_fields:
            bundle_name = common_fields[cf_name]
            cf_name = get_yt_name_from_fb_field_name(cf_name)
            target.deleteProjectCustomField(project_id, cf_name)
            target.createProjectCustomFieldDetailed(project_id, cf_name, 'No ' + cf_name.lower(),
                    {'bundle' : bundle_name})

        for cf_name in simple_fields:
            cf_name = get_yt_name_from_fb_field_name(cf_name)
            try:
                target.createProjectCustomFieldDetailed(project_id, cf_name, 'No ' + cf_name.lower())
            except YouTrackException:
                print "Can't create custom field with name [%s]" % cf_name
        cf_name = get_yt_name_from_fb_field_name('fix_for')
        milestones = source.get_milestones(project_id)
        value_sets["fix_for"] = []
        for milestone in milestones:
            value_sets["fix_for"].append(milestone.name)
            milestone.name = to_yt_field_value('fix_for', milestone.name)
        add_values_to_field(target, cf_name, project_id, milestones,
                            lambda bundle, value: _to_yt_version(bundle, value))

        cf_name = get_yt_name_from_fb_field_name('area')
        areas = source.get_areas(project_id)
        value_sets["area"] = []
        for area in areas:
            value_sets["area"].append(area.name)
            area.name = to_yt_field_value('area', area.name)
        add_values_to_field(target, cf_name, project_id, areas,
                            lambda bundle, value: _to_yt_subsystem(bundle, value))

        print 'Importing issues for project [ %s ]' % project_name
        start = 0
        issues_to_import = []
        # create dictionary with child : parent pairs
        while start <= max_issue_id:
            fb_issues = source.get_issues(project_name, start, 30)
            for issue in fb_issues :
                add_values_to_field(target, get_yt_name_from_fb_field_name('area'), project_id,
                    [issue.field_values['area']], lambda bundle, value: bundle.createElement(value))
                issues_to_import.append(_to_yt_issue(issue, value_sets))
            target.importIssues(project_id, project_name.encode('utf-8') + " assignees", issues_to_import)
            for issue in fb_issues :
                full_issue_id = '%s-%s' % (project_id, issue.ix_bug)
                for attach in issue.attachments :
                    target.createAttachmentFromAttachment(full_issue_id, attach)
                for tag in issue.tags :
                    target.executeCommand(full_issue_id, 'tag ' + tag)
                if issue.bug_parent is not None:
                    parent_issue_id = '%s-%s' % (source.get_issue_project_id(issue.bug_parent), issue.bug_parent)
                    link = Link()
                    link.typeName = 'parent-child'
                    link.source = full_issue_id
                    link.target = parent_issue_id
                    links_to_import.append(link)
            issues_to_import = []
            start += 30
        print 'Importing issues for project [ %s ] finished' % project_name

    print 'Importing issue links'
    print target.importLinks(links_to_import)
    print 'Importing issue links finished'
Exemplo n.º 19
0
def fb2youtrack(params):
    # Connection to FogBugz
    source = FBClient(params['fb_url'],
                      params['fb_login'],
                      params['fb_password'])

    # Connecting to YouTrack
    token = params.get('token')
    if not token and 'token_file' in params:
        try:
            with open(params['token_file'], 'r') as f:
                token = f.read().strip()
        except (OSError, IOError) as e:
            print("Cannot load token from file: " + str(e))
            sys.exit(1)
    if token:
        target = Connection(params['yt_url'], token=token)
    elif 'yt_login' in params:
        target = Connection(params['yt_url'],
                            params.get('yt_login'),
                            params.get('yt_password'))
    else:
        print("You have to provide token or login/password to import data")
        sys.exit(1)

    if not params.get('project_lead_login'):
        project_lead = params.get('yt_login')
        if not project_lead:
            for login in ('root', 'admin', 'administrator', 'guest'):
                try:
                    project_lead = target.getUser(login).login
                    break
                except youtrack.YouTrackException:
                    continue
        params['project_lead_login'] = project_lead

    max_issue_id = params['fb_max_issue_id']

    project_names = youtrackutils.fbugz.PROJECTS_TO_IMPORT
    accessible_projects = source.list_project_names()
    for p_name in project_names:
        if not (p_name in accessible_projects.keys()):
            print('Unknown project names. Exiting...')
            sys.exit()

#    for p_name in accessible_projects :
#        if (p_name.encode('utf-8') in project_names_str) :
#            project_names_str.remove(p_name.encode('utf-8'))
#            project_names.append(p_name)
#
#    if (len(project_names_str) != 0) :
#        print 'Unknown project names!'

    print('Creating custom fields')
#
#    for field_name in ['Category', 'Priority', 'Status']:
#        field_name = get_yt_name_from_fb__field_name(field_name)
#        create_custom_field(target, fbugz.CF_TYPES[field_name], field_name, False)

    fb_category_bundle_name = u'FB Categories'
    fb_priorities_bundle_name = u'FB Priorities'
    fb_statuses_bundle_name = u'FB Statuses'

    common_fields = {
        u'category'  :   fb_category_bundle_name,
        u'priority'  :   fb_priorities_bundle_name,
        u'status'    :   fb_statuses_bundle_name
    }

    field_name = u'category'
    create_bundle_with_values(
        target,
        youtrackutils.fbugz.CF_TYPES[get_yt_field_name(field_name)],
        common_fields[field_name],
        source.list_categories(),
        lambda bundle, value:
        bundle.createElement(to_yt_field_value(field_name, value)))
    field_name = u'priority'
    create_bundle_with_values(target, youtrackutils.fbugz.CF_TYPES[get_yt_field_name(field_name)],
                              common_fields[field_name],
                              [elem[0] + '-' + elem[1] for elem in source.list_priorities()],
                              lambda bundle, value : bundle.createElement(to_yt_field_value(field_name, value)))

    field_name = u'status'
    statuses = [(to_yt_field_value(field_name, value), resolved) for (value, resolved) in source.list_statuses()]
    create_bundle_with_values(target, youtrackutils.fbugz.CF_TYPES[get_yt_field_name(field_name)],
                              common_fields[field_name],
                              statuses, lambda bundle, value : to_yt_status(bundle, value))

    simple_fields = [u'original_title', u'version', u'computer', u'due', u'estimate']

    for name in simple_fields:
        name = get_yt_field_name(name)
        create_custom_field(target, youtrackutils.fbugz.CF_TYPES[name], name, False)

    print('Importing users')
    for name in ['Normal', 'Deleted', 'Community', 'Virtual'] :
        group = Group()
        group.name = name
        try :
            target.createGroup(group)
            print('Group with name [ %s ] successfully created' % name)
        except:
            print("Can't create group with name [ %s ] (maybe because it already exists)" % name)

    users_to_import = []
    max = 100
    for user in source.get_users() :
        yt_user = _to_yt_user(user)
        print('Importing user [ %s ]' % yt_user.login)
        users_to_import.append(yt_user)
        if len(users_to_import) >= max:
            _do_import_users(target, users_to_import)
            users_to_import = []
    _do_import_users(target, users_to_import)
    print('Importing users finished')

    # to handle linked issues
    try :
        target.createIssueLinkTypeDetailed('parent-child', 'child of', 'parent of', True)
    except YouTrackException:
        print("Can't create issue link type [ parent-child ] (maybe because it already exists)")
    links_to_import = []

    for project_name in project_names:
        value_sets = dict([])

        project_id = accessible_projects[project_name]
        print('Importing project [ %s ]' % project_name)
        target.createProjectDetailed(project_id,
                                     project_name.encode('utf-8'),
                                     '',
                                     params['project_lead_login'])

        print('Creating custom fields in project [ %s ]' % project_name)

        for cf_name in common_fields:
            bundle_name = common_fields[cf_name]
            cf_name = get_yt_field_name(cf_name)
            target.deleteProjectCustomField(project_id, cf_name)
            target.createProjectCustomFieldDetailed(project_id, cf_name, 'No ' + cf_name.lower(),
                    {'bundle' : bundle_name})

        for cf_name in simple_fields:
            cf_name = get_yt_field_name(cf_name)
            try:
                target.createProjectCustomFieldDetailed(project_id, cf_name, 'No ' + cf_name.lower())
            except YouTrackException:
                print("Can't create custom field with name [%s]" % cf_name)
        cf_name = get_yt_field_name('fix_for')
        milestones = source.get_milestones(project_id)
        value_sets["fix_for"] = []
        for milestone in milestones:
            value_sets["fix_for"].append(milestone.name)
            milestone.name = to_yt_field_value('fix_for', milestone.name)
        add_values_to_field(target, cf_name, project_id, milestones,
                            lambda bundle, value: _to_yt_version(bundle, value))

        cf_name = get_yt_field_name('area')
        areas = source.get_areas(project_id)
        value_sets["area"] = []
        for area in areas:
            value_sets["area"].append(area.name)
            area.name = to_yt_field_value('area', area.name)
        add_values_to_field(target, cf_name, project_id, areas,
                            lambda bundle, value: _to_yt_subsystem(bundle, value))

        print('Importing issues for project [ %s ]' % project_name)
        start = 0
        issues_to_import = []
        # create dictionary with child : parent pairs
        while start <= max_issue_id:
            fb_issues = source.get_issues(project_name, start, 30)
            for issue in fb_issues :
                add_values_to_field(target, get_yt_field_name('area'), project_id,
                                    [issue.field_values['area']], lambda bundle, value: bundle.createElement(value))
                issues_to_import.append(_to_yt_issue(issue, value_sets))
            target.importIssues(project_id, project_name.encode('utf-8') + " assignees", issues_to_import)
            for issue in fb_issues :
                full_issue_id = '%s-%s' % (project_id, issue.ix_bug)
                for attach in issue.attachments :
                    target.createAttachmentFromAttachment(full_issue_id, attach)
                for tag in issue.tags :
                    target.executeCommand(full_issue_id, 'tag ' + tag)
                if issue.bug_parent is not None:
                    parent_issue_id = '%s-%s' % (source.get_issue_project_id(issue.bug_parent), issue.bug_parent)
                    link = Link()
                    link.typeName = 'parent-child'
                    link.source = full_issue_id
                    link.target = parent_issue_id
                    links_to_import.append(link)
            issues_to_import = []
            start += 30
        print('Importing issues for project [ %s ] finished' % project_name)

    print('Importing issue links')
    print(target.importLinks(links_to_import))
    print('Importing issue links finished')
                        continue

                    if params.get('add_new_comments'):
                        target_comments = dict()
                        max_id = 0
                        for c in target_issue.getComments():
                            target_comments[c.created] = c
                            if max_id < c.created:
                                max_id = c.created
                        for c in issue.getComments():
                            if c.created > max_id or c.created not in target_comments:
                                group = None
                                if hasattr(c, 'permittedGroup'):
                                    group = c.permittedGroup
                                try:
                                    target.executeCommand(issue.id, 'comment', c.text, group, c.author)
                                except youtrack.YouTrackException, e:
                                    print 'Cannot add comment to issue '
                                    print e

                    if params.get('sync_custom_fields'):
                        skip_fields = []
                        if tt_settings and tt_settings.Enabled and tt_settings.TimeSpentField:
                            skip_fields.append(tt_settings.TimeSpentField)
                        skip_fields = [name.lower() for name in skip_fields]
                        for pcf in [pcf for pcf in project_custom_fields if pcf.name.lower() not in skip_fields]:
                            target_cf_value = None
                            if pcf.name in target_issue:
                                target_cf_value = target_issue[pcf.name]
                                if isinstance(target_cf_value, (list, tuple)):
                                    target_cf_value = set(target_cf_value)
Exemplo n.º 21
0
def fb2youtrack(target_url, target_login, target_password, source_url,
                source_login, source_password, project_names, max_issue_id):
    #encoding = 'utf-8'
    source = FBClient(source_url, source_login, source_password)
    target = Connection(target_url, target_login, target_password)
    accessible_projects = source.list_project_names()
    for p_name in project_names:
        if not (p_name in accessible_projects.keys()):
            print 'Unknown project names. Exiting...'
            sys.exit()


#    for p_name in accessible_projects :
#        if (p_name.encode('utf-8') in project_names_str) :
#            project_names_str.remove(p_name.encode('utf-8'))
#            project_names.append(p_name)
#
#    if (len(project_names_str) != 0) :
#        print 'Unknown project names!'

    print('Creating custom fields')
    #
    #    for field_name in ['Category', 'Priority', 'Status']:
    #        field_name = get_yt_name_from_fb__field_name(field_name)
    #        create_custom_field(target, fbugz.CF_TYPES[field_name], field_name, False)

    fb_category_bundle_name = u'FB Categories'
    fb_priorities_bundle_name = u'FB Priorities'
    fb_statuses_bundle_name = u'FB Statuses'

    common_fields = {
        u'category': fb_category_bundle_name,
        u'priority': fb_priorities_bundle_name,
        u'status': fb_statuses_bundle_name
    }

    field_name = u'category'
    create_bundle_with_values(
        target, fbugz.CF_TYPES[get_yt_name_from_fb_field_name(field_name)],
        common_fields[field_name], source.list_categories(),
        lambda bundle, value: bundle.createElement(
            to_yt_field_value(field_name, value)))
    field_name = u'priority'
    create_bundle_with_values(
        target, fbugz.CF_TYPES[get_yt_name_from_fb_field_name(field_name)],
        common_fields[field_name],
        [elem[0] + '-' + elem[1] for elem in source.list_priorities()],
        lambda bundle, value: bundle.createElement(
            to_yt_field_value(field_name, value)))

    field_name = u'status'
    statuses = [(to_yt_field_value(field_name, value), resolved)
                for (value, resolved) in source.list_statuses()]
    create_bundle_with_values(
        target, fbugz.CF_TYPES[get_yt_name_from_fb_field_name(field_name)],
        common_fields[field_name], statuses,
        lambda bundle, value: to_yt_status(bundle, value))

    simple_fields = [
        u'original_title', u'version', u'computer', u'due', u'estimate'
    ]

    for name in simple_fields:
        name = get_yt_name_from_fb_field_name(name)
        create_custom_field(target, fbugz.CF_TYPES[name], name, False)

    print 'Importing users'
    for name in ['Normal', 'Deleted', 'Community', 'Virtual']:
        group = Group()
        group.name = name
        try:
            target.createGroup(group)
            print 'Group with name [ %s ] successfully created' % name
        except:
            print "Can't create group with name [ %s ] (maybe because it already exists)" % name

    users_to_import = []
    max = 100
    for user in source.get_users():
        yt_user = _to_yt_user(user)
        print 'Importing user [ %s ]' % yt_user.login
        users_to_import.append(yt_user)
        if len(users_to_import) >= max:
            _do_import_users(target, users_to_import)
            users_to_import = []
    _do_import_users(target, users_to_import)
    print 'Importing users finished'

    # to handle linked issues
    try:
        target.createIssueLinkTypeDetailed('parent-child', 'child of',
                                           'parent of', True)
    except YouTrackException:
        print "Can't create issue link type [ parent-child ] (maybe because it already exists)"
    links_to_import = []

    for project_name in project_names:
        value_sets = dict([])

        project_id = accessible_projects[project_name]
        print 'Importing project [ %s ]' % project_name
        target.createProjectDetailed(project_id, project_name.encode('utf-8'),
                                     'no description', 'root')

        print 'Creating custom fields in project [ %s ]' % project_name

        for cf_name in common_fields:
            bundle_name = common_fields[cf_name]
            cf_name = get_yt_name_from_fb_field_name(cf_name)
            target.deleteProjectCustomField(project_id, cf_name)
            target.createProjectCustomFieldDetailed(project_id, cf_name,
                                                    'No ' + cf_name.lower(),
                                                    {'bundle': bundle_name})

        for cf_name in simple_fields:
            cf_name = get_yt_name_from_fb_field_name(cf_name)
            try:
                target.createProjectCustomFieldDetailed(
                    project_id, cf_name, 'No ' + cf_name.lower())
            except YouTrackException:
                print "Can't create custom field with name [%s]" % cf_name
        cf_name = get_yt_name_from_fb_field_name('fix_for')
        milestones = source.get_milestones(project_id)
        value_sets["fix_for"] = []
        for milestone in milestones:
            value_sets["fix_for"].append(milestone.name)
            milestone.name = to_yt_field_value('fix_for', milestone.name)
        add_values_to_field(
            target, cf_name, project_id, milestones,
            lambda bundle, value: _to_yt_version(bundle, value))

        cf_name = get_yt_name_from_fb_field_name('area')
        areas = source.get_areas(project_id)
        value_sets["area"] = []
        for area in areas:
            value_sets["area"].append(area.name)
            area.name = to_yt_field_value('area', area.name)
        add_values_to_field(
            target, cf_name, project_id, areas,
            lambda bundle, value: _to_yt_subsystem(bundle, value))

        print 'Importing issues for project [ %s ]' % project_name
        start = 0
        issues_to_import = []
        # create dictionary with child : parent pairs
        while start <= max_issue_id:
            fb_issues = source.get_issues(project_name, start, 30)
            for issue in fb_issues:
                add_values_to_field(
                    target, get_yt_name_from_fb_field_name('area'), project_id,
                    [issue.field_values['area']],
                    lambda bundle, value: bundle.createElement(value))
                issues_to_import.append(_to_yt_issue(issue, value_sets))
            target.importIssues(project_id,
                                project_name.encode('utf-8') + " assignees",
                                issues_to_import)
            for issue in fb_issues:
                full_issue_id = '%s-%s' % (project_id, issue.ix_bug)
                for attach in issue.attachments:
                    target.createAttachmentFromAttachment(
                        full_issue_id, attach)
                for tag in issue.tags:
                    target.executeCommand(full_issue_id, 'tag ' + tag)
                if issue.bug_parent is not None:
                    parent_issue_id = '%s-%s' % (source.get_issue_project_id(
                        issue.bug_parent), issue.bug_parent)
                    link = Link()
                    link.typeName = 'parent-child'
                    link.source = full_issue_id
                    link.target = parent_issue_id
                    links_to_import.append(link)
            issues_to_import = []
            start += 30
        print 'Importing issues for project [ %s ] finished' % project_name

    print 'Importing issue links'
    print target.importLinks(links_to_import)
    print 'Importing issue links finished'
def trac2youtrack(target_url, target_login, target_password, project_ID, project_name, env_path):
    # creating connection to trac to import issues to
    client = Client(env_path)
    # creating connection to youtrack to import issues in
    target = Connection(target_url, target_login, target_password)

    #create project
    print "Creating project[%s]" % project_name
    try :
        target.getProject(project_ID)
    except youtrack.YouTrackException:
        target.createProjectDetailed(project_ID, project_name, client.get_project_description(), target_login)

    #importing users
    trac_users = client.get_users()
    print "Importing users"
    yt_users = list([])
    # converting trac users to yt users
    registered_users = set([])
    for user in trac_users :
        print "Processing user [ %s ]" % user.name
        registered_users.add(user.name)
        yt_users.append(to_youtrack_user(user))
        # adding users to yt project
    target.importUsers(yt_users)
    print "Importing users finished"

    print "Creating project custom fields"

    create_yt_custom_field(target, project_ID, "Priority", client.get_issue_priorities())

    create_yt_custom_field(target, project_ID, "Type", client.get_issue_types())

    trac_resolution_to_yt_state = lambda track_field, yt_bundle : to_youtrack_state(track_field, yt_bundle)
    create_yt_bundle_custom_field(target, project_ID, "Resolution", client.get_issue_resolutions(), trac_resolution_to_yt_state)

    trac_versions = client.get_versions()
    trac_version_to_yt_version = lambda trac_field, yt_bundle : to_youtrack_version(trac_field, yt_bundle)
    create_yt_bundle_custom_field(target, project_ID, "Version", trac_versions, trac_version_to_yt_version)
    #create_yt_bundle_custom_field(target, project_ID, "Affected versions", trac_versions, trac_version_to_yt_version)

    trac_components = client.get_components()
    for cmp in trac_components :
        if cmp.owner not in registered_users :
            cmp.owner, registered_users = process_non_authorised_user(target, registered_users, cmp.owner)
    trac_component_to_yt_subsystem = lambda trac_field, yt_bundle : to_youtrack_subsystem(trac_field, yt_bundle)
    create_yt_bundle_custom_field(target, project_ID, "Component", trac_components, trac_component_to_yt_subsystem)

    create_yt_custom_field(target, project_ID, "Severity", client.get_severities())

    trac_custom_fields = client.get_custom_fields_declared()
    check_box_fields = dict([])
    for elem in trac_custom_fields:
        print "Processing custom field [ %s ]" % elem.name
        type_name = None
        if elem.type == "checkbox":
            if len(elem.label) > 0:
                opt = elem.label
            else:
                opt = elem.name
            options = list([opt])
            check_box_fields[elem.name] = opt
        else:
            options = elem.options

        values = None
        if len(options):
            values = options

        field_name = elem.name
        if field_name in tracLib.FIELD_NAMES.keys() :
            field_name = tracLib.FIELD_NAMES[field_name]

        field_type = tracLib.CUSTOM_FIELD_TYPES[elem.type]
        if field_name in tracLib.FIELD_TYPES.keys():
            field_type = tracLib.FIELD_TYPES[field_name]

        process_custom_field(target, project_ID, field_type, field_name, trac_values_to_youtrack_values(field_name, values))
        print "Creating project custom fields finished"

    print "Importing issues"
    trac_issues = client.get_issues()
    yt_issues = list([])
    counter = 0
    max = 100
    for issue in trac_issues:
        print "Processing issue [ %s ]" % (str(issue.id))
        counter += 1
        if not (issue.reporter in registered_users):
            yt_user, registered_users = process_non_authorised_user(target, registered_users, issue.reporter)
            if yt_user is None :
                issue.reporter = "guest"
            else:
                issue.reporter = yt_user
        if not (issue.owner in registered_users):
            yt_user, registered_users = process_non_authorised_user(target, registered_users, issue.owner)
            if yt_user is None :
                issue.owner = ""
            else:
                issue.owner = yt_user
        legal_cc = set([])
        for cc in issue.cc:
            if cc in registered_users:
                legal_cc.add(cc)
        issue.cc = legal_cc

        yt_issues.append(to_youtrack_issue(issue, check_box_fields))
        if counter == max:
            counter = 0
            print target.importIssues(project_ID, project_name + ' Assignees', yt_issues)
            yt_issues = list([])
    print target.importIssues(project_ID, project_name + ' Assignees', yt_issues)
    print 'Importing issues finished'
    #importing tags
    print "Importing keywords"
    for issue in trac_issues:
        print "Importing tags from issue [ %s ]" % (str(issue.id))
        tags = issue.keywords
        for t in tags:
            target.executeCommand(str(project_ID) + "-" + str(issue.id), "tag " + t.encode('utf-8'))
    print "Importing keywords finished"

    print "Importing attachments"
    for issue in trac_issues:
        print "Processing issue [ %s ]" % (str(issue.id))
        issue_attach = issue.attachment
        for attach in issue_attach:
            print "Processing attachment [ %s ]" % attach.filename.encode('utf-8')
            if not (attach.author_name in registered_users):
                yt_user, registered_users = process_non_authorised_user(target, registered_users, attach.author_name)
                if yt_user is None :
                    attach.author_name = "guest"
                else:
                    attach.author_name = yt_user
            content = open(urllib.quote(attach.filename.encode('utf-8')))
            target.createAttachment(str(project_ID) + "-" + str(issue.id), attach.name, content, attach.author_name,
                                    created=attach.time)
    print "Importing attachments finished"

    print "Importing workitems"
    tt_enabled = False
    for issue in trac_issues:
        if issue.workitems:
            if not tt_enabled:
                tt_settings = target.getProjectTimeTrackingSettings(str(project_ID))
                if not tt_settings.Enabled:
                    print "Enabling TimeTracking for the prject"
                    target.setProjectTimeTrackingSettings(str(project_ID), enabled=True)
                tt_enabled = True
            print "Processing issue [ %s ]" % (str(issue.id))
            workitems = [to_youtrack_workitem(w) for w in issue.workitems]
            target.importWorkItems(str(project_ID) + "-" + str(issue.id), workitems)
    print "Importing workitems finished"
                link_importer.addAvailableIssues(issues)

                for issue in issues:
                    try:
                        target_issue = target.getIssue(issue.id)
                    except youtrack.YouTrackException, e:
                        print "Cannot get target issue"
                        print e
                        continue

                    if params.get('sync_tags') and issue.tags:
                        try:
                            for tag in issue.tags:
                                tag = re.sub(r'[,&<>]', '_', tag)
                                try:
                                    target.executeCommand(issue.id, 'tag ' + tag, disable_notifications=True)
                                except youtrack.YouTrackException:
                                    tag = re.sub(r'[\s-]', '_', tag)
                                    target.executeCommand(issue.id, 'tag ' + tag, disable_notifications=True)
                        except youtrack.YouTrackException, e:
                            print "Cannot sync tags for issue " + issue.id
                            print e

                    if params.get('add_new_comments'):
                        target_comments = dict()
                        max_id = 0
                        for c in target_issue.getComments():
                            target_comments[c.created] = c
                            if max_id < c.created:
                                max_id = c.created
                        for c in issue.getComments():
def bugzilla2youtrack(target_url, target_login, target_pass, bz_db, bz_host, bz_port, bz_login, bz_pass,
                      bz_product_names, issues_filter):
    # connecting to bz
    client = Client(bz_host, int(bz_port), bz_login, bz_pass, db_name=bz_db)

    if not len(bz_product_names):
        answer = raw_input("All projects will be imported. Are you sure? [y/n]")
        if answer.capitalize() != "Y":
            sys.exit()
        bz_product_names = client.get_product_names()

    print "bz_product_names :   " + repr(bz_product_names)

    # connecting to yt
    target = Connection(target_url, target_login, target_pass)

    print "Creating issue link types"
    link_types = client.get_issue_link_types()
    for link in link_types:
        print "Processing link type [ %s ]" % link.name
        try:
            target.createIssueLinkType(to_yt_issue_link_type(link))
        except YouTrackException:
            print "Can't create link type [ %s ] (maybe because it already exists)" % link.name
    print "Creating issue link types finished"

    print "Creating custom fields"
    custom_fields = client.get_custom_fields()
    for cf in custom_fields:
        create_yt_custom_field(cf, target)
    print "Creating custom fields finished"

    for key in bugzilla.FIELD_TYPES:
        if key not in youtrack.EXISTING_FIELDS:
            create_custom_field(target, bugzilla.FIELD_TYPES[key], key, True, bundle_policy="1")

    bz_product_ids = []

    for name in bz_product_names:
        product_id = str(client.get_product_id_by_name(name))
        bz_product_ids.append(product_id)
        print "Creating project [ %s ] with name [ %s ]" % (product_id, name)
        try:
            target.getProject(str(product_id))
        except YouTrackException:
            target.createProjectDetailed(str(product_id), name, client.get_project_description(product_id),
                target_login)

        print "Importing components for project [ %s ]" % product_id
        process_components(client.get_components(product_id), product_id, target)
        print "Importing components finished for project [ %s ]" % product_id

        print "Importing versions for project [ %s ]" % product_id
        process_versions(client.get_versions(product_id), product_id, target)
        print "Importing versions finished for project [ %s ] finished" % product_id

        print "Importing issues to project [ %s ]" % product_id
        max_count = 100
        count = 0
        from_id = 0
        bz_issues_count = client.get_issues_count(product_id)
        while count < bz_issues_count:
            batch = client.get_issues(product_id, from_id, from_id + max_count)
            batch = [bz_issue for bz_issue in batch if (issues_filter(bz_issue))]
            count += len(batch)
            from_id += max_count
            target.importIssues(product_id, product_id + " assignees",
                [to_yt_issue(bz_issue, product_id, target) for bz_issue in batch])
            # todo convert to good tags import
            for issue in batch:
                tags = issue["keywords"] | issue["flags"]
                for t in tags:
                    print "Processing tag [ %s ]" % t.encode('utf8')
                    target.executeCommand(str(product_id) + "-" + str(issue[get_number_in_project_field_name()]),
                        "tag " + t.encode('utf8'))
            for issue in batch:
                for attach in issue["attachments"]:
                    print "Processing attachment [ %s ]" % (attach.name.encode('utf8'))
                    content = StringIO(attach.content)
                    target.createAttachment(str(product_id) + "-" + str(issue[get_number_in_project_field_name()]),
                        attach.name, content, attach.reporter
                        , created=str(int(attach.created) * 1000))
        print "Importing issues to project [ %s ] finished" % product_id

    # todo add pagination to links
    print "Importing issue links"
    cf_links = client.get_issue_links()
    duplicate_links = client.get_duplicate_links()
    if len(duplicate_links):
        try:
            target.createIssueLinkTypeDetailed("Duplicate", "duplicates", "is duplicated by", True)
        except YouTrackException:
            print "Can't create link type [ Duplicate ] (maybe because it already exists)"
    depend_links = client.get_dependencies_link()
    if len(depend_links):
        try:
            target.createIssueLinkTypeDetailed("Depend", "depends on", "is required for", True)
        except YouTrackException:
            print "Can't create link type [ Depend ] (maybe because it already exists)"
    links = cf_links | duplicate_links | depend_links

    links_to_import = list([])
    for link in links:
        print "Processing link %s for issue%s" % (link.name, link.source)
        if (str(link.target_product_id) in bz_product_ids) and (str(link.source_product_id) in bz_product_ids):
            links_to_import.append(to_yt_issue_link(link))
    print target.importLinks(links_to_import)
    print "Importing issue links finished"
Exemplo n.º 25
0
def Main(sendTo, subject, yamlMessage):
    """
    Workflow Zabbix-YouTrack
    :param sendTo: URL to Youtrack (ex. https://youtrack.example.com)
    :param subject: subject from Zabbix Action
    :param yamlMessage: message from Zabbix Action
    :return:
    """

    # ----- Use below example yamlMessage to debug -----
    #     yamlMessage = """Name: 'Test Zabbix-YT workflow, ignore it'
    # Text: 'Agent ping (server:agent.ping()): DOWN (1) '
    # Hostname: 'server.exmpale.ru'
    # Status: "OK"
    # Severity: "High"
    # EventID: "96976"
    # TriggerID: "123456789012" """

    messages = yaml.load(yamlMessage)

    # ----- START Issue parameters -----
    # Correspondence between the YouTrackPriority and ZabbixSeverity
    # Critical >= High
    # Normal < High

    ytPriority = 'Normal'
    if messages['Severity'] == 'Disaster' or messages['Severity'] == 'High':
        ytPriority = 'Critical'

    ytName = "{} ZabbixTriggerID::{}".format(messages['Name'],
                                             messages['TriggerID'])
    # ----- END Issue parameters -----

    # ----- START Youtrack Issue description -----
    # Search link to other issue
    searchString = "Hostname: '{}'".format(messages['Hostname'])
    linkToHostIssue = "{youtrack}/issues/{projectname}?q={query}".format(
        youtrack=sendTo,
        projectname=YT_PROJECT_NAME,
        query=urllib.parse.quote(searchString, safe=''))

    issueDescription = """
{ytName}
-----
{yamlMessage}
-----
- [https://zabbix.example.com/zabbix.php?action=dashboard.view Zabbix Dashboard]
- Show [{linkToHostIssue} all issue for *this host*]
""".format(
        ytName=ytName,
        yamlMessage=yamlMessage,
        linkToHostIssue=linkToHostIssue,
    )

    # ----- END Youtrack Issue description -----

    # ----- START Youtrack current week -----

    # Create connect to Youtrack API
    connection = Connection(sendTo, YT_USER, YT_PASSWORD)

    # Get current week in YT format (Sprint planned)
    version = connection.getAllBundles('version')

    for fixVersion in version[0].values:
        if fixVersion['archived'] == False and fixVersion['released'] == False:
            fixVersionWeek = fixVersion['name']
            break
    # ----- END Youtrack current week -----

    # ----- START Youtrack get or create issue -----
    # Get issue if exist
    # Search for TriggerID
    createNewIssue = False

    logger.debug("Get issue with text '{}'".format(messages['TriggerID']))
    issue = connection.getIssues(
        YT_PROJECT_NAME, "ZabbixTriggerID::{}".format(messages['TriggerID']),
        0, 1)

    if len(issue) == 0:
        createNewIssue = True

    else:
        # if issue contains TriggerID in summary, then it's good issue
        # else create new issue, this is bad issue, not from Zabbix
        if "ZabbixTriggerID::{}".format(
                messages['TriggerID']) in issue[0]['summary']:
            issueId = issue[0]['id']
            issue = connection.getIssue(issueId)
        else:
            createNewIssue = True

    # Create new issue
    if createNewIssue:
        logger.debug("Create new issue because it is not exist")
        issue = connection.createIssue(
            YT_PROJECT_NAME,
            'Unassigned',
            ytName,
            issueDescription,
            priority=ytPriority,
            subsystem=YT_SUBSYSTEM,
            type=YT_TYPE,
        )
        time.sleep(3)

        # Parse ID for new issue
        result = re.search(r'(CM-\d*)', issue[0]['location'])
        issueId = result.group(0)
        issue = connection.getIssue(issueId)

    logger.debug("Issue have id={}".format(issueId))

    # Set issue service
    ExecAndLog(connection, issueId, "Service {}".format(YT_SERVICE))

    # Update priority
    ExecAndLog(connection, issueId, "Priority {}".format(ytPriority))

    # ----- END Youtrack get or create issue -----

    # ----- START PROBLEM block ------
    if messages['Status'] == "PROBLEM":

        # Issue exist and NOT Hold on, Unnassigned and Estimated time set
        if issue['State'] != 'Hold on':

            # Estimated time
            ExecAndLog(connection, issueId,
                       "Estimated time {}".format(YT_TIME))

            # Update fix version
            ExecAndLog(connection=connection,
                       issueId=issueId,
                       command="Sprint planned {}".format(fixVersionWeek))

        # Reopen if Fixed or Verified or Canceled
        if issue['State'] == 'Fixed' or issue['State'] == 'Verified' or issue[
                'State'] == 'Canceled':
            # Reopen Issue
            ExecAndLog(connection, issueId, "State reopen")

            # Assignee issue
            ExecAndLog(connection, issueId, command="Assignee Unassigned")

        # Update summary and description for issue
        logger.debug("Run command in {issueId}: {command}".format(
            issueId=issueId,
            command=
            """Update summary and description with connection.updateIssue method"""
        ))
        connection.updateIssue(issueId=issueId,
                               summary=ytName,
                               description=issueDescription)

        # Add comment
        logger.debug("Run command in {issueId}: {command}".format(
            issueId=issueId,
            command="""Now is PROBLEM {}""".format(messages['Text'])))
        connection.executeCommand(issueId=issueId,
                                  command="",
                                  comment=YT_COMMENT.format(
                                      status=messages['Status'],
                                      text=messages['Text']))

        # Send ID to Zabbix:
        logger.debug("ZABBIX-API: Send Youtrack ID to {}".format(
            messages['EventID']))
        Zbx.event.acknowledge(eventids=messages['EventID'],
                              message="Create Youtrack task")
        Zbx.event.acknowledge(
            eventids=messages['EventID'],
            message="https://youtrack.example.com/issue/{}".format(issueId))
    # ----- End PROBLEM block ------

    # ----- Start OK block -----
    if messages['Status'] == "OK":

        if issue['State'] == 'Hold on' or issue['State'] == 'Registered':
            # Cancel if not in work
            ExecAndLog(connection, issueId, command="State Cancel")

            # Assignee issue
            ExecAndLog(connection,
                       issueId,
                       command="Assignee {}".format(YT_ASSIGNEE))

        if issue['State'] == 'Fixed':
            # Verify if Fixed
            ExecAndLog(connection, issueId, command="State verify")

        logger.debug("Run command in {issueId}: {command}".format(
            issueId=issueId,
            command="""Now is OK {}""".format(messages['Text'])))
        connection.executeCommand(issueId=issueId,
                                  command="",
                                  comment=YT_COMMENT.format(
                                      status=messages['Status'],
                                      text=messages['Text']))
def youtrack2youtrack(source_url, source_login, source_password, target_url, target_login, target_password,
                      project_ids, query='', source_token=None, target_token=None, params=None):
    if not len(project_ids):
        print("You should sign at least one project to import")
        return
    if params is None:
        params = {}

    source = Connection(source_url, source_login, source_password) if (source_token is None) else Connection(source_url,
                                                                                                             token=source_token)
    target = Connection(target_url, target_login, target_password) if (target_token is None) else Connection(target_url,
                                                                                                             token=target_token)
    # , proxy_info = httplib2.ProxyInfo(socks.PROXY_TYPE_HTTP, 'localhost', 8888)

    print("Import issue link types")
    for ilt in source.getIssueLinkTypes():
        try:
            print(target.createIssueLinkType(ilt))
        except youtrack.YouTrackException as e:
            print(e.message)

    user_importer = UserImporter(source, target, caching_users=params.get('enable_user_caching', True))
    link_importer = LinkImporter(target)

    # create all projects with minimum info and project lead set
    created_projects = []
    for project_id in project_ids:
        created = create_project_stub(source, target, project_id, user_importer)
        created_projects.append(created)

    # save created project ids to create correct group roles afterwards
    user_importer.addCreatedProjects([project.id for project in created_projects])
    # import project leads with group they are included and roles assigned to these groups
    user_importer.importUsersRecursively([target.getUser(project.lead) for project in created_projects])
    # afterwards in a script any user import imply recursive import

    cf_names_to_import = set([])  # names of cf prototypes that should be imported
    for project_id in project_ids:
        cf_names_to_import.update([pcf.name.capitalize() for pcf in source.getProjectCustomFields(project_id)])

    target_cf_names = [pcf.name.capitalize() for pcf in target.getCustomFields()]

    period_cf_names = []

    for cf_name in cf_names_to_import:
        source_cf = source.getCustomField(cf_name)
        if source_cf.type.lower() == 'period':
            period_cf_names.append(source_cf.name.lower())

        print("Processing custom field '%s'" % utf8encode(cf_name))
        if cf_name in target_cf_names:
            target_cf = target.getCustomField(cf_name)
            if not (target_cf.type == source_cf.type):
                print("In your target and source YT instances you have field with name [ %s ]" % utf8encode(cf_name))
                print("They have different types. Source field type [ %s ]. Target field type [ %s ]" %
                      (source_cf.type, target_cf.type))
                print("exiting...")
                exit()
        else:
            if hasattr(source_cf, "defaultBundle"):
                create_bundle_from_bundle(source, target, source_cf.defaultBundle, source_cf.type, user_importer)
            target.createCustomField(source_cf)

    failed_commands = []

    for projectId in project_ids:
        source = Connection(source_url, source_login, source_password) if (source_token is None) else Connection(
            source_url, token=source_token)
        target = Connection(target_url, target_login, target_password) if (target_token is None) else Connection(
            target_url, token=target_token)
        # , proxy_info = httplib2.ProxyInfo(socks.PROXY_TYPE_HTTP, 'localhost', 8888)
        # reset connections to avoid disconnections
        user_importer.resetConnections(source, target)
        link_importer.resetConnections(target)

        # copy project, subsystems, versions
        project = source.getProject(projectId)

        link_importer.addAvailableIssuesFrom(projectId)
        project_custom_fields = source.getProjectCustomFields(projectId)
        # create bundles and additional values
        for pcf_ref in project_custom_fields:
            pcf = source.getProjectCustomField(projectId, pcf_ref.name)
            if hasattr(pcf, "bundle"):
                try:
                    create_bundle_from_bundle(source, target, pcf.bundle, source.getCustomField(pcf.name).type,
                                              user_importer)
                except youtrack.YouTrackException as e:
                    if e.response.status != 409:
                        raise e
                    else:
                        print(e)

        target_project_fields = [pcf.name.lower() for pcf in target.getProjectCustomFields(projectId)]
        for field in project_custom_fields:
            if field.name.lower() in target_project_fields:
                if hasattr(field, 'bundle'):
                    if field.bundle != target.getProjectCustomField(projectId, field.name).bundle:
                        target.deleteProjectCustomField(projectId, field.name)
                        create_project_custom_field(target, field, projectId)
            else:
                try:
                    create_project_custom_field(target, field, projectId)
                except youtrack.YouTrackException as e:
                    if e.response.status != 409:
                        raise e
                    else:
                        print(e)

        # copy issues
        start = 0
        max = 20

        sync_workitems = enable_time_tracking(source, target, projectId)
        tt_settings = target.getProjectTimeTrackingSettings(projectId)

        print("Import issues")
        last_created_issue_number = 0

        while True:
            try:
                print("Get issues from " + str(start) + " to " + str(start + max))
                issues = source.getIssues(projectId, query, start, max)

                if len(issues) <= 0:
                    break

                if convert_period_values and period_cf_names:
                    for issue in issues:
                        for pname in period_cf_names:
                            for fname in issue.__dict__:
                                if fname.lower() != pname:
                                    continue
                                issue[fname] = period_to_minutes(issue[fname])

                users = set([])

                for issue in issues:
                    print("Collect users for issue [%s]" % issue.id)

                    users.add(issue.getReporter())
                    if issue.hasAssignee():
                        if isinstance(issue.Assignee, (list, tuple)):
                            users.update(issue.getAssignee())
                        else:
                            users.add(issue.getAssignee())
                    # TODO: http://youtrack.jetbrains.net/issue/JT-6100
                    users.add(issue.getUpdater())
                    if issue.hasVoters():
                        users.update(issue.getVoters())
                    for comment in issue.getComments():
                        users.add(comment.getAuthor())

                    print("Collect links for issue [%s]" % issue.id)
                    link_importer.collectLinks(issue.getLinks(True))
                    # links.extend(issue.getLinks(True))

                    # fix problem with comment.text
                    for comment in issue.getComments():
                        if not hasattr(comment, "text") or (len(comment.text.strip()) == 0):
                            setattr(comment, 'text', 'no text')

                user_importer.importUsersRecursively(users)

                print("Create issues [" + str(len(issues)) + "]")
                if params.get('create_new_issues'):
                    create_issues(target, issues, last_created_issue_number)
                else:
                    print(target.importIssues(projectId, project.name + ' Assignees', issues))
                link_importer.addAvailableIssues(issues)

                for issue in issues:
                    try:
                        target_issue = target.getIssue(issue.id)
                    except youtrack.YouTrackException as e:
                        print("Cannot get target issue")
                        print(e)
                        continue

                    if params.get('sync_tags') and issue.tags:
                        try:
                            for tag in issue.tags:
                                tag = re.sub(r'[,&<>]', '_', tag)
                                try:
                                    target.executeCommand(issue.id, 'tag ' + tag, disable_notifications=True)
                                except youtrack.YouTrackException:
                                    tag = re.sub(r'[\s-]', '_', tag)
                                    target.executeCommand(issue.id, 'tag ' + tag, disable_notifications=True)
                        except youtrack.YouTrackException as e:
                            print("Cannot sync tags for issue " + issue.id)
                            print(e)

                    if params.get('add_new_comments'):
                        target_comments = dict()
                        max_id = 0
                        for c in target_issue.getComments():
                            target_comments[c.created] = c
                            if max_id < c.created:
                                max_id = c.created
                        for c in issue.getComments():
                            if c.created > max_id or c.created not in target_comments:
                                group = None
                                if hasattr(c, 'permittedGroup'):
                                    group = c.permittedGroup
                                try:
                                    target.executeCommand(issue.id, 'comment', c.text, group, c.author,
                                                          disable_notifications=True)
                                except youtrack.YouTrackException as e:
                                    print('Cannot add comment to issue')
                                    print(e)

                    if params.get('sync_custom_fields'):
                        skip_fields = []
                        if tt_settings and tt_settings.Enabled and tt_settings.TimeSpentField:
                            skip_fields.append(tt_settings.TimeSpentField)
                        skip_fields = [name.lower() for name in skip_fields]
                        for pcf in [pcf for pcf in project_custom_fields if pcf.name.lower() not in skip_fields]:
                            target_cf_value = None
                            if pcf.name in target_issue:
                                target_cf_value = target_issue[pcf.name]
                                if isinstance(target_cf_value, (list, tuple)):
                                    target_cf_value = set(target_cf_value)
                                elif target_cf_value == target.getProjectCustomField(projectId, pcf.name).emptyText:
                                    target_cf_value = None
                            source_cf_value = None
                            if pcf.name in issue:
                                source_cf_value = issue[pcf.name]
                                if isinstance(source_cf_value, (list, tuple)):
                                    source_cf_value = set(source_cf_value)
                                elif source_cf_value == source.getProjectCustomField(projectId, pcf.name).emptyText:
                                    source_cf_value = None
                            if source_cf_value == target_cf_value:
                                continue
                            if isinstance(source_cf_value, set) or isinstance(target_cf_value, set):
                                if source_cf_value is None:
                                    source_cf_value = set([])
                                elif not isinstance(source_cf_value, set):
                                    source_cf_value = set([source_cf_value])
                                if target_cf_value is None:
                                    target_cf_value = set([])
                                elif not isinstance(target_cf_value, set):
                                    target_cf_value = set([target_cf_value])
                                for v in target_cf_value:
                                    if v not in source_cf_value:
                                        target.executeCommand(issue.id, 'remove %s %s' % (pcf.name, v),
                                                              disable_notifications=True)
                                for v in source_cf_value:
                                    if v not in target_cf_value:
                                        target.executeCommand(issue.id, 'add %s %s' % (pcf.name, v),
                                                              disable_notifications=True)
                            else:
                                if source_cf_value is None:
                                    source_cf_value = target.getProjectCustomField(projectId, pcf.name).emptyText
                                if pcf.type.lower() == 'date':
                                    m = re.match(r'(\d{10})(?:\d{3})?', str(source_cf_value))
                                    if m:
                                        source_cf_value = datetime.datetime.fromtimestamp(
                                            int(m.group(1))).strftime('%Y-%m-%d')
                                elif pcf.type.lower() == 'period':
                                    source_cf_value = '%sm' % source_cf_value
                                command = '%s %s' % (pcf.name, source_cf_value)
                                try:
                                    target.executeCommand(issue.id, command, disable_notifications=True)
                                except youtrack.YouTrackException as e:
                                    if e.response.status == 412 and e.response.reason.find('Precondition Failed') > -1:
                                        print('WARN: Some workflow blocks following command: %s' % command)
                                        failed_commands.append((issue.id, command))

                    if sync_workitems:
                        workitems = source.getWorkItems(issue.id)
                        if workitems:
                            existing_workitems = dict()
                            target_workitems = target.getWorkItems(issue.id)
                            if target_workitems:
                                for w in target_workitems:
                                    _id = '%s\n%s\n%s' % (w.date, w.authorLogin, w.duration)
                                    if hasattr(w, 'description'):
                                        _id += '\n%s' % w.description
                                    existing_workitems[_id] = w
                            new_workitems = []
                            for w in workitems:
                                _id = '%s\n%s\n%s' % (w.date, w.authorLogin, w.duration)
                                if hasattr(w, 'description'):
                                    _id += '\n%s' % w.description
                                if _id not in existing_workitems:
                                    new_workitems.append(w)
                            if new_workitems:
                                print("Process workitems for issue [ " + issue.id + "]")
                                try:
                                    user_importer.importUsersRecursively(
                                        [source.getUser(w.authorLogin)
                                         for w in new_workitems])
                                    target.importWorkItems(issue.id, new_workitems)
                                except youtrack.YouTrackException as e:
                                    if e.response.status == 404:
                                        print("WARN: Target YouTrack doesn't support workitems importing.")
                                        print("WARN: Workitems won't be imported.")
                                        sync_workitems = False
                                    else:
                                        print("ERROR: Skipping workitems because of error:" + str(e))

                    print("Process attachments for issue [%s]" % issue.id)
                    existing_attachments = dict()
                    try:
                        for a in target.getAttachments(issue.id):
                            existing_attachments[a.name + '\n' + a.created] = a
                    except youtrack.YouTrackException as e:
                        if e.response.status == 404:
                            print("Skip importing attachments because issue %s doesn't exist" % issue.id)
                            continue
                        raise e

                    attachments = []

                    users = set([])
                    for a in issue.getAttachments():
                        if a.name + '\n' + a.created in existing_attachments and not params.get('replace_attachments'):
                            a.name = utf8encode(a.name)
                            try:
                                print("Skip attachment '%s' (created: %s) because it's already exists"
                                      % (utf8encode(a.name), utf8encode(a.created)))
                            except Exception:
                                pass
                            continue
                        attachments.append(a)
                        author = a.getAuthor()
                        if author is not None:
                            users.add(author)
                    user_importer.importUsersRecursively(users)

                    for a in attachments:
                        print("Transfer attachment of " + utf8encode(issue.id) + ": " + utf8encode(a.name))
                        # TODO: add authorLogin to workaround http://youtrack.jetbrains.net/issue/JT-6082
                        # a.authorLogin = target_login
                        try:
                            target.createAttachmentFromAttachment(issue.id, a)
                        except BaseException as e:
                            print("Cant import attachment [ %s ]" % utf8encode(a.name))
                            print(repr(e))
                            continue
                        if params.get('replace_attachments'):
                            try:
                                old_attachment = existing_attachments.get(a.name + '\n' + a.created)
                                if old_attachment:
                                    print('Deleting old attachment')
                                    target.deleteAttachment(issue.id, old_attachment.id)
                            except BaseException as e:
                                print("Cannot delete attachment '%s' from issue %s" % (
                                utf8encode(a.name), utf8encode(issue.id)))
                                print(e)

            except Exception as e:
                print('Cant process issues from ' + str(start) + ' to ' + str(start + max))
                traceback.print_exc()
                raise e

            start += max

    print("Import issue links")
    link_importer.importCollectedLinks()

    print("Trying to execute failed commands once again")
    for issue_id, command in failed_commands:
        try:
            print('Executing command on issue %s: %s' % (issue_id, command))
            target.executeCommand(issue_id, command, disable_notifications=True)
        except youtrack.YouTrackException as e:
            print('Failed to execute command for issue #%s: %s' % (issue_id, command))
            print(e)
Exemplo n.º 27
0
def bugzilla2youtrack(params):
    # Connecting to Bugzilla
    client = Client(host=params['bz_host'],
                    port=int(params['bz_port']),
                    login=params['bz_login'],
                    password=params['bz_password'],
                    db_name=params['bz_db'])

    bz_product_names = params.get('bz_product_names')
    if not bz_product_names:
        answer = raw_input(
            "All projects will be imported. Are you sure? [Y/n] ")
        if answer.strip().lower() not in ("y", "yes", ""):
            sys.exit()
        bz_product_names = client.get_product_names()

    print("bz_product_names: " + repr(bz_product_names))

    # Connecting to YouTrack
    token = params.get('token')
    if not token and 'token_file' in params:
        try:
            with open(params['token_file'], 'r') as f:
                token = f.read().strip()
        except (OSError, IOError) as e:
            print("Cannot load token from file: " + str(e))
            sys.exit(1)
    if token:
        target = Connection(params['yt_url'], token=token)
    elif 'yt_login' in params:
        target = Connection(params['yt_url'], params.get('yt_login'),
                            params.get('yt_password'))
    else:
        print("You have to provide token or login/password to import data")
        sys.exit(1)

    print("Creating issue link types")
    link_types = client.get_issue_link_types()
    for link in link_types:
        print("Processing link type [ %s ]" % link.name)
        try:
            target.createIssueLinkType(to_yt_issue_link_type(link))
        except YouTrackException:
            print(
                "Can't create link type [ %s ] (maybe because it already exists)"
                % link.name)
    print("Creating issue link types finished")

    print("Creating custom fields")
    custom_fields = client.get_custom_fields()
    for cf in custom_fields:
        create_yt_custom_field(cf, target)
    print("Creating custom fields finished")

    for key in youtrackutils.bugzilla.FIELD_TYPES:
        if key not in youtrack.EXISTING_FIELDS:
            create_custom_field(target,
                                youtrackutils.bugzilla.FIELD_TYPES[key],
                                key,
                                True,
                                bundle_policy="1")

    bz_product_ids = []

    for name in bz_product_names:
        product_id = str(client.get_product_id_by_name(name))
        bz_product_ids.append(product_id)
        print("Creating project [ %s ] with name [ %s ]" % (product_id, name))
        try:
            target.getProject(product_id)
        except YouTrackException:
            target.createProjectDetailed(
                product_id, name, client.get_project_description(product_id),
                'root')

        print("Importing components for project [ %s ]" % product_id)
        process_components(client.get_components(product_id), product_id,
                           target)
        print("Importing components finished for project [ %s ]" % product_id)

        print("Importing versions for project [ %s ]" % product_id)
        process_versions(client.get_versions(product_id), product_id, target)
        print("Importing versions finished for project [ %s ] finished" %
              product_id)

        print("Importing issues to project [ %s ]" % product_id)
        max_count = 100
        count = 0
        from_id = 0
        bz_issues_count = client.get_issues_count(product_id)
        while count < bz_issues_count:
            batch = client.get_issues(product_id, from_id, from_id + max_count)
            batch = [bz_issue for bz_issue in batch]
            count += len(batch)
            from_id += max_count
            target.importIssues(product_id, product_id + " assignees", [
                to_yt_issue(bz_issue, product_id, target) for bz_issue in batch
            ])
            # todo convert to good tags import
            for issue in batch:
                tags = issue["keywords"] | issue["flags"]
                for t in tags:
                    print("Processing tag [ %s ]" % t.encode('utf8'))
                    target.executeCommand(
                        str(product_id) + "-" +
                        str(issue[get_number_in_project_field_name()]),
                        "tag " + t.encode('utf8'))
            for issue in batch:
                issue_id = str(product_id) + '-' + \
                           str(issue[get_number_in_project_field_name()])
                for attach in issue["attachments"]:
                    print("Processing attachment [ %s ] for issue %s" %
                          (utf8encode(attach.name), issue_id))
                    content = StringIO(attach.content)
                    try:
                        target.importAttachment(
                            issue_id, attach.name, content,
                            attach.reporter.login, None, None,
                            str(int(attach.created) * 1000))
                    except urllib2.HTTPError as e:
                        print("WARN: Cant import attachment [ %s ]" %
                              utf8encode(attach.name))
                        print(e.code)
                        print(e.read())
                        print("Please check Max Upload File Size in YouTrack")
                        continue
        print("Importing issues to project [ %s ] finished" % product_id)

    # todo add pagination to links
    print("Importing issue links")
    cf_links = client.get_issue_links()
    duplicate_links = client.get_duplicate_links()
    if len(duplicate_links):
        try:
            target.createIssueLinkTypeDetailed("Duplicate", "duplicates",
                                               "is duplicated by", True)
        except YouTrackException:
            print(
                "Can't create link type [ Duplicate ] (maybe because it already exists)"
            )
    depend_links = client.get_dependencies_link()
    if len(depend_links):
        try:
            target.createIssueLinkTypeDetailed("Depend", "depends on",
                                               "is required for", True)
        except YouTrackException:
            print(
                "Can't create link type [ Depend ] (maybe because it already exists)"
            )
    links = cf_links | duplicate_links | depend_links

    links_to_import = list([])
    for link in links:
        print("Processing link %s for issue%s" % (link.name, link.source))
        if (str(link.target_product_id) in bz_product_ids) and (str(
                link.source_product_id) in bz_product_ids):
            links_to_import.append(to_yt_issue_link(link))
    print(target.importLinks(links_to_import))
    print("Importing issue links finished")
def bugzilla2youtrack(target_url, target_login, target_pass, bz_db, bz_host, bz_port, bz_login, bz_pass,
                      bz_product_names, issues_filter):
    # connecting to bz
    client = Client(bz_host, int(bz_port), bz_login, bz_pass, db_name=bz_db)

    if not len(bz_product_names):
        answer = raw_input("All projects will be imported. Are you sure? [y/n]")
        if answer.capitalize() != "Y":
            sys.exit()
        bz_product_names = client.get_product_names()

    print("bz_product_names :   " + repr(bz_product_names))

    # connecting to yt
    target = Connection(target_url, target_login, target_pass)

    print("Creating issue link types")
    link_types = client.get_issue_link_types()
    for link in link_types:
        print("Processing link type [ %s ]" % link.name)
        try:
            target.createIssueLinkType(to_yt_issue_link_type(link))
        except YouTrackException:
            print("Can't create link type [ %s ] (maybe because it already exists)" % link.name)
    print("Creating issue link types finished")

    print("Creating custom fields")
    custom_fields = client.get_custom_fields()
    for cf in custom_fields:
        create_yt_custom_field(cf, target)
    print("Creating custom fields finished")

    for key in youtrackutils.bugzilla.FIELD_TYPES:
        if key not in youtrack.EXISTING_FIELDS:
            create_custom_field(target, youtrackutils.bugzilla.FIELD_TYPES[key], key, True, bundle_policy="1")

    bz_product_ids = []

    for name in bz_product_names:
        product_id = str(client.get_product_id_by_name(name))
        bz_product_ids.append(product_id)
        print("Creating project [ %s ] with name [ %s ]" % (product_id, name))
        try:
            target.getProject(str(product_id))
        except YouTrackException:
            target.createProjectDetailed(str(product_id), name, client.get_project_description(product_id),
                target_login)

        print("Importing components for project [ %s ]" % product_id)
        process_components(client.get_components(product_id), product_id, target)
        print("Importing components finished for project [ %s ]" % product_id)

        print("Importing versions for project [ %s ]" % product_id)
        process_versions(client.get_versions(product_id), product_id, target)
        print("Importing versions finished for project [ %s ] finished" % product_id)

        print("Importing issues to project [ %s ]" % product_id)
        max_count = 100
        count = 0
        from_id = 0
        bz_issues_count = client.get_issues_count(product_id)
        while count < bz_issues_count:
            batch = client.get_issues(product_id, from_id, from_id + max_count)
            batch = [bz_issue for bz_issue in batch if (issues_filter(bz_issue))]
            count += len(batch)
            from_id += max_count
            target.importIssues(product_id, product_id + " assignees",
                [to_yt_issue(bz_issue, product_id, target) for bz_issue in batch])
            # todo convert to good tags import
            for issue in batch:
                tags = issue["keywords"] | issue["flags"]
                for t in tags:
                    print("Processing tag [ %s ]" % t.encode('utf8'))
                    target.executeCommand(str(product_id) + "-" + str(issue[get_number_in_project_field_name()]),
                        "tag " + t.encode('utf8'))
            for issue in batch:
                for attach in issue["attachments"]:
                    print("Processing attachment [ %s ]" % (attach.name.encode('utf8')))
                    content = StringIO(attach.content)
                    target.createAttachment(str(product_id) + "-" + str(issue[get_number_in_project_field_name()]),
                        attach.name, content, attach.reporter.login
                        , created=str(int(attach.created) * 1000))
        print("Importing issues to project [ %s ] finished" % product_id)

    # todo add pagination to links
    print("Importing issue links")
    cf_links = client.get_issue_links()
    duplicate_links = client.get_duplicate_links()
    if len(duplicate_links):
        try:
            target.createIssueLinkTypeDetailed("Duplicate", "duplicates", "is duplicated by", True)
        except YouTrackException:
            print("Can't create link type [ Duplicate ] (maybe because it already exists)")
    depend_links = client.get_dependencies_link()
    if len(depend_links):
        try:
            target.createIssueLinkTypeDetailed("Depend", "depends on", "is required for", True)
        except YouTrackException:
            print("Can't create link type [ Depend ] (maybe because it already exists)")
    links = cf_links | duplicate_links | depend_links

    links_to_import = list([])
    for link in links:
        print("Processing link %s for issue%s" % (link.name, link.source))
        if (str(link.target_product_id) in bz_product_ids) and (str(link.source_product_id) in bz_product_ids):
            links_to_import.append(to_yt_issue_link(link))
    print(target.importLinks(links_to_import))
    print("Importing issue links finished")
def Main(sendTo, subject, yamlMessage):
    """
    Workflow Zabbix-YouTrack
    :param sendTo: URL to Youtrack (ex. https://youtrack.example.com)
    :param subject: subject from Zabbix Action
    :param yamlMessage: message from Zabbix Action
    :return:
    """

    # ----- Use below example yamlMessage to debug -----
#     yamlMessage = """Name: 'Test Zabbix-YT workflow, ignore it'
# Text: 'Agent ping (server:agent.ping()): DOWN (1) '
# Hostname: 'server.exmpale.ru'
# Status: "OK"
# Severity: "High"
# EventID: "96976"
# TriggerID: "123456789012" """

    messages = yaml.load(yamlMessage)

    # ----- START Issue parameters -----
    # Correspondence between the YouTrackPriority and ZabbixSeverity
    # Critical >= High
    # Normal < High

    ytPriority = 'Normal'
    if messages['Severity'] == 'Disaster' or messages['Severity'] == 'High':
        ytPriority = 'Critical'

    ytName = "{} ZabbixTriggerID::{}".format(messages['Name'], messages['TriggerID'])
    # ----- END Issue parameters -----

    # ----- START Youtrack Issue description -----
    # Search link to other issue
    searchString = "Hostname: '{}'".format(messages['Hostname'])
    linkToHostIssue = "{youtrack}/issues/{projectname}?q={query}".format(
        youtrack=sendTo,
        projectname=YT_PROJECT_NAME,
        query=urllib.parse.quote(searchString, safe='')
    )

    issueDescription = """
{ytName}
-----
{yamlMessage}
-----
- [https://zabbix.example.com/zabbix.php?action=dashboard.view Zabbix Dashboard]
- Show [{linkToHostIssue} all issue for *this host*]
""".format(
        ytName=ytName,
        yamlMessage=yamlMessage,
        linkToHostIssue=linkToHostIssue, )

    # ----- END Youtrack Issue description -----

    # ----- START Youtrack current week -----

    # Create connect to Youtrack API
    connection = Connection(sendTo, YT_USER, YT_PASSWORD)

    # Get current week in YT format (Sprint planned)
    version = connection.getAllBundles('version')

    for fixVersion in version[0].values:
        if fixVersion['archived'] == False and fixVersion['released'] == False:
            fixVersionWeek = fixVersion['name']
            break
    # ----- END Youtrack current week -----

    # ----- START Youtrack get or create issue -----
    # Get issue if exist
    # Search for TriggerID
    createNewIssue = False

    logger.debug("Get issue with text '{}'".format(messages['TriggerID']))
    issue = connection.getIssues(YT_PROJECT_NAME,
                                 "ZabbixTriggerID::{}".format(messages['TriggerID']),
                                 0,
                                 1)


    if len(issue) == 0:
        createNewIssue = True

    else:
        # if issue contains TriggerID in summary, then it's good issue
        # else create new issue, this is bad issue, not from Zabbix
        if "ZabbixTriggerID::{}".format(messages['TriggerID']) in issue[0]['summary']:
            issueId = issue[0]['id']
            issue = connection.getIssue(issueId)
        else:
            createNewIssue = True

    # Create new issue
    if createNewIssue:
        logger.debug("Create new issue because it is not exist")
        issue = connection.createIssue(YT_PROJECT_NAME,
                                       'Unassigned',
                                       ytName,
                                       issueDescription,
                                       priority=ytPriority,
                                       subsystem=YT_SUBSYSTEM,
                                       type=YT_TYPE,
                                       )
        time.sleep(3)

        # Parse ID for new issue
        result = re.search(r'(CM-\d*)', issue[0]['location'])
        issueId = result.group(0)
        issue = connection.getIssue(issueId)

    logger.debug("Issue have id={}".format(issueId))

    # Set issue service
    ExecAndLog(connection, issueId, "Service {}".format(YT_SERVICE))

    # Update priority
    ExecAndLog(connection, issueId, "Priority {}".format(ytPriority))

    # ----- END Youtrack get or create issue -----

    # ----- START PROBLEM block ------
    if messages['Status'] == "PROBLEM":

        # Issue exist and NOT Hold on, Unnassigned and Estimated time set
        if issue['State'] != 'Hold on':

            # Estimated time
            ExecAndLog(connection, issueId, "Estimated time {}".format(YT_TIME))

            # Update fix version
            ExecAndLog(connection=connection, issueId=issueId, command="Sprint planned {}".format(fixVersionWeek))

        # Reopen if Fixed or Verified or Canceled
        if issue['State'] == 'Fixed' or issue['State'] == 'Verified' or issue['State'] == 'Canceled':
            # Reopen Issue
            ExecAndLog(connection, issueId, "State reopen")

            # Assignee issue
            ExecAndLog(connection, issueId, command="Assignee Unassigned")

        # Update summary and description for issue
        logger.debug("Run command in {issueId}: {command}".format(issueId=issueId,
                                                                  command="""Update summary and description with connection.updateIssue method"""
                                                                  ))
        connection.updateIssue(issueId=issueId, summary=ytName, description=issueDescription)

        # Add comment
        logger.debug("Run command in {issueId}: {command}".format(issueId=issueId,
                                                                  command="""Now is PROBLEM {}""".format(
                                                                      messages['Text'])
                                                                  ))
        connection.executeCommand(issueId=issueId,
                                  command="",
                                  comment=YT_COMMENT.format(
                                      status=messages['Status'],
                                      text=messages['Text'])
                                  )

        # Send ID to Zabbix:
        logger.debug("ZABBIX-API: Send Youtrack ID to {}".format(messages['EventID']))
        Zbx.event.acknowledge(eventids=messages['EventID'], message="Create Youtrack task")
        Zbx.event.acknowledge(eventids=messages['EventID'],
                              message="https://youtrack.example.com/issue/{}".format(issueId))
    # ----- End PROBLEM block ------


    # ----- Start OK block -----
    if messages['Status'] == "OK":

        if issue['State'] == 'Hold on' or issue['State'] == 'Registered':
            # Cancel if not in work
            ExecAndLog(connection, issueId, command="State Cancel")

            # Assignee issue
            ExecAndLog(connection, issueId, command="Assignee {}".format(YT_ASSIGNEE))

        if issue['State'] == 'Fixed':
            # Verify if Fixed
            ExecAndLog(connection, issueId, command="State verify")

        logger.debug("Run command in {issueId}: {command}".format(issueId=issueId,
                                                                  command="""Now is OK {}""".format(messages['Text'])
                                                                  ))
        connection.executeCommand(issueId=issueId,
                                  command="",
                                  comment=YT_COMMENT.format(
                                      status=messages['Status'],
                                      text=messages['Text'])
                                  )