示例#1
0
def post_users_zendesk(batch_size=100):
    zendesk_users = []
    if post_queue.qsize() < batch_size:
        return
    logger.info("Posting %d users..." % batch_size)
    zendesk_users.extend(
        (post_queue.get().to_primitive()) for i in xrange(batch_size))
    data = json.dumps({"users": zendesk_users})
    handle_retries(retryable_request=ZendeskUserPostRequest,
                   get_request_kwargs={'data': data})
示例#2
0
def main_upload():
    POOL = main.POOL
    global_results = get_global_results()
    parser = argparse.ArgumentParser(
        description="Migrate leftover support tickets from desk to zendesk.")
    parser.add_argument("--mode",
                        help="Specify either (u)sers or (t)ickets to migrate")
    parser.add_argument('--filename',
                        help="Specify file to read broken tickets from")
    options = parser.parse_args()
    mode = options.mode
    filename = options.filename
    agent_id = handle_retries(
        retryable_request=ZendeskUserRequest,
        get_request_kwargs={"url": "/api/v2/users/%s" % AGENT_ID})
    with open(filename) as f:
        desk_ticket_ids = [line.strip() for line in f]

    for i in xrange(0, len(desk_ticket_ids), 100):
        comma_sep_tickets = ','.join(desk_ticket_ids[i:i + 100])
        desk_ticket_models = handle_retries(
            retryable_request=DeskIndividualTicketRequest,
            get_request_kwargs={
                'url': '/api/v2/cases/search',
                'params': {
                    'embed': 'customer, message',
                    'page': 1,
                    'per_page': 100,
                    'case_id': comma_sep_tickets
                }
            })

        for ticket in desk_ticket_models:
            if mode == 'u':
                post_func = post_users_zendesk
                global_results.appendleft(
                    POOL.apply_async(get_customers, kwds={"ticket": ticket}))
            elif mode == 't':
                logger.info("Adding ticket external id %s" % ticket.id)
                post_func = post_tickets_zendesk
                global_results.appendleft(
                    POOL.apply_async(migrate_ticket,
                                     kwds={
                                         "ticket": ticket,
                                         "agent": agent_id
                                     }))

    while len(global_results) > 0:
        result = global_results.pop()
        result.get()
    POOL.close()
    POOL.join()

    flush_queues(post_func)
示例#3
0
def pool_controller(retryable_request, get_request_kwargs, agent_id):  # noqa
    # Get first page and total number of apges
    num_pages = handle_retries(retryable_request=retryable_request,
                               get_pages=True,
                               get_request_kwargs=get_request_kwargs)
    if not num_pages:
        logger.error("Error: Could not get number of pages")
        return
    logger.info("Number of pages %d" % num_pages)
    for i in xrange(1, num_pages + 1):
        logger.info("Processing page %d" % i)
        # Returns list of dictionaries for ticket objects OR list of user objects
        object_list = []
        migrating_users = retryable_request == DeskCustomerRequest
        if migrating_users:
            kwargs = {
                'params': {
                    'embed': 'facebook_user,twitter_user',
                    'page': i,
                    'per_page': 100
                }
            }
        else:
            kwargs = {
                'params': {
                    'embed': 'customer, message',
                    'page': i,
                    'per_page': 100
                }
            }
        object_list = POOL.apply(handle_retries,
                                 kwds={
                                     "retryable_request": retryable_request,
                                     "get_request_kwargs": kwargs
                                 })
        for elem in object_list:
            if migrating_users:
                global_results.appendleft(
                    POOL.apply_async(migrate_user, kwds={"desk_user": elem}))
            else:
                global_results.appendleft(
                    POOL.apply_async(migrate_ticket,
                                     kwds={
                                         "ticket": elem,
                                         "agent": agent_id
                                     }))
    while len(global_results) > 0:
        result = global_results.pop()
        result.get()
    POOL.close()
    POOL.join()
    if migrating_users:
        post_func = post_users_zendesk
    else:
        post_func = post_tickets_zendesk

    flush_queues(post_func)
    return num_pages
示例#4
0
def ticket_json_to_desk_obj(ticket):
    """Create ticket schmantics objects for desk data."""
    ticket.notes = []
    ticket.attachments = []
    if ticket.num_replies != 0:
        upper_limit = int(math.ceil(ticket.num_replies / 100.) + 1)
        for i in xrange(1, upper_limit):
            ticket.messages.extend(
                handle_retries(retryable_request=DeskMessageRequest,
                               get_request_kwargs={
                                   "params": {
                                       'page': i,
                                       'per_page': 100
                                   },
                                   "url":
                                   "/api/v2/cases/%d/replies" % (ticket.id)
                               }))
    if ticket.num_notes != 0:
        upper_limit = int(math.ceil(ticket.num_notes / 100.) + 1)
        for i in xrange(1, upper_limit):
            ticket.notes.extend(
                handle_retries(retryable_request=DeskMessageRequest,
                               get_request_kwargs={
                                   "params": {
                                       'page': i,
                                       'per_page': 100
                                   },
                                   "url":
                                   "/api/v2/cases/%d/notes" % (ticket.id)
                               }))
    if ticket.num_attachments != 0:
        upper_limit = int(math.ceil(ticket.num_attachments / 100.) + 1)
        for i in xrange(1, upper_limit):
            ticket.attachments.extend(
                handle_retries(retryable_request=DeskAttachmentRequest,
                               get_request_kwargs={
                                   "params": {
                                       'page': i,
                                       'per_page': 100
                                   },
                                   "url":
                                   "/api/v2/cases/%d/attachments" % (ticket.id)
                               }))
    return ticket
