<commit_before># -*- coding: utf-8 -*-
from urllib2 import Request, urlopen
import json

API_SETTINGS = {'api_key': '', 'api_point': ''}


class NovaPoshtaApi(object):
    """A base API class, that holds shared methods and settings for other models.
    Creates basic query object and provide `apiKey` and API endpoint configuration.
    """

    def __init__(self):
        """
        Creates basic configuration dictionary, used as API query template by models.
        Settings can be set through `API_SETTINGS` variable.
        """
        self.query = {} or dict()
        self.query['modelName'] = self.__class__.__name__
        self.query['methodProperties'] = {}
        self.query['apiKey'] = API_SETTINGS['api_key']
        if API_SETTINGS['api_point']:
            self.api_point = API_SETTINGS['api_point']
        else:
            self.api_point = 'https://api.novaposhta.ua/v2.0/json/'

    def send(self, method=None, method_props=None):
        """
        Primary method for API requests and data fetching.
        It uses `urllib2` and `json` libs for requests to API through `HTTP` protocol.
        Modifies API template and then makes request to API endpoint.

        :param method:
            name of the API method, should be passed for every request
        :type method:
            str or unicode
        :param method_props:
            additional params for API methods.
        :type method_props:
            dict
        :return:
            dictionary with fetched info
        :rtype:
            dict
        """
        self.query['calledMethod'] = method
        if method_props:
            self.query['methodProperties'] = method_props
        req = Request(self.api_point, json.dumps(self.query))
        response = json.load(urlopen(req))
        return response


class Address(NovaPoshtaApi):
    """A class representing the `Address` model of Nova Poshta API.
    Used for parsing `geodata` (like cities, streets etc.).
    """

    def get_city_ref(self, city):
        """
        Method for fetching `CityRef` (unique city hash) from API, required for other methods.

        :example:
            ``address = Address()``
            ``address.get_city_ref(city=u'Здолбунів')``
        :param city:
            name of the required city
        :type city:
            str or unicode
        :return:
            parsed `CityRef` value (hash)
        :rtype:
            unicode
        """
        city_ref = self.get_city_by_name(city=city)['data'][0]['Ref']
        return city_ref

    def get_cities(self):
        """
        Method for fetching info about all cities.

        :example:
            ``address = Address()``
            ``address.get_cities()``
        :return:
            dictionary with info about all cities
        :rtype:
            dict
        """
        req = self.send(method='getCities')
        return req

    def get_city_by_name(self, city):
        """
        Method for fetching info about city by city name.

        :example:
            ``address = Address()``
            ``address.get_city_by_name(city=u'Здолбунів')``
        :param city:
            name of the desired city
        :type city:
            str or unicode
        :return:
            dictionary with info about city
        :rtype:
            dict
        """
        req = self.send(method='getCities', method_props={'FindByString': city})
        return req

    def ex_get_streets(self, city):
        """
        Method for fetching all info about streets in provided city.
        Extended version of `get_streets` method.
        City name is used instead of ID.

        :example:
            ``address = Address()``
            ``address.get_streets(city=u'Здолбунів')``
        :param city:
            name of the desired city
        :type city:
            str or unicode
        :return:
            dictionary with info about streets
        :rtype:
            dict
        """
        city_ref = self.get_city_ref(city=city)
        req = self.send(method='getStreet', method_props={"CityRef": city_ref})
        return req

    def get_streets(self, city_ref):
        """
        Method for fetching info about streets in desired city.

        :example:
            ``address = Address()``
            ``address.get_streets(city_ref='0006560c-4079-11de-b509-001d92f78698')``
        :param city_ref:
            ID of the target city
        :type city_ref:
            str or unicode
        :return:
            dictionary with info about streets
        :rtype:
            dict
        """
        req = self.send(method='getStreet', method_props={"CityRef": city_ref})
        return req

    def ex_get_street_by_name(self, city, street):
        """
        Method for fetching info about specific street in desired city.
        Extended version of `get_street_by_name` method.
        City name is used instead of ID.

        :example:
            ``address = Address()``
            ``print address.ex_get_street_by_name(city=u'Здолбунів', street=u'Незалежності')``
        :param city:
            name of the target city
        :type city:
            str or unicode
        :param street:
            name of the street
        :type street:
            str or unicode
        :return:
            dictionary with info about street
        :rtype:
            dict
        """
        city_ref = self.get_city_ref(city=city)
        req = self.send(method='getStreet', method_props={'CityRef': city_ref, 'FindByString': street})
        return req

    def get_street_by_name(self, city_ref, street):
        """
        Method for fetching info about specific street in desired city.

        :example:
            ``address = Address()``
            ``address.get_street_by_name(city_ref='0006560c-4079-11de-b509-001d92f78698', street=u'Незалежності')``
        :param city_ref:
            ID of the target city
        :type city_ref:
            str or unicode
        :param street:
            name of the target street
        :type street:
            str or unicode
        :return:
            dictionary with info about street
        :rtype:
            dict
        """
        req = self.send(method='getStreet', method_props={"CityRef": city_ref, "FindByString": street})
        return req

    def ex_get_warehouses(self, city):
        """
        Method for fetching info about all warehouses in desired city.
        Extended version of `get_warehouses` method.
        City name is used instead of ID.

        :example:
            ``address = Address()``
            ``address.get_warehouses(city=u'Здолбунів')``
        :param city:
            name of the target city
        :type city:
            str or unicode
        :return:
            parsed dictionary with all info about warehouses
        :rtype:
            dict
        """
        city_ref = self.get_city_ref(city=city)
        req = self.send(method='getWarehouses', method_props={'CityRef': city_ref})
        return req

    def get_warehouses(self, city_ref):
        """
        Method for fetching info about all warehouses in desired city.

        :example:
            ``address = Address()``
            ``address.get_warehouses(city='0006560c-4079-11de-b509-001d92f78698')``
        :param city_ref:
            ID of the target city
        :type city_ref:
            str or unicode
        :return:
            parsed dictionary with all info about warehouses
        :rtype:
            dict
        """
        req = self.send(method='getWarehouses', method_props={"CityRef": city_ref})
        return req

    def get_warehouse_types(self):
        """
        Method for fetching info about warehouse's types.

        :example:
            ``address = Address()``
            ``address.get_warehouse_types()``
        :return:
            parsed dictionary with info about warehouse's types
        :rtype:
            dict
        """
        req = self.send(method='getWarehouseTypes')
        return req

    def get_areas(self):
        """
        Method for fetching info about areas geographical areas.

        :example:
            ``address = Address()``
            ``address.get_areas()``
        :return:
            parsed dictionary with info about areas
        :rtype:
            dict
        """
        req = self.send(method='getAreas')
        return req


