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)
        target_project_fields = [pcf.name for pcf in target.getProjectCustomFields(projectId)]
        for field in project_custom_fields:
            if field.name 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:
                create_project_custom_field(target, field, projectId)

        # 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:
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"
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")
        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:
                create_project_custom_field(target, field, projectId)

        # 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:
Esempio n. 6
0
class YouTrackExporter(object):

    def __init__(self, yt, influx):
        self.yt = YouTrack(**yt)
        self.influx = InfluxDBClient(**influx)
        self.logger = logging.getLogger('worktime_reporter')
        self.project_blacklist = ['RRS', 'RR_INS']

    def get_all_issues(self, project):
        def _gen():
            skip = 0
            while True:
                issues = self.yt.getIssues(project.id,
                                           'Spent time: -?', skip, 50)
                if not issues:
                    break
                skip += len(issues)
                yield issues
        return itertools.chain.from_iterable(_gen())

    def issue_to_measurement(self, project, issue):
        self.logger.info(f'Looking into issue {issue.id}')
        lead_time = getattr(issue, 'Lead time', None)
        stream = getattr(issue, 'Stream', None)
        target = getattr(issue, 'Target', 'Core')
        _type = getattr(issue, 'Type', None)
        resolved = getattr(issue, 'resolved', None)
        estimation = getattr(issue, 'Estimation', None)
        tags = {
            'project': project.id,
            'issue': issue.id,
            'stream': stream,
            'target': target,
            'type': _type
        }
        counter = defaultdict(lambda: 0)
        for work_item in self.yt.getWorkItems(issue.id):
            _tags = dict(tags)
            _tags['author'] = getattr(work_item, 'authorLogin', None)
            created = getattr(work_item, 'created', None)
            work_type = getattr(work_item, 'worktype', None)
            date = getattr(work_item, 'date', created)
            duration = int(work_item.duration)
            if not created:
                continue
            created_dt = datetime.datetime.fromtimestamp(float(created)/1000)
            date_dt = datetime.datetime.fromtimestamp(float(date)/1000)
            time_dt = datetime.datetime.combine(date_dt.date(), created_dt.time(), date_dt.tzinfo)
            time_ts = int(datetime.datetime.timestamp(time_dt)*1000)
            counter[work_type] += duration
            yield {
                'measurement': 'work_item',
                'tags': _tags,
                'time': time_ts,
                'fields': {
                    work_type: duration
                }
            }

        cycle_time = counter['Analytics'] + counter['Development'] + counter['Testing']
        created = getattr(issue, 'created', None)
        if estimation:
            yield {
                'measurement': 'issue',
                'tags': tags,
                'time': int(created),
                'fields': {
                    'estimation': int(estimation)
                }
            }
        if lead_time and resolved:
            yield {
                'measurement': 'issue',
                'tags': tags,
                'time': int(resolved),
                'fields': {
                    'lead_time': float(lead_time),
                    'cycle_time': cycle_time,
                }
            }

    def process_project(self, project):
        if project.id in self.project_blacklist:
            self.logger.info(f'Skipping project {project.id}, blacklisted')
            return

        ts = self.yt.getProjectTimeTrackingSettings(project['id'])
        if not ts.Enabled:
            self.logger.info(
                f'Skipping project {project.id}, no time tracking')
            return

        self.logger.info(f'Looking into project {project.id}')
        issues = self.get_all_issues(project)
        measurements = (self.issue_to_measurement(project, issue) for issue in issues)
        return itertools.chain.from_iterable(measurements)

    def export(self):
        def _gen():
            projects = self.yt.getProjects()
            for project_id in projects:
                project = self.yt.getProject(project_id)
                measurements = self.process_project(project)
                if measurements:
                    yield measurements

        self.influx.drop_measurement('issue')
        self.influx.drop_measurement('work_item')

        measurements = itertools.chain.from_iterable(_gen())
        for chunk in grouper(measurements, 50):
            filtered = filter(lambda x: x is not None, chunk)
            self.influx.write_points(filtered, time_precision='ms')