<commit_before>import json
import urlparse
import requests


# Freshdesk uses some exotic return codes

HTTP_ALREADY_EXISTS = 422

class FreshDeskObjects(object):
    """ Abstract class to wrap a type of freshdesk resource.
    """
    def __init__(self, client):
        self.client = client

    def api_endpoint(self, id=None):
        if id:
            return '/{}s/{}.json'.format(self.api_name, id)
        else:
            return '/{}s.json'.format(self.api_name)

    # CRUD methods

    def create(self, **kwargs):
        return self.client.req(
            requests.post, self.api_endpoint(), self.wrapper_name,
            **kwargs)

    def update(self, id, **kwargs):
        return self.client.req(
            requests.put, self.api_endpoint(id), self.wrapper_name,
            **kwargs)

    def delete(self, id):
        return self.client.req(
            requests.delete, self.api_endpoint(id), self.wrapper_name)

    def get(self, id):
        return self.client.req(
            requests.get, self.api_endpoint(id), self.wrapper_name,
            id=id)

    def get_list(self, **params):
        return self.client.req(
            requests.get, self.api_endpoint(), self.wrapper_name,
            params=params)


class FreshDeskContacts(FreshDeskObjects):
    """ http://freshdesk.com/api#user
    """
    api_name = 'contact'
    wrapper_name = 'user'

    # Status names (default is state=verified)
    DELETED    = 'deleted'
    ALL        = 'all'         # Do not include deleted accounts
    VERIFIED   = 'verified'
    UNVERIFIED = 'unverified'

    def create(self, name, email, **kwargs):
        return super(FreshDeskContacts, self).create(name=name, email=email, **kwargs)

    def create_or_enable(self, name, email, **kwargs):
        """ If creation fails because user exists in deleted state, restore it.

        Freshdesk do not allow two users with same email, even if the collision
        is with a deleted user.
        """
        try:
            return self.create(name=name, email=email)
        except FreshDeskClient.APIError as e:
            contacts = self.get_list(
                state='deleted', query='email is {}'.format(email))
            if ((len(contacts) > 0) and
                (e.resp.status_code == HTTP_ALREADY_EXISTS)):
                return self.update(contacts[0]['id'], name=name, deleted=False,
                                   **kwargs)
            else:
                raise


class FreshDeskCustomers(FreshDeskObjects):
    """ http://freshdesk.com/api#companies
    """
    api_name = 'customer'
    wrapper_name = api_name

    def create(self, name, **kwargs):
        return super(FreshDeskCustomers, self).create(name=name, **kwargs)


class FreshDeskClient(object):
    """ Simple wrapper arround freshdesk REST API

    This wrapper is non neutral, it includes Oasiswork policy about default
    settings.

        cli = FreshDeskClient('https://mycompany.freshdesk.com', 'qwertyui')
        cli.contacts.get_list(state='all')

    """
    class APIError(Exception):
        def __init__(self, resp):
            self.resp = resp
        def __str__(self):
            return 'HTTP {}: {}'.format(self.resp.status_code, self.resp.text)

    def __init__(self, url, key):
        """
        :param url  endpoint url, eg: https://yourcompany.freshdesk.com
        :param key  API key
        """
        self.url = url
        self.key = key
        self.last_resp = None

        # Resources types
        self.customers = FreshDeskCustomers(self)
        self.contacts = FreshDeskContacts(self)

    def req(self, func, path, resource_type, params={}, **kwargs):
        abs_url = urlparse.urljoin(self.url, path)

        if func in (requests.patch, requests.put, requests.post):
            req_attrs = {
                'data': json.dumps({resource_type: kwargs}),
                'headers': {'Content-Type': 'application/json'}
            }
        else:
            req_attrs = {}

        resp =  func(
            abs_url,
            auth=(self.key, '<PASSWORD>'),
            params=params,
            **req_attrs)
        self.last_resp = resp
        if resp.ok:
            # for those two types of requests, the output is empty
            if func in (requests.delete, requests.put):
                return resp.text
            else:
                # Just unwrap {'res_type': {...}} -> {...}
                return [i[resource_type] for i in resp.json()]

        else:
            raise self.APIError(resp)
<commit_msg>handle either return list or single item return in unwrapping json<commit_after>import json
import urlparse
import requests


# Freshdesk uses some exotic return codes

HTTP_ALREADY_EXISTS = 422