class Counterparty(NovaPoshtaApi):
    """
    A class representing the `Counterparty` model of Nova Poshta API.
    Used for interact with counterpart's info.
    """

    def get_counterparties(self, cp_type='Sender'):
        """
        Method for fetching all information about counterparties.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparties(cp_type='Recipient')``
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparties
        :rtype:
            dict
        """
        req = self.send(method='getCounterparties', method_props={"CounterpartyProperty": cp_type})
        return req

    def get_counterparty_by_name(self, name, cp_type='Sender'):
        """
        Method for fetching info about counterparty by name.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparties(name=u'Талісман', cp_type='Recipient')``
        :param name:
            name of the desired counterparty
        :type name:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparty
        :rtype:
            dict
        """
        req = self.send(method='getCounterparties',
                        method_props={"CounterpartyProperty": cp_type, 'FindByString': name})
        return req

    def get_counterparty_ref(self, name, cp_type='Sender'):
        """
        Method for fetching `Ref` (unique counterparty hash) from API, required for other methods.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparty_ref(name=u'Талісман', cp_type='Recipient')``
        :param name:
            name of the desired counterparty
        :type name:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            ID of the counterparty
        :rtype:
            unicode
        """
        props = {'CounterpartyProperty': cp_type, 'FindByString': name}  # separate for a better look :)
        req = self.send(method='getCounterparties', method_props=props)['data'][0]['Ref']
        return req

    def ex_get_counterparty_by_edrpou(self, city, code):
        """
        Method for fetching info about counterparty by `EDRPOU` - National State Registry
        of Ukrainian Enterprises and Organizations (8-digit code).
        Extended version of `get_counterparty_by_edrpou` method.
        City name is used instead of ID.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.ex_get_counterparty_by_edrpou(city=u'Здолбунів', code='12345678')``
        :param city:
            name of the city of counterparty
        :type city:
            str or unicode
        :param code:
            EDRPOU code of the counterparty
        :type code:
            str or unicode
        :return:
            dictionary with info about counterparty
        :rtype:
            dict
        """
        city_ref = Address().get_city_by_name(city)['data'][0]['Ref']
        req = self.send(method='getCounterpartyByEDRPOU', method_props={"CityRef": city_ref, 'EDRPOU': code})
        return req

    def get_counterparty_by_edrpou(self, city_ref, code):
        """
        Method for fetching info about counterparty by `EDRPOU` - National State Registry
        of Ukrainian Enterprises and Organizations (8-digit code).

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparty_by_edrpou(city_ref=u'0006560c-4079-11de-b509-001d92f78698', code='12345678')``
        :param city_ref:
            ID of the city of counterparty
        :type city_ref:
            str or unicode
        :param code:
            EDRPOU code of the counterparty
        :type code:
            str or unicode
        :return:
            dictionary with info about counterparty
        :rtype:
            dict
        """
        req = self.send(method='getCounterpartyByEDRPOU', method_props={"CityRef": city_ref, 'EDRPOU': code})
        return req

    def ex_get_counterparty_addresses(self, name, cp_type='Sender'):
        """
        Method for fetching counterparty's addresses.
        Extended version of `get_counterparty_addresses` method.
        Counterparty's name is used instead of ID.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.ex_get_counterparty_addresses(u'Талісман', cp_type='Recipient')``
        :param name:
            name of the counterparty
        :type name:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparty's addresses
        """
        cp_ref = self.get_counterparty_ref(name=name, cp_type=cp_type)
        req = self.send(method='getCounterpartyAddresses',
                        method_props={'Ref': cp_ref, 'CounterpartyProperty': cp_type})
        return req

    def get_counterparty_addresses(self, cp_ref, cp_type='Sender'):
        """
        Method for fetching counterparty's addresses.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparty_addresses(u'f70f1bee-55fd-11e5-8d8d-005056887b8d', cp_type='Recipient')``
        :param cp_ref:
            ID of the counterparty
        :type cp_ref:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparty's addresses
        """
        req = self.send(method='getCounterpartyAddresses',
                        method_props={'Ref': cp_ref, 'CounterpartyProperty': cp_type})
        return req

    def ex_get_counterparty_contact_persons(self, name, cp_type='Sender'):
        """
        Method for fetching info about counterparty's contact persons.
        Extended version of `get_counterparty_contact_persons` method.
        Counterparty's name is used instead of ID (additionally, counterparty type should be used).

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.ex_get_counterparty_contact_persons(u'Талісман', cp_type='Recipient')``
        :param name:
            name of the counterparty
        :type name:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparty's contact persons
        """
        cp_ref = self.get_counterparty_ref(name=name, cp_type=cp_type)
        req = self.send(method='getCounterpartyContactPersons', method_props={'Ref': cp_ref})
        return req

    def get_counterparty_contact_persons(self, cp_ref):
        """
        Method for fetching info about counterparty's contact persons.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparty_contact_persons(u'f70f1bee-55fd-11e5-8d8d-005056887b8d')``
        :param cp_ref:
            name of the counterparty
        :type cp_ref:
            str or unicode
        :return:
            dictionary with info about counterparty's contact persons
        """
        req = self.send(method='getCounterpartyContactPersons', method_props={'Ref': cp_ref})
        return req

    def save(self, from_data=None, city_ref=None, first_name=None, mid_name=None, last_name=None, phone=None, email=None,
             cp_type=None, cp_prop=None):
        """
        Method for saving counterparty.
        It covers `save` method from API, so all method properties are used.
        This method is difficult to use because of a lot of params. It is here for `API exactly coverage`.

        :example:
            ``counterparty =Counterparty()``
            ``counterparty.save_counterparty(city_ref='db5c88d7-391c-11dd-90d9-001a92567626', first_name=u'Фелікс',``
                               ``mid_name=u'Едуардович', last_name=u'Ковальчук', phone=i'0937979489',
                               ``email='<EMAIL>', cp_type='PrivatePerson', cp_prop='Recipient')``

        :param city_ref:
            ID of the counterparty's city
        :type city_ref:
            str or unicode
        :param first_name:
            first name of the counterparty
        :type:
            str or unicode
        :param mid_name:
            middle name of the counterparty
        :type:
            str or unicode
        :param last_name:
            last name of the counterparty
        :type:
            str or unicode
        :param phone:
            phone number of the counterparty
        :type:
            str or unicode
        :param email:
            e-mail address of the counterparty
        :type:
            str or unicode
        :param cp_type:
            type of the counterparty (`PrivatePerson` etc.)
        :type:
            str or unicode
        :param cp_prop:
            counterparty property (can be either `Sender` or `Recipient`)
        :type:
            str or unicode
        :return:
            dictionary with info about saved counterparty
        :rtype:
            dict
        """
        props = {}
        if from_data:
            props = from_data
        else:
            if city_ref:
                props['CityRef'] = city_ref
            if first_name:
                props['FirstName'] = first_name
            if mid_name:
                props['MiddleName'] = mid_name
            if last_name:
                props['LastName'] = last_name
            if phone:
                props['Phone'] = phone
            if email:
                props['Email'] = email
            if cp_type:
                props['CounterpartyType'] = cp_type
            if cp_prop:
                props['CounterpartyProperty'] = cp_prop
        req = self.send(method='save', method_props=props)
        return req



    # # not tested yet, because of contract lack!
    # def save_third_person(self, city_ref=None, first_name=None, mid_name=None, last_name=None, phone=None, email=None,
    #                       cp_type=None, edrpou=None, own_form=None):
    #     """
    #     Method for saving third-party person.
    #     It covers `save` method from API, so all method properties are used.
    #     Just like `save_counterparty`, this method is difficult to use because of a lot of params.
    #     It is here for `API exactly coverage`
    #
    #     :example:
    #         ``counterparty =Counterparty()``
    #         ``counterparty.save_third_person(city_ref='db5c88d7-391c-11dd-90d9-001a92567626', first_name=u'Фелікс',``
    #                            ``mid_name=u'Едуардович', last_name=u'Ковальчук', phone=i'0937979489',
    #                            ``email='<EMAIL>', cp_type='PrivatePerson', cp_prop='Recipient')``
    #
    #     :param city_ref:
    #         ID of the counterparty's city
    #     :type city_ref:
    #         str or unicode
    #     :param first_name:
    #         first name of the counterparty
    #     :type:
    #         str or unicode
    #     :param mid_name:
    #         middle name of the counterparty
    #     :type:
    #         str or unicode
    #     :param last_name:
    #         last name of the counterparty
    #     :type:
    #         str or unicode
    #     :param phone:
    #         phone number of the counterparty
    #     :type:
    #         str or unicode
    #     :param email:
    #         e-mail address of the counterparty
    #     :type:
    #         str or unicode
    #     :param cp_type:
    #         type of the counterparty (`PrivatePerson` etc.)
    #     :type:
    #         str or unicode
    #     :type:
    #         str or unicode
    #     :return:
    #         dictionary with info about saved counterparty
    #     :rtype:
    #         dict
    #     """
    #     props = {'CounterpartyProperty': 'ThirdPerson'
    #                                      ''}
    #     if city_ref:
    #         props['CityRef'] = city_ref
    #     if first_name:
    #         props['FirstName'] = first_name
    #     if mid_name:
    #         props['MiddleName'] = mid_name
    #     if last_name:
    #         props['LastName'] = last_name
    #     if phone:
    #         props['Phone'] = phone
    #     if email:
    #         props['Email'] = email
    #     if cp_type:
    #         props['CounterpartyType'] = cp_type
    #     if edrpou:
    #         props['EDRPOU'] = edrpou
    #     if own_form:
    #         props['OwnershipForm'] = own_form
    #
    #     req = self.send(method='saveThirdPerson', method_props=props)
    #     print props
    #     return req