示例#5
0
def get_customers(ticket):
    desk_user_model = handle_retries(
        retryable_request=DeskIndividualCustomerRequest,
        get_request_kwargs={
            'url': '/api/v2/customers/%s' % ticket.user_id,
            'params': {
                'embed': 'facebook_user,twitter_user'
            }
        })
    logger.info("Adding customer external id %s" % desk_user_model.id)
    migrate_user(desk_user_model)
示例#6
0
def update_tickets_zendesk(batch_size=100):
    zendesk_tickets = []
    if update_queue.qsize() < batch_size:
        return
    zendesk_tickets.extend(update_queue.get() for i in xrange(batch_size))
    dedup_dict = defaultdict(
        list)  # One API call can't update the same ticket twice
    for ticket in zendesk_tickets:
        dedup_dict[ticket.id].append(ticket)
    ztickets_deduped = []
    for id, item in dedup_dict.iteritems():
        ztickets_deduped.append(item[0].to_primitive())
        if len(item) > 1:
            logger.info("There were %d dupes" % (len(item) - 1))
            for dupe in item[1:]:
                update_queue.put(dupe)
    logger.info("Updating ticket...")
    data = json.dumps({"tickets": ztickets_deduped})
    logger.info(data)
    handle_retries(retryable_request=ZendeskUpdateRequest,
                   get_request_kwargs={'data': data})
示例#7
0
def migrate_ticket(ticket, agent):  # noqa
    desk_ticket = ticket_json_to_desk_obj(ticket)
    if not desk_ticket:
        return
    attachment_tuples = []
    for attachment in desk_ticket.attachments:
        content = handle_retries(retryable_request=CheckUpload,
                                 get_request_kwargs={'url': attachment.url})
        if content:
            uploaded_attachment = handle_retries(
                retryable_request=ZendeskUpload,
                get_request_kwargs={
                    'params': {
                        'filename': attachment.file_name
                    },
                    'data': content
                })
            attachment_tuples.append(
                AttachmentTuple(token=uploaded_attachment.get('upload',
                                                              {}).get(
                                                                  'token', ''),
                                message_uri=attachment.message_uri))
    zd_ticket = desk_ticket_to_ZTicket(ticket=desk_ticket,
                                       agent_id=agent,
                                       attachment_tuples=attachment_tuples)
    if not zd_ticket:
        return
    logger.info("Creating OR updating ticket: %d" % desk_ticket.id)
    id = handle_retries(retryable_request=ZendeskTicketIDRequest,
                        get_request_kwargs={
                            'url': "/api/v2/search.json",
                            'params': {
                                'query':
                                'type:ticket external_id:%d' % desk_ticket.id
                            }
                        })
    # Ticket already exists
    if id > 0:
        zd_ticket.id = id
        num_comments = handle_retries(
            retryable_request=ZendeskTicketCommentCount,
            get_request_kwargs={
                'url': "/api/v2/tickets/%d.json" % (id),
                'params': {
                    'include': 'comment_count'
                }
            })
        comments_to_add = len(zd_ticket.comments) - num_comments
        logger.info("Adding %d comments to ticket %d already in zendesk" %
                    (comments_to_add, id))
        if comments_to_add > 0:
            individual_tickets = create_ZTickets_for_comments(
                zd_ticket, comments_to_add)
            [update_queue.put(i_ticket) for i_ticket in individual_tickets]
        zd_ticket.comments = None
        update_queue.put(
            zd_ticket
        )  # In all cases (new comment/no new comment), we should add the original ticket to update status/etc.
        global_results.appendleft(POOL.apply_async(update_tickets_zendesk))
    elif id == 0:
        post_queue.put(zd_ticket)
        global_results.appendleft(POOL.apply_async(post_tickets_zendesk))
    else:
        logger.error(
            "Could not add ticket %d to the queue - checking existence failed"
            % desk_ticket.id)
