patent_client.util — Patent Client 0.1.4 documentation

# Source code for patent_client.util

    from collections import OrderedDict
    from hashlib import md5
    import json
    from importlib import import_module
    from copy import deepcopy
    from dateutil.parser import parse as parse_dt
    import inflection

    def hash_dict(dictionary):
        return md5(json.dumps(dictionary, sort_keys=True).encode('utf-8')).hexdigest()

    FILTERS = {
        #'exact',
        #'iexact',
        #'contains',
        #'icontains',
        'in': lambda x, y: x in y,
        'eq': lambda x, y: x == y,
        #'gt',
        #'gte',
        #'lt',
        #'lte',
        #'startswith',
        #'istartswith',
        #'endswith',
        #'iendswith',
        #'range',
        #'date',
        #'year','
        #'month',
        #'day',
        #'week',
        #'week_day',
        #'quarter','
        #'time',
        #'hour','
        #'minute',
        #'second',
        #'isnull',
        #'regex',
        #'iregex',
    }

    def one_to_one(class_name, **mapping):
        module_name, class_name = class_name.rsplit('.', 1)
        @property
        def get(self):
            klass = getattr(import_module(module_name), class_name)
            filter_obj = {k: getattr(self, v) for (k, v) in mapping.items()}
            return klass.objects.get(**filter_obj)
        return get

    def one_to_many(class_name, **mapping):
        module_name, class_name = class_name.rsplit('.', 1)
        @property
        def get(self):
            klass = getattr(import_module(module_name), class_name)
            filter_obj = {k: getattr(self, v) for (k, v) in mapping.items()}
            return klass.objects.filter(**filter_obj)
        return get

    def recur_accessor(obj, accessor):
        if '__' not in accessor:
            a = accessor
            rest = None
        else:
            a, rest = accessor.split('__', 1)

        if hasattr(obj, a):
            new_obj = getattr(obj, a)
            if callable(new_obj):
                new_obj = new_obj()
        else:
            try:
                a = int(a)
            except ValueError:
                pass
            try:
                new_obj = obj[a]
            except (KeyError, TypeError):
                new_obj=None
        if not rest:
            return new_obj
        else:
            return recur_accessor(new_obj, rest)

    class Model(object):
        def __init__(self, data):
            self.data = {inflection.underscore(k):v for (k,v) in data.items()}
            for k, v in self.data.items():
                try:
                    if 'datetime' in k and type(v) == str:
                        self.data[k] = parse_dt(v)
                    elif 'date' in k and type(v) == str:
                        self.data[k] = parse_dt(v).date()
                except ValueError: # Malformed datetimes:
                    self.data[k] = None
                setattr(self, k, self.data[k])

    class Manager:
        def __init__(self, *args, **kwargs):
            """Simply store the keyword arguments"""
            self.args = args
            self.kwargs = kwargs

        def filter(self, *args, **kwargs):
            """Return a new Manager with a combination of previous and new keyword arguments"""
            return self.__class__(*args, *self.args, **{**kwargs, **self.kwargs})

            """ Next step would be to implement application-layer filters
            for key, value in kwargs.items():
                selector, operator = key.rsplit('__', 1)
                if operator not in FILTERS:
                    operator = 'eq'
                    accessor = key
                selector_lambda = lambda x: accessor(x, accessor)
                self.objs = filter(lambda x: FILTERS[operator](selector_lambda(x), value), self.objs)
            """

        def order_by(self, *args):
            """Take arguments, and store in a special keyword argument called 'sort' """
            kwargs = deepcopy(self.kwargs)
            if 'sort' not in kwargs:
                kwargs['sort'] = list()
            kwargs['sort'] += args
            return self.__class__(*self.args, **{**kwargs, **self.kwargs})

        def get(self, *args, **kwargs):
            """Implement a new manager with the requested keywords, and if the length is 1,
            return that record, else raise an exception"""
            args = [a for a in args + self.args if a]
            manager = self.__class__().filter(*args, **{**self.kwargs, **kwargs})
            if len(manager) > 1:
                doc_nos = '\n'.join([str(r) for r in manager])
                raise ValueError('More than one document found!\n' + doc_nos)
            return manager.first()

        def exclude(self, **kwargs):
            raise NotImplemented(f'{self.__class__} has no exclude method')

        def count(self):
            return len(self)

        def __len__(self):
            raise NotImplemented(f'{self.__class__} has no length method')

        def first(self):
            return self[0]

        def all(self):
            return iter(self)

        def values(self, *fields):
            """Return new manager with special keywords 'values__fields' and 'values__list'"""
            return self.__class__(
            *self.args,
            **{
                **self.kwargs,
                **dict(
                    values__fields=fields,
                    values__list=False)}
            )

        def values_list(self, *fields, flat=False):
            """Same as values, but adds an additional parameter for "flat" lists """
            return self.__class__(
            *self.args,
            **{
                **self.kwargs,
                **dict(
                    values__fields=fields,
                    values__list=True,
                    values__flat=flat,
                    )}
            )
            self.kwargs['values__fields'] = fields
            return ValuesSet(self, *fields, values_list=True, flat=flat)

        def __getitem__(self, key):
            """resolves slices and keys into Model objects. Relies on .get_item(key) to obtain
            the record itself"""

            if type(key) == slice:
                indices = list(range(len(self)))[key.start : key.stop : key.step]
                return [self.__getitem__(index) for index in indices]
            else:
                obj = self.get_item(key)
                if 'values__fields' in self.kwargs:
                    data = obj.data
                    fdata = OrderedDict()
                    for k in self.kwargs['values__fields']:
                        key = k.replace('__', '_')
                        value = recur_accessor(obj, k)
                        fdata[key] = value
                    data = fdata
                    if self.kwargs.get('values__list', False):
                        data = tuple(data[k] for k, v in data.items())
                        if len(self.kwargs['values__fields']) == 1 and self.kwargs['values__flat']:
                            data = data[0]
                    return data
                else:
                    return obj