class Common(NovaPoshtaApi):
    """A class representing the `Common` model of Nova Poshta API.
    Used for parsing common (obviously) information, which represents different data (cargo, payment etc.).
    """

    def get_types_of_payers(self):
        """
        Method for fetching info about types of payers.

        :example:
            ``common = Common()``
            ``common.get_types_of_payers()``
        :return:
            dictionary with info about types of payers
        """<commit_msg>added update method, changes in save method, docs update<commit_after># -*- coding: utf-8 -*-
from urllib2 import Request, urlopen
import json

API_SETTINGS = {'api_key': '', 'api_point': ''}


class NovaPoshtaApi(object):
    """A base API class, that holds shared methods and settings for other models.
    Creates basic query object and provide `apiKey` and API endpoint configuration.
    """

    def __init__(self):
        """
        Creates basic configuration dictionary, used as API query template by models.
        Settings can be set through `API_SETTINGS` variable.
        """
        self.query = {} or dict()
        self.query['modelName'] = self.__class__.__name__
        self.query['methodProperties'] = {}
        self.query['apiKey'] = API_SETTINGS['api_key']
        if API_SETTINGS['api_point']:
            self.api_point = API_SETTINGS['api_point']
        else:
            self.api_point = 'https://api.novaposhta.ua/v2.0/json/'

    def send(self, method=None, method_props=None):
        """
        Primary method for API requests and data fetching.
        It uses `urllib2` and `json` libs for requests to API through `HTTP` protocol.
        Modifies API template and then makes request to API endpoint.

        :param method:
            name of the API method, should be passed for every request
        :type method:
            str or unicode
        :param method_props:
            additional params for API methods.
        :type method_props:
            dict
        :return:
            dictionary with fetched info
        :rtype:
            dict
        """
        self.query['calledMethod'] = method
        if method_props:
            self.query['methodProperties'] = method_props
        req = Request(self.api_point, json.dumps(self.query))
        response = json.load(urlopen(req))
        return response