示例#8
0
def main():
    parser = argparse.ArgumentParser(
        description="Migrate support tickets from desk to zendesk.")
    parser.add_argument("--mode",
                        help="Specify either (u)sers or (t)ickets to migrate")
    options = parser.parse_args()
    mode = options.mode
    # hardcode the agent from which all tickets are being posted
    agent_id = handle_retries(
        retryable_request=ZendeskUserRequest,
        get_request_kwargs={"url": "/api/v2/users/%s" % AGENT_ID})
    if mode == 'u':
        pool_controller(retryable_request=DeskCustomerRequest,
                        agent_id=agent_id,
                        get_request_kwargs={
                            'params': {
                                'embed': 'facebook_user,twitter_user',
                                'page': 1,
                                'per_page': 100
                            }
                        })
    elif mode == 't':
        pool_controller(retryable_request=DeskTicketRequest,
                        agent_id=agent_id,
                        get_request_kwargs={
                            'params': {
                                'embed': 'customer, message',
                                'page': 1,
                                'per_page': 100
                            }
                        })
    else:
        logger.error("Unsupported mode %s" % mode)
        return

    logger.info('Complete: All pages processed')
    if mode == 't':
        for status in TICKET_STATUSES:
            num_tickets = handle_retries(retryable_request=ZendeskVerification,
                                         get_request_kwargs={
                                             'params': {
                                                 'query':
                                                 'type:ticket status:%s' %
                                                 status
                                             }
                                         })
            if not num_tickets:
                logger.error("Verification failed")
                return
            logger.info('Number of %s tickets in Zendesk: %s' %
                        (status, num_tickets))
    else:
        for role in ROLES:
            num_users = handle_retries(retryable_request=ZendeskVerification,
                                       get_request_kwargs={
                                           'params': {
                                               'query':
                                               'type:user role:%s' % role
                                           }
                                       })
            if not num_users:
                logger.error("Verification failed")
                return
            logger.info('Number of %s users in Zendesk: %s' %
                        (role, num_users))
示例#9
0
def desk_ticket_to_ZTicket(ticket, agent_id, attachment_tuples):  # noqa
    """Convert Desk Ticket object to Zendesk ZTicket object."""
    zmessages = []
    creator_id = handle_retries(retryable_request=ZendeskSearch,
                                get_request_kwargs={
                                    "url": "/api/v2/search.json",
                                    "params": {
                                        "query":
                                        "type:user %d" % ticket.user_id
                                    }
                                })
    if creator_id == 0 or not creator_id:  # Must migrate users BEFORE migrating tickets
        logger.error(
            "Could not get creator_id for desk ticket %d...not posting or adding"
            % ticket.id)
        return
    remaining_attachments = attachment_tuples
    for message in ticket.messages:
        if not message.body.strip(
        ):  # Zendesk requires all comments to have a body, but Desk does not have this requirement
            message.body = "No message"
        # we set created_at to updated_at because ZD has no draft message status, but Desk does.
        zmessage = ZMessageCreate(
            {
                'value': message.body,
                'created_at': message.updated_at,
                'author_id': agent_id,
                'uploads': [],
                'public': True
            },
            strict=False,
            partial=False)
        zmessage.uploads = [
            at.token for at in attachment_tuples
            if at.message_uri == message.uri
        ]
        remaining_attachments = [
            at for at in remaining_attachments if at.message_uri != message.uri
        ]  # Remove attachment we just added
        zmessage.author_id = agent_id
        if message.direction == 'in':
            zmessage.author_id = creator_id
            if message.creator_id != ticket.user_id:
                zd_user_id = handle_retries(retryable_request=ZendeskSearch,
                                            get_request_kwargs={
                                                "url": "/api/v2/search.json",
                                                "params": {
                                                    "query":
                                                    "type:user %d" %
                                                    message.creator_id
                                                }
                                            })
                if zd_user_id:
                    zmessage.author_id = zd_user_id
                else:
                    logger.error(
                        "Could not get creator_id for desk message %d...not posting or adding"
                        % ticket.id)
                    return
        zmessages.append(zmessage)
    for note in ticket.notes:
        if not note.body.strip():
            note.body = "No message"
        zmessage = ZMessageCreate(
            {
                'value': note.body,
                'created_at': note.updated_at,
                'author_id': agent_id,
                'uploads': [],
                'public': False
            },
            strict=False,
            partial=False)
        zmessages.append(zmessage)

    zticket = ZTicket(
        {
            'comments': zmessages,
            'subject': ticket.subject,
            'priority': 'low',
            'status': ticket.status,
            'external_id': ticket.id,
            'requester_id': creator_id,
            'assignee_id': agent_id,
            'tags': ['from_desk'],
            'created_at': ticket.created_at,
            'solved_at': ticket.resolved_at,
            'updated_at': ticket.updated_at
        },
        strict=False,
        partial=False)
    # Leftover attachments with no associated reply get added onto first message
    zticket.comments[0].uploads = [at.token for at in remaining_attachments]
    if 4 <= ticket.priority <= 6:
        zticket.priority = 'normal'
    elif 7 <= ticket.priority <= 9:
        zticket.priority = 'high'
    elif ticket.priority == 10:
        zticket.priority = 'urgent'

    if ticket.status == 'resolved':
        zticket.status = 'solved'
    return zticket