843 lines
24 KiB
Python
Executable File
843 lines
24 KiB
Python
Executable File
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
import datetime
|
|
import json
|
|
from decimal import Decimal
|
|
from functools import reduce
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
|
|
class PYSON(object):
|
|
_operator = None
|
|
_binary_operator = None
|
|
|
|
def pyson(self):
|
|
raise NotImplementedError
|
|
|
|
def types(self):
|
|
raise NotImplementedError
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
raise NotImplementedError
|
|
|
|
def __invert__(self):
|
|
if self.types() != {bool}:
|
|
return Not(Bool(self))
|
|
else:
|
|
return Not(self)
|
|
|
|
def __and__(self, other):
|
|
if (isinstance(other, PYSON)
|
|
and other.types() != {bool}):
|
|
other = Bool(other)
|
|
if (isinstance(self, And)
|
|
and not isinstance(self, Or)):
|
|
return And(*self._statements, other)
|
|
if self.types() != {bool}:
|
|
return And(Bool(self), other)
|
|
else:
|
|
return And(self, other)
|
|
|
|
__rand__ = __and__
|
|
|
|
def __or__(self, other):
|
|
if (isinstance(other, PYSON)
|
|
and other.types() != {bool}):
|
|
other = Bool(other)
|
|
if isinstance(self, Or):
|
|
return Or(*self._statements, other)
|
|
if self.types() != {bool}:
|
|
return Or(Bool(self), other)
|
|
else:
|
|
return Or(self, other)
|
|
|
|
__ror__ = __or__
|
|
|
|
def __eq__(self, other):
|
|
return Equal(self, other)
|
|
|
|
def __ne__(self, other):
|
|
return Not(Equal(self, other))
|
|
|
|
def __gt__(self, other):
|
|
return Greater(self, other)
|
|
|
|
def __ge__(self, other):
|
|
return Greater(self, other, True)
|
|
|
|
def __lt__(self, other):
|
|
return Less(self, other)
|
|
|
|
def __le__(self, other):
|
|
return Less(self, other, True)
|
|
|
|
def get(self, k, d=''):
|
|
return Get(self, k, d)
|
|
|
|
def in_(self, obj):
|
|
return In(self, obj)
|
|
|
|
def contains(self, k):
|
|
return In(k, self)
|
|
|
|
def __repr__(self):
|
|
params = self.__repr_params__
|
|
if self._operator and isinstance(params[0], PYSON):
|
|
return '%s.%s(%s)' % (
|
|
repr(params[0]), self._operator,
|
|
', '.join(map(repr, params[1:])))
|
|
elif self._binary_operator and isinstance(params[0], PYSON):
|
|
return '(%s %s %s)' % (
|
|
repr(params[0]), self._binary_operator, repr(params[1]))
|
|
else:
|
|
klass = self.__class__.__name__
|
|
return '%s(%s)' % (klass, ', '.join(map(repr, params)))
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return NotImplementedError
|
|
|
|
|
|
class PYSONEncoder(json.JSONEncoder):
|
|
|
|
def default(self, obj):
|
|
if isinstance(obj, PYSON):
|
|
return obj.pyson()
|
|
elif isinstance(obj, datetime.date):
|
|
if isinstance(obj, datetime.datetime):
|
|
return DateTime(obj.year, obj.month, obj.day,
|
|
obj.hour, obj.minute, obj.second, obj.microsecond
|
|
).pyson()
|
|
else:
|
|
return Date(obj.year, obj.month, obj.day).pyson()
|
|
elif isinstance(obj, datetime.timedelta):
|
|
return TimeDelta(obj.days, obj.seconds, obj.microseconds)
|
|
elif isinstance(obj, Decimal):
|
|
return float(obj)
|
|
return super(PYSONEncoder, self).default(obj)
|
|
|
|
|
|
class PYSONDecoder(json.JSONDecoder):
|
|
|
|
def __init__(self, context=None, noeval=False):
|
|
self.__context = context or {}
|
|
self.noeval = noeval
|
|
super(PYSONDecoder, self).__init__(object_hook=self._object_hook)
|
|
|
|
def _object_hook(self, dct):
|
|
if '__class__' in dct:
|
|
klass = CONTEXT.get(dct['__class__'])
|
|
if klass:
|
|
if not self.noeval:
|
|
return klass.eval(dct, self.__context)
|
|
else:
|
|
dct = dct.copy()
|
|
del dct['__class__']
|
|
return klass(**dct)
|
|
return dct
|
|
|
|
|
|
class Eval(PYSON):
|
|
|
|
def __init__(self, v, d=''):
|
|
super(Eval, self).__init__()
|
|
self._value = v
|
|
self._default = d
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
params = (self._value,)
|
|
if self._default != '':
|
|
params += (self._default,)
|
|
return params
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'Eval',
|
|
'v': self._value,
|
|
'd': self._default,
|
|
}
|
|
|
|
def types(self):
|
|
if isinstance(self._default, PYSON):
|
|
return self._default.types()
|
|
else:
|
|
return {type(self._default)}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
if '.' in dct['v'] and dct['v'] not in context:
|
|
base, name = dct['v'].split('.', 1)
|
|
return Eval.eval({
|
|
'v': name,
|
|
'd': dct['d'],
|
|
}, context.get(base) or {})
|
|
return context.get(dct['v'], dct['d'])
|
|
|
|
@property
|
|
def basename(self):
|
|
name = self._value
|
|
if name.startswith('_parent_'):
|
|
name = name[len('_parent_'):]
|
|
if '.' in name:
|
|
name = name.split('.', 1)[0]
|
|
return name
|
|
|
|
|
|
class Not(PYSON):
|
|
|
|
def __init__(self, v):
|
|
super(Not, self).__init__()
|
|
if isinstance(v, PYSON):
|
|
if v.types() != {bool}:
|
|
v = Bool(v)
|
|
elif not isinstance(v, bool):
|
|
v = bool(v)
|
|
self._value = v
|
|
|
|
def __repr__(self):
|
|
if (isinstance(self._value, Equal)
|
|
and isinstance(self._value._statement1, PYSON)):
|
|
return '(%s != %s)' % (
|
|
repr(self._value._statement1),
|
|
repr(self._value._statement2))
|
|
elif isinstance(self._value, PYSON):
|
|
return '~%s' % repr(self._value)
|
|
else:
|
|
return super().__repr__()
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return (self._value,)
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'Not',
|
|
'v': self._value,
|
|
}
|
|
|
|
def types(self):
|
|
return {bool}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
return not Bool(dct['v']).eval(dct, context)
|
|
|
|
|
|
class Bool(PYSON):
|
|
|
|
def __init__(self, v):
|
|
super(Bool, self).__init__()
|
|
self._value = v
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return (self._value,)
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'Bool',
|
|
'v': self._value,
|
|
}
|
|
|
|
def types(self):
|
|
return {bool}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
return bool(dct['v'])
|
|
|
|
|
|
class And(PYSON):
|
|
_pyson_class = 'And'
|
|
_binary_operator = '&'
|
|
|
|
def __init__(self, *statements, **kwargs):
|
|
super(And, self).__init__()
|
|
statements = list(statements) + kwargs.get('s', [])
|
|
for i, statement in enumerate(list(statements)):
|
|
if isinstance(statement, PYSON):
|
|
if statement.types() != {bool}:
|
|
statements[i] = Bool(statement)
|
|
elif not isinstance(statement, bool):
|
|
statements[i] = bool(statement)
|
|
assert len(statements) >= 2, 'must have at least 2 statements'
|
|
self._statements = statements
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return tuple(self._statements)
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'And',
|
|
's': self._statements,
|
|
}
|
|
|
|
def types(self):
|
|
return {bool}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
return bool(reduce(lambda x, y: x and y, dct['s']))
|
|
|
|
|
|
class Or(And):
|
|
_binary_operator = '|'
|
|
|
|
def pyson(self):
|
|
res = super(Or, self).pyson()
|
|
res['__class__'] = 'Or'
|
|
return res
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
return bool(reduce(lambda x, y: x or y, dct['s']))
|
|
|
|
|
|
class Equal(PYSON):
|
|
_binary_operator = '=='
|
|
|
|
def __init__(self, s1, s2):
|
|
statement1, statement2 = s1, s2
|
|
super(Equal, self).__init__()
|
|
if isinstance(statement1, PYSON):
|
|
types1 = statement1.types()
|
|
else:
|
|
types1 = {type(s1)}
|
|
if isinstance(statement2, PYSON):
|
|
types2 = statement2.types()
|
|
else:
|
|
types2 = {type(s2)}
|
|
assert types1 == types2, 'statements must have the same type'
|
|
self._statement1 = statement1
|
|
self._statement2 = statement2
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return (self._statement1, self._statement2)
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'Equal',
|
|
's1': self._statement1,
|
|
's2': self._statement2,
|
|
}
|
|
|
|
def types(self):
|
|
return {bool}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
return dct['s1'] == dct['s2']
|
|
|
|
|
|
class Greater(PYSON):
|
|
|
|
def __init__(self, s1, s2, e=False):
|
|
statement1, statement2, equal = s1, s2, e
|
|
super(Greater, self).__init__()
|
|
for i in (statement1, statement2):
|
|
if isinstance(i, PYSON):
|
|
assert i.types().issubset({
|
|
int, float, type(None),
|
|
datetime.datetime, datetime.date,
|
|
datetime.timedelta}), \
|
|
'statement must be an integer, float, date or datetime'
|
|
else:
|
|
assert isinstance(i, (
|
|
int, float, type(None),
|
|
datetime.datetime, datetime.date,
|
|
datetime.timedelta)), \
|
|
'statement must be an integer, float, date or datetime'
|
|
if isinstance(equal, PYSON):
|
|
if equal.types() != {bool}:
|
|
equal = Bool(equal)
|
|
elif not isinstance(equal, bool):
|
|
equal = bool(equal)
|
|
self._statement1 = statement1
|
|
self._statement2 = statement2
|
|
self._equal = equal
|
|
|
|
@property
|
|
def _binary_operator(self):
|
|
return '>=' if self._equal else '>'
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return (self._statement1, self._statement2, self._equal)
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'Greater',
|
|
's1': self._statement1,
|
|
's2': self._statement2,
|
|
'e': self._equal,
|
|
}
|
|
|
|
def types(self):
|
|
return {bool}
|
|
|
|
@staticmethod
|
|
def _convert(dct):
|
|
for i in ('s1', 's2'):
|
|
if dct[i] is None:
|
|
dct[i] = 0.0
|
|
if not isinstance(dct[i], (int, float)):
|
|
dct = dct.copy()
|
|
stmt = dct[i]
|
|
if isinstance(stmt, datetime.datetime):
|
|
stmt = stmt.timestamp()
|
|
elif isinstance(stmt, datetime.date):
|
|
time = datetime.time(0, 0)
|
|
stmt = datetime.datetime.combine(stmt, time).timestamp()
|
|
elif isinstance(stmt, datetime.timedelta):
|
|
stmt = stmt.total_seconds()
|
|
dct[i] = float(stmt)
|
|
return dct
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
if dct['s1'] is None or dct['s2'] is None:
|
|
return False
|
|
dct = Greater._convert(dct)
|
|
if dct['e']:
|
|
return dct['s1'] >= dct['s2']
|
|
else:
|
|
return dct['s1'] > dct['s2']
|
|
|
|
|
|
class Less(Greater):
|
|
|
|
@property
|
|
def _binary_operator(self):
|
|
return '<=' if self._equal else '<'
|
|
|
|
def pyson(self):
|
|
res = super(Less, self).pyson()
|
|
res['__class__'] = 'Less'
|
|
return res
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
if dct['s1'] is None or dct['s2'] is None:
|
|
return False
|
|
dct = Less._convert(dct)
|
|
if dct['e']:
|
|
return dct['s1'] <= dct['s2']
|
|
else:
|
|
return dct['s1'] < dct['s2']
|
|
|
|
|
|
class If(PYSON):
|
|
|
|
def __init__(self, c, t, e=None):
|
|
condition, then_statement, else_statement = c, t, e
|
|
super(If, self).__init__()
|
|
if isinstance(condition, PYSON):
|
|
if condition.types() != {bool}:
|
|
condition = Bool(condition)
|
|
elif not isinstance(condition, bool):
|
|
condition = bool(condition)
|
|
self._condition = condition
|
|
self._then_statement = then_statement
|
|
self._else_statement = else_statement
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return (self._condition, self._then_statement, self._else_statement)
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'If',
|
|
'c': self._condition,
|
|
't': self._then_statement,
|
|
'e': self._else_statement,
|
|
}
|
|
|
|
def types(self):
|
|
if isinstance(self._then_statement, PYSON):
|
|
types = self._then_statement.types()
|
|
else:
|
|
types = {type(self._then_statement)}
|
|
if isinstance(self._else_statement, PYSON):
|
|
types |= self._else_statement.types()
|
|
else:
|
|
types |= {type(self._else_statement)}
|
|
return types
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
if dct['c']:
|
|
return dct['t']
|
|
else:
|
|
return dct['e']
|
|
|
|
|
|
class Get(PYSON):
|
|
_operator = 'get'
|
|
|
|
def __init__(self, v, k, d=''):
|
|
obj, key, default = v, k, d
|
|
super(Get, self).__init__()
|
|
if isinstance(obj, PYSON):
|
|
assert obj.types() == {dict}, 'obj must be a dict'
|
|
else:
|
|
assert isinstance(obj, dict), 'obj must be a dict'
|
|
self._obj = obj
|
|
if isinstance(key, PYSON):
|
|
assert key.types() == {str}, 'key must be a string'
|
|
else:
|
|
assert isinstance(key, str), 'key must be a string'
|
|
self._key = key
|
|
self._default = default
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
params = (self._obj, self._key)
|
|
if self._default != '':
|
|
params += (self._default,)
|
|
return params
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'Get',
|
|
'v': self._obj,
|
|
'k': self._key,
|
|
'd': self._default,
|
|
}
|
|
|
|
def types(self):
|
|
if isinstance(self._default, PYSON):
|
|
return self._default.types()
|
|
else:
|
|
return {type(self._default)}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
return dct['v'].get(dct['k'], dct['d'])
|
|
|
|
|
|
class In(PYSON):
|
|
_operator = 'in_'
|
|
|
|
def __init__(self, k, v):
|
|
key, obj = k, v
|
|
super(In, self).__init__()
|
|
if isinstance(key, PYSON):
|
|
assert key.types().issubset({str, int}), \
|
|
'key must be a string or an integer or a long'
|
|
else:
|
|
assert isinstance(key, (str, int)), \
|
|
'key must be a string or an integer or a long'
|
|
if isinstance(obj, PYSON):
|
|
assert obj.types().issubset({dict, list}), \
|
|
'obj must be a dict or a list'
|
|
if obj.types() == {dict}:
|
|
if isinstance(key, PYSON):
|
|
assert key.types() == {str}, 'key must be a string'
|
|
else:
|
|
assert isinstance(key, str), 'key must be a string'
|
|
else:
|
|
assert isinstance(obj, (dict, list))
|
|
if isinstance(obj, dict):
|
|
if isinstance(key, PYSON):
|
|
assert key.types() == {str}, 'key must be a string'
|
|
else:
|
|
assert isinstance(key, str), 'key must be a string'
|
|
self._key = key
|
|
self._obj = obj
|
|
|
|
def __repr__(self):
|
|
params = self.__repr_params__
|
|
if isinstance(params[1], PYSON):
|
|
return '%s.contains(%s)' % (
|
|
repr(params[1]), ', '.join(map(repr, params[:1] + params[2:])))
|
|
else:
|
|
return super().__repr__()
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return (self._key, self._obj)
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'In',
|
|
'k': self._key,
|
|
'v': self._obj,
|
|
}
|
|
|
|
def types(self):
|
|
return {bool}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
if dct['v']:
|
|
return dct['k'] in dct['v']
|
|
else:
|
|
return False
|
|
|
|
|
|
class Date(PYSON):
|
|
|
|
def __init__(self, year=None, month=None, day=None,
|
|
delta_years=0, delta_months=0, delta_days=0, start=None, **kwargs):
|
|
year = kwargs.get('y', year)
|
|
month = kwargs.get('M', month)
|
|
day = kwargs.get('d', day)
|
|
delta_years = kwargs.get('dy', delta_years)
|
|
delta_months = kwargs.get('dM', delta_months)
|
|
delta_days = kwargs.get('dd', delta_days)
|
|
super(Date, self).__init__()
|
|
for i in (year, month, day, delta_years, delta_months, delta_days):
|
|
if isinstance(i, PYSON):
|
|
assert i.types().issubset({int, type(None)}), \
|
|
'%s must be an integer or None' % (i,)
|
|
else:
|
|
assert isinstance(i, (int, type(None))), \
|
|
'%s must be an integer or None' % (i,)
|
|
self._year = year
|
|
self._month = month
|
|
self._day = day
|
|
self._delta_years = delta_years
|
|
self._delta_months = delta_months
|
|
self._delta_days = delta_days
|
|
self._start = start
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return (self._year, self._month, self._day,
|
|
self._delta_years, self._delta_months, self._delta_days,
|
|
self._start)
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'Date',
|
|
'y': self._year,
|
|
'M': self._month,
|
|
'd': self._day,
|
|
'dy': self._delta_years,
|
|
'dM': self._delta_months,
|
|
'dd': self._delta_days,
|
|
'start': self._start,
|
|
}
|
|
|
|
def types(self):
|
|
return {datetime.date}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
today = dct.get('start')
|
|
if isinstance(today, datetime.datetime):
|
|
today = today.date()
|
|
if not isinstance(today, datetime.date):
|
|
today = datetime.date.today()
|
|
return today + relativedelta(
|
|
year=dct['y'],
|
|
month=dct['M'],
|
|
day=dct['d'],
|
|
years=dct['dy'],
|
|
months=dct['dM'],
|
|
days=dct['dd'],
|
|
)
|
|
|
|
|
|
class DateTime(Date):
|
|
|
|
def __init__(self, year=None, month=None, day=None,
|
|
hour=None, minute=None, second=None, microsecond=None,
|
|
delta_years=0, delta_months=0, delta_days=0,
|
|
delta_hours=0, delta_minutes=0, delta_seconds=0,
|
|
delta_microseconds=0, start=None, **kwargs):
|
|
hour = kwargs.get('h', hour)
|
|
minute = kwargs.get('m', minute)
|
|
second = kwargs.get('s', second)
|
|
microsecond = kwargs.get('ms', microsecond)
|
|
delta_hours = kwargs.get('dh', delta_hours)
|
|
delta_minutes = kwargs.get('dm', delta_minutes)
|
|
delta_seconds = kwargs.get('ds', delta_seconds)
|
|
delta_microseconds = kwargs.get('dms', delta_microseconds)
|
|
super(DateTime, self).__init__(year=year, month=month, day=day,
|
|
delta_years=delta_years, delta_months=delta_months,
|
|
delta_days=delta_days, start=start, **kwargs)
|
|
for i in (hour, minute, second, microsecond,
|
|
delta_hours, delta_minutes, delta_seconds, delta_microseconds):
|
|
if isinstance(i, PYSON):
|
|
assert i.types() == {int, type(None)}, \
|
|
'%s must be an integer or None' % (i,)
|
|
else:
|
|
assert isinstance(i, (int, type(None))), \
|
|
'%s must be an integer or None' % (i,)
|
|
self._hour = hour
|
|
self._minute = minute
|
|
self._second = second
|
|
self._microsecond = microsecond
|
|
self._delta_hours = delta_hours
|
|
self._delta_minutes = delta_minutes
|
|
self._delta_seconds = delta_seconds
|
|
self._delta_microseconds = delta_microseconds
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
date_params = super(DateTime, self).__repr_params__
|
|
return (date_params[:3]
|
|
+ (self._hour, self._minute, self._second, self._microsecond)
|
|
+ date_params[3:-1]
|
|
+ (self._delta_hours, self._delta_minutes, self._delta_seconds,
|
|
self._delta_microseconds)
|
|
+ date_params[-1:])
|
|
|
|
def pyson(self):
|
|
res = super(DateTime, self).pyson()
|
|
res['__class__'] = 'DateTime'
|
|
res['h'] = self._hour
|
|
res['m'] = self._minute
|
|
res['s'] = self._second
|
|
res['ms'] = self._microsecond
|
|
res['dh'] = self._delta_hours
|
|
res['dm'] = self._delta_minutes
|
|
res['ds'] = self._delta_seconds
|
|
res['dms'] = self._delta_microseconds
|
|
return res
|
|
|
|
def types(self):
|
|
return {datetime.datetime}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
now = dct.get('start')
|
|
if (isinstance(now, datetime.date)
|
|
and not isinstance(now, datetime.datetime)):
|
|
now = datetime.datetime.combine(now, datetime.time())
|
|
if not isinstance(now, datetime.datetime):
|
|
now = datetime.datetime.utcnow()
|
|
return now + relativedelta(
|
|
year=dct['y'],
|
|
month=dct['M'],
|
|
day=dct['d'],
|
|
hour=dct['h'],
|
|
minute=dct['m'],
|
|
second=dct['s'],
|
|
microsecond=dct['ms'],
|
|
years=dct['dy'],
|
|
months=dct['dM'],
|
|
days=dct['dd'],
|
|
hours=dct['dh'],
|
|
minutes=dct['dm'],
|
|
seconds=dct['ds'],
|
|
microseconds=dct['dms'],
|
|
)
|
|
|
|
|
|
class TimeDelta(PYSON):
|
|
|
|
def __init__(self, days=0, seconds=0, microseconds=0):
|
|
for i in [days, seconds, microseconds]:
|
|
if isinstance(i, PYSON):
|
|
assert i.types().issubset({int, float}), \
|
|
'%s must be an integer' % (i,)
|
|
else:
|
|
assert isinstance(i, (int, float)), \
|
|
'%s must be an integer' % (i,)
|
|
self._days = days
|
|
self._seconds = seconds
|
|
self._microseconds = microseconds
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return self._days, self._seconds, self._microseconds
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'TimeDelta',
|
|
'd': self._days,
|
|
's': self._seconds,
|
|
'm': self._microseconds,
|
|
}
|
|
|
|
def types(self):
|
|
return {datetime.timedelta}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
return datetime.timedelta(
|
|
days=dct['d'],
|
|
seconds=dct['s'],
|
|
microseconds=dct['m'],
|
|
)
|
|
|
|
|
|
class Len(PYSON):
|
|
|
|
def __init__(self, v):
|
|
super(Len, self).__init__()
|
|
if isinstance(v, PYSON):
|
|
assert v.types().issubset({dict, list, str}), \
|
|
'value must be a dict or a list or a string'
|
|
else:
|
|
assert isinstance(v, (dict, list, str)), \
|
|
'value must be a dict or list or a string'
|
|
self._value = v
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return (self._value,)
|
|
|
|
def pyson(self):
|
|
return {
|
|
'__class__': 'Len',
|
|
'v': self._value,
|
|
}
|
|
|
|
def types(self):
|
|
return {int}
|
|
|
|
@staticmethod
|
|
def eval(dct, context):
|
|
return len(dct['v'])
|
|
|
|
|
|
class Id(PYSON):
|
|
"""The database id for filesystem id"""
|
|
|
|
def __init__(self, module, fs_id):
|
|
super(Id, self).__init__()
|
|
self._module = module
|
|
self._fs_id = fs_id
|
|
|
|
@property
|
|
def __repr_params__(self):
|
|
return (self._module, self._fs_id)
|
|
|
|
def pyson(self):
|
|
from trytond.pool import Pool
|
|
ModelData = Pool().get('ir.model.data')
|
|
return ModelData.get_id(self._module, self._fs_id)
|
|
|
|
def types(self):
|
|
return {int}
|
|
|
|
|
|
CONTEXT = {
|
|
'Eval': Eval,
|
|
'Not': Not,
|
|
'Bool': Bool,
|
|
'And': And,
|
|
'Or': Or,
|
|
'Equal': Equal,
|
|
'Greater': Greater,
|
|
'Less': Less,
|
|
'If': If,
|
|
'Get': Get,
|
|
'In': In,
|
|
'Date': Date,
|
|
'DateTime': DateTime,
|
|
'TimeDelta': TimeDelta,
|
|
'Len': Len,
|
|
'Id': Id,
|
|
'true': True,
|
|
'false': False,
|
|
}
|