class Address(NovaPoshtaApi):
    """A class representing the `Address` model of Nova Poshta API.
    Used for parsing `geodata` (like cities, streets etc.).
    """

    def get_city_ref(self, city):
        """
        Method for fetching `CityRef` (unique city hash) from API, required for other methods.

        :example:
            ``address = Address()``
            ``address.get_city_ref(city=u'Здолбунів')``
        :param city:
            name of the required city
        :type city:
            str or unicode
        :return:
            parsed `CityRef` value (hash)
        :rtype:
            unicode
        """
        city_ref = self.get_city_by_name(city=city)['data'][0]['Ref']
        return city_ref

    def get_cities(self):
        """
        Method for fetching info about all cities.

        :example:
            ``address = Address()``
            ``address.get_cities()``
        :return:
            dictionary with info about all cities
        :rtype:
            dict
        """
        req = self.send(method='getCities')
        return req

    def get_city_by_name(self, city):
        """
        Method for fetching info about city by city name.

        :example:
            ``address = Address()``
            ``address.get_city_by_name(city=u'Здолбунів')``
        :param city:
            name of the desired city
        :type city:
            str or unicode
        :return:
            dictionary with info about city
        :rtype:
            dict
        """
        req = self.send(method='getCities', method_props={'FindByString': city})
        return req

    def ex_get_streets(self, city):
        """
        Method for fetching all info about streets in provided city.
        Extended version of `get_streets` method.
        City name is used instead of ID.

        :example:
            ``address = Address()``
            ``address.get_streets(city=u'Здолбунів')``
        :param city:
            name of the desired city
        :type city:
            str or unicode
        :return:
            dictionary with info about streets
        :rtype:
            dict
        """
        city_ref = self.get_city_ref(city=city)
        req = self.send(method='getStreet', method_props={"CityRef": city_ref})
        return req

    def get_streets(self, city_ref):
        """
        Method for fetching info about streets in desired city.

        :example:
            ``address = Address()``
            ``address.get_streets(city_ref='0006560c-4079-11de-b509-001d92f78698')``
        :param city_ref:
            ID of the target city
        :type city_ref:
            str or unicode
        :return:
            dictionary with info about streets
        :rtype:
            dict
        """
        req = self.send(method='getStreet', method_props={"CityRef": city_ref})
        return req

    def ex_get_street_by_name(self, city, street):
        """
        Method for fetching info about specific street in desired city.
        Extended version of `get_street_by_name` method.
        City name is used instead of ID.

        :example:
            ``address = Address()``
            ``print address.ex_get_street_by_name(city=u'Здолбунів', street=u'Незалежності')``
        :param city:
            name of the target city
        :type city:
            str or unicode
        :param street:
            name of the street
        :type street:
            str or unicode
        :return:
            dictionary with info about street
        :rtype:
            dict
        """
        city_ref = self.get_city_ref(city=city)
        req = self.send(method='getStreet', method_props={'CityRef': city_ref, 'FindByString': street})
        return req

    def get_street_by_name(self, city_ref, street):
        """
        Method for fetching info about specific street in desired city.

        :example:
            ``address = Address()``
            ``address.get_street_by_name(city_ref='0006560c-4079-11de-b509-001d92f78698', street=u'Незалежності')``
        :param city_ref:
            ID of the target city
        :type city_ref:
            str or unicode
        :param street:
            name of the target street
        :type street:
            str or unicode
        :return:
            dictionary with info about street
        :rtype:
            dict
        """
        req = self.send(method='getStreet', method_props={"CityRef": city_ref, "FindByString": street})
        return req

    def ex_get_warehouses(self, city):
        """
        Method for fetching info about all warehouses in desired city.
        Extended version of `get_warehouses` method.
        City name is used instead of ID.

        :example:
            ``address = Address()``
            ``address.get_warehouses(city=u'Здолбунів')``
        :param city:
            name of the target city
        :type city:
            str or unicode
        :return:
            parsed dictionary with all info about warehouses
        :rtype:
            dict
        """
        city_ref = self.get_city_ref(city=city)
        req = self.send(method='getWarehouses', method_props={'CityRef': city_ref})
        return req

    def get_warehouses(self, city_ref):
        """
        Method for fetching info about all warehouses in desired city.

        :example:
            ``address = Address()``
            ``address.get_warehouses(city='0006560c-4079-11de-b509-001d92f78698')``
        :param city_ref:
            ID of the target city
        :type city_ref:
            str or unicode
        :return:
            parsed dictionary with all info about warehouses
        :rtype:
            dict
        """
        req = self.send(method='getWarehouses', method_props={"CityRef": city_ref})
        return req

    def get_warehouse_types(self):
        """
        Method for fetching info about warehouse's types.

        :example:
            ``address = Address()``
            ``address.get_warehouse_types()``
        :return:
            parsed dictionary with info about warehouse's types
        :rtype:
            dict
        """
        req = self.send(method='getWarehouseTypes')
        return req

    def get_areas(self):
        """
        Method for fetching info about areas geographical areas.

        :example:
            ``address = Address()``
            ``address.get_areas()``
        :return:
            parsed dictionary with info about areas
        :rtype:
            dict
        """
        req = self.send(method='getAreas')
        return req