class FreshDeskObjects(object):
    """ Abstract class to wrap a type of freshdesk resource.
    """
    def __init__(self, client):
        self.client = client

    def api_endpoint(self, id=None):
        if id:
            return '/{}s/{}.json'.format(self.api_name, id)
        else:
            return '/{}s.json'.format(self.api_name)

    # CRUD methods

    def create(self, **kwargs):
        return self.client.req(
            requests.post, self.api_endpoint(), self.wrapper_name,
            **kwargs)

    def update(self, id, **kwargs):
        return self.client.req(
            requests.put, self.api_endpoint(id), self.wrapper_name,
            **kwargs)

    def delete(self, id):
        return self.client.req(
            requests.delete, self.api_endpoint(id), self.wrapper_name)

    def get(self, id):
        return self.client.req(
            requests.get, self.api_endpoint(id), self.wrapper_name,
            id=id)

    def get_list(self, **params):
        return self.client.req(
            requests.get, self.api_endpoint(), self.wrapper_name,
            params=params)


class FreshDeskContacts(FreshDeskObjects):
    """ http://freshdesk.com/api#user
    """
    api_name = 'contact'
    wrapper_name = 'user'

    # Status names (default is state=verified)
    DELETED    = 'deleted'
    ALL        = 'all'         # Do not include deleted accounts
    VERIFIED   = 'verified'
    UNVERIFIED = 'unverified'

    def create(self, name, email, **kwargs):
        return super(FreshDeskContacts, self).create(name=name, email=email, **kwargs)

    def create_or_enable(self, name, email, **kwargs):
        """ If creation fails because user exists in deleted state, restore it.

        Freshdesk do not allow two users with same email, even if the collision
        is with a deleted user.
        """
        try:
            return self.create(name=name, email=email)
        except FreshDeskClient.APIError as e:
            contacts = self.get_list(
                state='deleted', query='email is {}'.format(email))
            if ((len(contacts) > 0) and
                (e.resp.status_code == HTTP_ALREADY_EXISTS)):
                return self.update(contacts[0]['id'], name=name, deleted=False,
                                   **kwargs)
            else:
                raise


class FreshDeskCustomers(FreshDeskObjects):
    """ http://freshdesk.com/api#companies
    """
    api_name = 'customer'
    wrapper_name = api_name

    def create(self, name, **kwargs):
        return super(FreshDeskCustomers, self).create(name=name, **kwargs)


class FreshDeskClient(object):
    """ Simple wrapper arround freshdesk REST API

    This wrapper is non neutral, it includes Oasiswork policy about default
    settings.

        cli = FreshDeskClient('https://mycompany.freshdesk.com', 'qwertyui')
        cli.contacts.get_list(state='all')

    """
    class APIError(Exception):
        def __init__(self, resp):
            self.resp = resp
        def __str__(self):
            return 'HTTP {}: {}'.format(self.resp.status_code, self.resp.text)

    def __init__(self, url, key):
        """
        :param url  endpoint url, eg: https://yourcompany.freshdesk.com
        :param key  API key
        """
        self.url = url
        self.key = key
        self.last_resp = None

        # Resources types
        self.customers = FreshDeskCustomers(self)
        self.contacts = FreshDeskContacts(self)

    def req(self, func, path, resource_type, params={}, **kwargs):
        abs_url = urlparse.urljoin(self.url, path)

        if func in (requests.patch, requests.put, requests.post):
            req_attrs = {
                'data': json.dumps({resource_type: kwargs}),
                'headers': {'Content-Type': 'application/json'}
            }
        else:
            req_attrs = {}

        resp =  func(
            abs_url,
            auth=(self.key, '<PASSWORD>'),
            params=params,
            **req_attrs)
        self.last_resp = resp
        if resp.ok:
            # for those two types of requests, the output is empty
            if func in (requests.delete, requests.put):
                return resp.text
            else:
                # Either we get a list or a single object
                json_obj = resp.json()
                if isinstance(json_obj, (list, tuple)):
                    return [i[resource_type] for i in json_obj]
                else:
                    return json_obj[resource_type]
        else:
            raise self.APIError(resp)

{'fraction_non_alphanumeric': 0.11204481792717087, 'fraction_numerical': 0.0010773540185304892, 'mean_word_length': 2.0536184210526316, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 8, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2591423', 'n_tokens_mistral': 2733, 'n_tokens_neox': 2632, 'n_words': 681}