#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2009-2011, Nicolas Clairon
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the University of California, Berkeley nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import datetime
import logging
from copy import deepcopy
from mongokit.mongo_exceptions import EvalException
log = logging.getLogger(__name__)
import six
[docs]def totimestamp(value):
"""
convert a datetime into a float since epoch
"""
import calendar
return int(calendar.timegm(value.timetuple()) * 1000 + value.microsecond / 1000)
[docs]def fromtimestamp(epoch_date):
"""
convert a float since epoch to a datetime object
"""
seconds = float(epoch_date) / 1000.0
return datetime.datetime.utcfromtimestamp(seconds)
[docs]class i18nDotedDict(dict):
"""
Dot notation dictionary access with i18n support
"""
def __init__(self, dic, doc):
super(i18nDotedDict, self).__init__(dic)
self._doc = doc
def __setattr__(self, key, value):
from mongokit.schema_document import i18n
if key in self:
if isinstance(self[key], i18n):
self[key][self._doc._current_lang] = value
else:
self[key] = value
else:
dict.__setattr__(self, key, value)
def __getattr__(self, key):
from mongokit.schema_document import i18n
if key in self:
if isinstance(self[key], i18n):
if self._doc._current_lang not in self[key]:
return self[key].get(self._doc._fallback_lang)
return self[key][self._doc._current_lang]
return self[key]
else:
return super(i18nDotedDict, self).__getattribute__(key)
def __deepcopy__(self, memo={}):
obj = dict(self)
return deepcopy(obj, memo)
def __getstate__(self):
return self.__dict__
def __setstate__(self, d):
self.__dict__.update(d)
[docs]class DotedDict(dict):
"""
Dot notation dictionary access
"""
def __init__(self, doc=None, warning=False):
self._dot_notation_warning = warning
if doc is None:
doc = {}
super(DotedDict, self).__init__(doc)
self.__dotify_dict(self)
def __dotify_dict(self, doc):
for k, v in six.iteritems(doc):
if isinstance(v, dict):
doc[k] = DotedDict(v)
self.__dotify_dict(v)
def __setattr__(self, key, value):
if key in self:
self[key] = value
else:
# Check for '_' should be first, coz we want to set protected
# attrs in __init__ without recursion, i.e. _dot_notation_warning
if not key.startswith('_') and self._dot_notation_warning and \
key not in ['db', 'collection', 'versioning_collection', 'connection', 'fs']:
log.warning("dot notation: %s was not found in structure. Add it as attribute instead" % key)
dict.__setattr__(self, key, value)
def __getattr__(self, key):
if key in self:
return self[key]
else:
# kindof bulletproof
if key.startswith('_'):
return super(DotedDict, self).__getattribute__(key)
else:
raise AttributeError('Not such attribute {0}'.format(key))
def __deepcopy__(self, memo={}):
obj = dict(self)
return deepcopy(obj, memo)
def __getstate__(self):
return self.__dict__
def __setstate__(self, d):
self.__dict__.update(d)
[docs]class DotExpandedDict(dict):
"""
A special dictionary constructor that takes a dictionary in which the keys
may contain dots to specify inner dictionaries. It's confusing, but this
example should make sense.
>>> d = DotExpandedDict({'person.1.firstname': ['Simon'], \
'person.1.lastname': ['Willison'], \
'person.2.firstname': ['Adrian'], \
'person.2.lastname': ['Holovaty']})
>>> d
{'person': {'1': {'lastname': ['Willison'], 'firstname': ['Simon']},
'2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}}
>>> d['person']
{'1': {'lastname': ['Willison'], 'firstname': ['Simon']}, '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}
>>> d['person']['1']
{'lastname': ['Willison'], 'firstname': ['Simon']}
# Gotcha: Results are unpredictable if the dots are "uneven":
>>> DotExpandedDict({'c.1': 2, 'c.2': 3, 'c': 1})
{'c': 1}
"""
# code taken from Django source code http://code.djangoproject.com/
def __init__(self, key_to_list_mapping):
super(DotExpandedDict, self).__init__()
for k, v in key_to_list_mapping.items():
current = self
bits = k.split('.')
for bit in bits[:-1]:
if bit.startswith('$'):
try:
bit = eval(bit[1:])
except:
raise EvalException('%s is not a python type' % bit[:1])
current = current.setdefault(bit, {})
# Now assign value to current position
last_bit = bits[-1]
if last_bit.startswith('$'):
try:
last_bit = eval(last_bit[1:])
except:
raise EvalException('%s is not a python type' % last_bit)
try:
current[last_bit] = v
except TypeError: # Special-case if current isn't a dict.
current = {last_bit: v}
[docs]class DotCollapsedDict(dict):
"""
A special dictionary constructor that take a dict and provides
a dot collapsed dict:
>>> DotCollapsedDict({'a':{'b':{'c':{'d':3}, 'e':5}, "g":2}, 'f':6})
{'a.b.c.d': 3, 'a.b.e': 5, 'a.g': 2, 'f': 6}
>>> DotCollapsedDict({'bla':{'foo':{unicode:{"bla":3}}, 'bar':'egg'}})
{'bla.foo.$unicode.bla': 3, 'bla.bar': "egg"}
>>> DotCollapsedDict({'bla':{'foo':{unicode:{"bla":3}}, 'bar':'egg'}}, remove_under_type=True)
{'bla.foo':{}, 'bla.bar':unicode}
>>> dic = {'bar':{'foo':3}, 'bla':{'g':2, 'h':3}}
>>> DotCollapsedDict(dic, reference={'bar.foo':None, 'bla':{'g':None, 'h':None}})
{'bar.foo':3, 'bla':{'g':2, 'h':3}}
"""
def __init__(self, passed_dict, remove_under_type=False, reference=None):
super(DotCollapsedDict, self).__init__()
self._remove_under_type = remove_under_type
assert isinstance(passed_dict, dict), "you must pass a dict instance"
final_dict = {}
self._reference = reference
self._make_dotation(passed_dict, final_dict)
self.update(final_dict)
def _make_dotation(self, d, final_dict, key=""):
for k, v in six.iteritems(d):
if isinstance(k, type):
k = "$%s" % k.__name__
if isinstance(v, dict) and v != {}:
if key:
_key = "%s.%s" % (key, k)
else:
_key = k
if self._reference and _key in self._reference:
final_dict[_key] = v
if self._remove_under_type:
if [1 for i in v.keys() if isinstance(i, type)]:
v = v.__class__()
if not key:
final_dict[k] = v
else:
final_dict["%s.%s" % (key, k)] = v
else:
self._make_dotation(v, final_dict, _key)
else:
self._make_dotation(v, final_dict, _key)
else:
if not key:
final_dict[k] = v
else:
if not self._reference:
final_dict["%s.%s" % (key, k)] = v
elif "%s.%s" % (key, k) in self._reference:
final_dict["%s.%s" % (key, k)] = v
#else:
# final_dict[key] = {k: v}
# print "+++", {k:v}