class Counterparty(NovaPoshtaApi):
    """
    A class representing the `Counterparty` model of Nova Poshta API.
    Used for interact with counterpart's info.
    """

    def get_counterparties(self, cp_type='Sender'):
        """
        Method for fetching all information about counterparties.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparties(cp_type='Recipient')``
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparties
        :rtype:
            dict
        """
        req = self.send(method='getCounterparties', method_props={"CounterpartyProperty": cp_type})
        return req

    def get_counterparty_by_name(self, name, cp_type='Sender'):
        """
        Method for fetching info about counterparty by name.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparties(name=u'Талісман', cp_type='Recipient')``
        :param name:
            name of the desired counterparty
        :type name:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparty
        :rtype:
            dict
        """
        req = self.send(method='getCounterparties',
                        method_props={"CounterpartyProperty": cp_type, 'FindByString': name})
        return req

    def get_counterparty_ref(self, name, cp_type='Sender'):
        """
        Method for fetching `Ref` (unique counterparty hash) from API, required for other methods.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparty_ref(name=u'Талісман', cp_type='Recipient')``
        :param name:
            name of the desired counterparty
        :type name:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            ID of the counterparty
        :rtype:
            unicode
        """
        props = {'CounterpartyProperty': cp_type, 'FindByString': name}  # separate for a better look :)
        req = self.send(method='getCounterparties', method_props=props)['data'][0]['Ref']
        return req

    def ex_get_counterparty_by_edrpou(self, city, code):
        """
        Method for fetching info about counterparty by `EDRPOU` - National State Registry
        of Ukrainian Enterprises and Organizations (8-digit code).
        Extended version of `get_counterparty_by_edrpou` method.
        City name is used instead of ID.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.ex_get_counterparty_by_edrpou(city=u'Здолбунів', code='12345678')``
        :param city:
            name of the city of counterparty
        :type city:
            str or unicode
        :param code:
            EDRPOU code of the counterparty
        :type code:
            str or unicode
        :return:
            dictionary with info about counterparty
        :rtype:
            dict
        """
        city_ref = Address().get_city_by_name(city)['data'][0]['Ref']
        req = self.send(method='getCounterpartyByEDRPOU', method_props={"CityRef": city_ref, 'EDRPOU': code})
        return req

    def get_counterparty_by_edrpou(self, city_ref, code):
        """
        Method for fetching info about counterparty by `EDRPOU` - National State Registry
        of Ukrainian Enterprises and Organizations (8-digit code).

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparty_by_edrpou(city_ref=u'0006560c-4079-11de-b509-001d92f78698', code='12345678')``
        :param city_ref:
            ID of the city of counterparty
        :type city_ref:
            str or unicode
        :param code:
            EDRPOU code of the counterparty
        :type code:
            str or unicode
        :return:
            dictionary with info about counterparty
        :rtype:
            dict
        """
        req = self.send(method='getCounterpartyByEDRPOU', method_props={"CityRef": city_ref, 'EDRPOU': code})
        return req

    def ex_get_counterparty_addresses(self, name, cp_type='Sender'):
        """
        Method for fetching counterparty's addresses.
        Extended version of `get_counterparty_addresses` method.
        Counterparty's name is used instead of ID.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.ex_get_counterparty_addresses(u'Талісман', cp_type='Recipient')``
        :param name:
            name of the counterparty
        :type name:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparty's addresses
        """
        cp_ref = self.get_counterparty_ref(name=name, cp_type=cp_type)
        req = self.send(method='getCounterpartyAddresses',
                        method_props={'Ref': cp_ref, 'CounterpartyProperty': cp_type})
        return req

    def get_counterparty_addresses(self, cp_ref, cp_type='Sender'):
        """
        Method for fetching counterparty's addresses.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparty_addresses(u'f70f1bee-55fd-11e5-8d8d-005056887b8d', cp_type='Recipient')``
        :param cp_ref:
            ID of the counterparty
        :type cp_ref:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparty's addresses
        """
        req = self.send(method='getCounterpartyAddresses',
                        method_props={'Ref': cp_ref, 'CounterpartyProperty': cp_type})
        return req

    def ex_get_counterparty_contact_persons(self, name, cp_type='Sender'):
        """
        Method for fetching info about counterparty's contact persons.
        Extended version of `get_counterparty_contact_persons` method.
        Counterparty's name is used instead of ID (additionally, counterparty type should be used).

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.ex_get_counterparty_contact_persons(u'Талісман', cp_type='Recipient')``
        :param name:
            name of the counterparty
        :type name:
            str or unicode
        :param cp_type:
            type of the counterparty: can be either `Sender` or `Recipient` (`Sender` used as default)
        :type cp_type:
            str or unicode
        :return:
            dictionary with info about counterparty's contact persons
        """
        cp_ref = self.get_counterparty_ref(name=name, cp_type=cp_type)
        req = self.send(method='getCounterpartyContactPersons', method_props={'Ref': cp_ref})
        return req

    def get_counterparty_contact_persons(self, cp_ref):
        """
        Method for fetching info about counterparty's contact persons.

        :example:
            ``counterparty = Counterparty()``
            ``counterparty.get_counterparty_contact_persons(u'f70f1bee-55fd-11e5-8d8d-005056887b8d')``
        :param cp_ref:
            name of the counterparty
        :type cp_ref:
            str or unicode
        :return:
            dictionary with info about counterparty's contact persons
        """
        req = self.send(method='getCounterpartyContactPersons', method_props={'Ref': cp_ref})
        return req

    def save(self,                                                                               # TODO: Default values!
             from_data=None,
             city_ref=None,
             first_name=None,
             mid_name=None,
             last_name=None,
             phone=None, email=None,
             cp_type=None,
             cp_prop=None):
        """
        Method for saving counterparty.
        Named arguments can be used or it is possible to save pre-parsed dictionary with counterparty info.

        :example:
            >>> counterparty = Counterparty()
            >>> counterparty.save(city_ref='db5c88d7-391c-11dd-90d9-001a92567626', first_name=u'Фелікс',
            ... mid_name=u'Едуардович', last_name=u'Ковальчук', phone='0937979489',
            ... email='<EMAIL>', cp_type='PrivatePerson', cp_prop='Recipient')
            or:
            >>> counterparty = Counterparty()
            >>> data = {
            ... 'CityRef' : 'db5c88d7-391c-11dd-90d9-001a92567626',
            ... 'CounterpartyProperty' : 'Recipient',
            ... 'CounterpartyType' : 'PrivatePerson',
            ... 'Email' : '',
            ... 'FirstName' : 'Андрій',
            ... 'LastName' : 'Яковлєв',
            ... 'MiddleName' : 'Адуардович',
            ... 'Phone' : '0997979789' }
            >>> counterparty.save(from_data=data)


        :param city_ref:
            ID of the counterparty's city
        :type city_ref:
            str or unicode
        :param first_name:
            first name of the counterparty
        :type:
            str or unicode
        :param mid_name:
            middle name of the counterparty
        :type:
            str or unicode
        :param last_name:
            last name of the counterparty
        :type:
            str or unicode
        :param phone:
            phone number of the counterparty
        :type:
            str or unicode
        :param email:
            e-mail address of the counterparty
        :type:
            str or unicode
        :param cp_type:
            type of the counterparty (`PrivatePerson` etc.)
        :type:
            str or unicode
        :param cp_prop:
            counterparty property (can be either `Sender` or `Recipient`)
        :type:
            str or unicode
        :return:
            dictionary with info about saved counterparty
        :rtype:
            dict
        """
        props = {}
        if from_data:
            props = from_data
        else:
            if city_ref:
                props['CityRef'] = city_ref
            if first_name:
                props['FirstName'] = first_name
            if mid_name:
                props['MiddleName'] = mid_name
            if last_name:
                props['LastName'] = last_name
            if phone:
                props['Phone'] = phone
            if email:
                props['Email'] = email
            if cp_type:
                props['CounterpartyType'] = cp_type
            if cp_prop:
                props['CounterpartyProperty'] = cp_prop
        req = self.send(method='save', method_props=props)
        return req

    # TODO: API requires all fields to be passed. Maybe we can pre-fetch data from API and use if no need to update it
    def update(self,
               from_data=None,
               cp_ref=None,
               city_ref=None,
               first_name=None,
               mid_name=None,
               last_name=None,
               phone=None,
               email=None,
               cp_type=None,
               cp_prop=None,
               own_form=None):
        """
        Method for updating counterparty. All fields are required
        Named arguments can be used or it is possible to update pre-parsed dictionary with counterparty info.

        :example:
            >>> counterparty = Counterparty()
            >>> counterparty.update(ref='db5c88d7-391c-11dd-90d9-001a92567626',
            ... city_ref='db5c88d7-391c-11dd-90d9-001a92567626', first_name=u'Фелікс',
            ... mid_name=u'Едуардович', last_name=u'Ковальчук', phone='0937979489',
            ... email='<EMAIL>', cp_type='PrivatePerson', cp_prop='Recipient', own_form='')
            or:
            >>> counterparty = Counterparty()
            >>> data = {
            ... 'Ref': 'db5c88d7-391c-11dd-90d9-001a92567626',
            ... 'CityRef' : 'db5c88d7-391c-11dd-90d9-001a92567626',
            ... 'CounterpartyProperty' : 'Recipient',
            ... 'CounterpartyType' : 'PrivatePerson',
            ... 'Email' : '',
            ... 'FirstName' : 'Андрій',
            ... 'LastName' : 'Яковлєв',
            ... 'MiddleName' : 'Едуардович',
            ... 'Phone' : '0997979789',
            ... 'OwnershipForm': '' }
            >>> counterparty.update(from_data=data)


        :param city_ref:
            ID of the counterparty's city
        :type city_ref:
            str or unicode
        :param first_name:
            first name of the counterparty
        :type:
            str or unicode
        :param mid_name:
            middle name of the counterparty
        :type:
            str or unicode
        :param last_name:
            last name of the counterparty
        :type:
            str or unicode
        :param phone:
            phone number of the counterparty
        :type:
            str or unicode
        :param email:
            e-mail address of the counterparty
        :type:
            str or unicode
        :param cp_type:
            type of the counterparty (`PrivatePerson` etc.)
        :type:
            str or unicode
        :param cp_prop:
            counterparty property (can be either `Sender` or `Recipient`)
        :type:
            str or unicode
        :param own_form:
            ownership form of the counterparty.
            if needed, `get_ownership_forms_list`method from Common class can be used to get possible values
        :type own_form:
            str or unicode
        :return:
            dictionary with info about saved counterparty
        :rtype:
            dict
        """
        props = {}
        if from_data:
            props = from_data
        else:
            if cp_ref:
                props['Ref'] = cp_ref
            if city_ref:
                props['CityRef'] = city_ref
            if first_name:
                props['FirstName'] = first_name
            if mid_name:
                props['MiddleName'] = mid_name
            if last_name:
                props['LastName'] = last_name
            if phone:
                props['Phone'] = phone
            if email:
                props['Email'] = email
            if cp_type:
                props['CounterpartyType'] = cp_type
            if cp_prop:
                props['CounterpartyProperty'] = cp_prop
            if own_form:
                props['OwnershipForm'] = own_form
        req = self.send(method='update', method_props=props)
        return req

    def save_third_person(self):
        """Not implemented due to contract lack, will be here in the future. Maybe :)"""
        return False


class Common(NovaPoshtaApi):
    """A class representing the `Common` model of Nova Poshta API.
    Used for parsing common (obviously) information, which represents different data (cargo, payment etc.).
    """

    def get_types_of_payers(self):
        """
        Method for fetching info about types of payers.

        :example:
            ``common = Common()``
            ``common.get_types_of_payers()``
        :return:
            dictionary with info about types of payers
        """
{'fraction_non_alphanumeric': 0.09263632460283383, 'fraction_numerical': 0.012537569772434521, 'mean_word_length': 1.8415177209784663, 'pattern_counts': {'":': 18, '<': 7, '<?xml version=': 0, '>': 37, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '727856', 'n_tokens_mistral': 13302, 'n_tokens_neox': 12636, 'n_words': 3607}