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])


[docs]class Manager:
[docs] def __init__(self, *args, **kwargs): """Simply store the keyword arguments""" self.args = args self.kwargs = kwargs
[docs] 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) """
[docs] 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})
[docs] 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)
[docs] 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)} )
[docs] 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)
[docs] 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