2012-08-06 09:46:27 +00:00
|
|
|
/*
|
|
|
|
* py.js helpers and setup
|
|
|
|
*/
|
2013-08-06 12:50:22 +00:00
|
|
|
(function() {
|
|
|
|
|
|
|
|
var instance = openerp;
|
|
|
|
|
2012-08-06 09:46:27 +00:00
|
|
|
instance.web.pyeval = {};
|
|
|
|
|
2012-10-08 12:06:19 +00:00
|
|
|
var obj = function () {};
|
|
|
|
obj.prototype = py.object;
|
2012-08-06 09:46:27 +00:00
|
|
|
var asJS = function (arg) {
|
2012-10-08 12:06:19 +00:00
|
|
|
if (arg instanceof obj) {
|
2012-08-06 09:46:27 +00:00
|
|
|
return arg.toJSON();
|
|
|
|
}
|
|
|
|
return arg;
|
|
|
|
};
|
|
|
|
|
2012-10-08 12:06:19 +00:00
|
|
|
var datetime = py.PY_call(py.object);
|
2012-11-23 07:59:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* computes (Math.floor(a/b), a%b and passes that to the callback.
|
|
|
|
*
|
|
|
|
* returns the callback's result
|
|
|
|
*/
|
|
|
|
var divmod = function (a, b, fn) {
|
|
|
|
var mod = a%b;
|
|
|
|
// in python, sign(a % b) === sign(b). Not in JS. If wrong side, add a
|
|
|
|
// round of b
|
2012-11-30 10:36:38 +00:00
|
|
|
if (mod > 0 && b < 0 || mod < 0 && b > 0) {
|
2012-11-23 07:59:38 +00:00
|
|
|
mod += b;
|
|
|
|
}
|
|
|
|
return fn(Math.floor(a/b), mod);
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* Passes the fractional and integer parts of x to the callback, returns
|
|
|
|
* the callback's result
|
|
|
|
*/
|
|
|
|
var modf = function (x, fn) {
|
|
|
|
var mod = x%1;
|
|
|
|
if (mod < 0) {
|
|
|
|
mod += 1;
|
|
|
|
}
|
|
|
|
return fn(mod, Math.floor(x));
|
|
|
|
};
|
|
|
|
var zero = py.float.fromJSON(0);
|
2012-11-23 09:52:06 +00:00
|
|
|
|
2012-11-23 07:59:38 +00:00
|
|
|
// Port from pypy/lib_pypy/datetime.py
|
2012-11-23 09:52:06 +00:00
|
|
|
var DAYS_IN_MONTH = [null, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
|
|
var DAYS_BEFORE_MONTH = [null];
|
|
|
|
var dbm = 0;
|
|
|
|
for (var i=1; i<DAYS_IN_MONTH.length; ++i) {
|
|
|
|
DAYS_BEFORE_MONTH.push(dbm);
|
|
|
|
dbm += DAYS_IN_MONTH[i];
|
|
|
|
}
|
|
|
|
var is_leap = function (year) {
|
|
|
|
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
|
|
};
|
|
|
|
var days_before_year = function (year) {
|
|
|
|
var y = year - 1;
|
|
|
|
return y*365 + Math.floor(y/4) - Math.floor(y/100) + Math.floor(y/400);
|
|
|
|
};
|
|
|
|
var days_in_month = function (year, month) {
|
|
|
|
if (month === 2 && is_leap(year)) {
|
|
|
|
return 29;
|
|
|
|
}
|
|
|
|
return DAYS_IN_MONTH[month];
|
|
|
|
};
|
|
|
|
var days_before_month = function (year, month) {
|
|
|
|
var post_leap_feb = month > 2 && is_leap(year);
|
|
|
|
return DAYS_BEFORE_MONTH[month]
|
|
|
|
+ (post_leap_feb ? 1 : 0);
|
|
|
|
};
|
|
|
|
var ymd2ord = function (year, month, day) {
|
|
|
|
var dim = days_in_month(year, month);
|
|
|
|
if (!(1 <= day && day <= dim)) {
|
|
|
|
throw new Error("ValueError: day must be in 1.." + dim);
|
|
|
|
}
|
|
|
|
return days_before_year(year)
|
|
|
|
+ days_before_month(year, month)
|
|
|
|
+ day;
|
|
|
|
};
|
|
|
|
var DI400Y = days_before_year(401);
|
|
|
|
var DI100Y = days_before_year(101);
|
|
|
|
var DI4Y = days_before_year(5);
|
|
|
|
var assert = function (bool) {
|
|
|
|
if (!bool) {
|
|
|
|
throw new Error("AssertionError");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
var ord2ymd = function (n) {
|
|
|
|
--n;
|
|
|
|
var n400, n100, n4, n1, n0;
|
|
|
|
divmod(n, DI400Y, function (_n400, n) {
|
|
|
|
n400 = _n400;
|
|
|
|
divmod(n, DI100Y, function (_n100, n) {
|
|
|
|
n100 = _n100;
|
|
|
|
divmod(n, DI4Y, function (_n4, n) {
|
|
|
|
n4 = _n4;
|
|
|
|
divmod(n, 365, function (_n1, n) {
|
|
|
|
n1 = _n1;
|
|
|
|
n0 = n;
|
2013-07-25 10:33:01 +00:00
|
|
|
});
|
2012-11-23 09:52:06 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
n = n0;
|
|
|
|
var year = n400 * 400 + 1 + n100 * 100 + n4 * 4 + n1;
|
|
|
|
if (n1 == 4 || n100 == 100) {
|
|
|
|
assert(n0 === 0);
|
|
|
|
return {
|
|
|
|
year: year - 1,
|
|
|
|
month: 12,
|
|
|
|
day: 31
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
var leapyear = n1 === 3 && (n4 !== 24 || n100 == 3);
|
|
|
|
assert(leapyear == is_leap(year));
|
|
|
|
var month = (n + 50) >> 5;
|
|
|
|
var preceding = DAYS_BEFORE_MONTH[month] + ((month > 2 && leapyear) ? 1 : 0);
|
|
|
|
if (preceding > n) {
|
|
|
|
--month;
|
|
|
|
preceding -= DAYS_IN_MONTH[month] + ((month === 2 && leapyear) ? 1 : 0);
|
|
|
|
}
|
|
|
|
n -= preceding;
|
|
|
|
return {
|
|
|
|
year: year,
|
|
|
|
month: month,
|
|
|
|
day: n+1
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts the stuff passed in into a valid date, applying overflows as needed
|
|
|
|
*/
|
|
|
|
var tmxxx = function (year, month, day, hour, minute, second, microsecond) {
|
|
|
|
hour = hour || 0; minute = minute || 0; second = second || 0;
|
|
|
|
microsecond = microsecond || 0;
|
|
|
|
|
|
|
|
if (microsecond < 0 || microsecond > 999999) {
|
|
|
|
divmod(microsecond, 1000000, function (carry, ms) {
|
|
|
|
microsecond = ms;
|
2013-07-25 10:33:01 +00:00
|
|
|
second += carry;
|
2012-11-23 09:52:06 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
if (second < 0 || second > 59) {
|
|
|
|
divmod(second, 60, function (carry, s) {
|
|
|
|
second = s;
|
|
|
|
minute += carry;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (minute < 0 || minute > 59) {
|
|
|
|
divmod(minute, 60, function (carry, m) {
|
|
|
|
minute = m;
|
|
|
|
hour += carry;
|
2013-07-25 10:33:01 +00:00
|
|
|
});
|
2012-11-23 09:52:06 +00:00
|
|
|
}
|
|
|
|
if (hour < 0 || hour > 23) {
|
|
|
|
divmod(hour, 24, function (carry, h) {
|
|
|
|
hour = h;
|
|
|
|
day += carry;
|
2013-07-25 10:33:01 +00:00
|
|
|
});
|
2012-11-23 09:52:06 +00:00
|
|
|
}
|
|
|
|
// That was easy. Now it gets muddy: the proper range for day
|
|
|
|
// can't be determined without knowing the correct month and year,
|
|
|
|
// but if day is, e.g., plus or minus a million, the current month
|
|
|
|
// and year values make no sense (and may also be out of bounds
|
|
|
|
// themselves).
|
|
|
|
// Saying 12 months == 1 year should be non-controversial.
|
|
|
|
if (month < 1 || month > 12) {
|
|
|
|
divmod(month-1, 12, function (carry, m) {
|
|
|
|
month = m + 1;
|
|
|
|
year += carry;
|
2013-07-25 10:33:01 +00:00
|
|
|
});
|
2012-11-23 09:52:06 +00:00
|
|
|
}
|
|
|
|
// Now only day can be out of bounds (year may also be out of bounds
|
|
|
|
// for a datetime object, but we don't care about that here).
|
|
|
|
// If day is out of bounds, what to do is arguable, but at least the
|
|
|
|
// method here is principled and explainable.
|
|
|
|
var dim = days_in_month(year, month);
|
|
|
|
if (day < 1 || day > dim) {
|
|
|
|
// Move day-1 days from the first of the month. First try to
|
|
|
|
// get off cheap if we're only one day out of range (adjustments
|
|
|
|
// for timezone alone can't be worse than that).
|
|
|
|
if (day === 0) {
|
|
|
|
--month;
|
|
|
|
if (month > 0) {
|
|
|
|
day = days_in_month(year, month);
|
|
|
|
} else {
|
|
|
|
--year; month=12; day=31;
|
|
|
|
}
|
|
|
|
} else if (day == dim + 1) {
|
|
|
|
++month;
|
|
|
|
day = 1;
|
|
|
|
if (month > 12) {
|
|
|
|
month = 1;
|
|
|
|
++year;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var r = ord2ymd(ymd2ord(year, month, 1) + (day - 1));
|
|
|
|
year = r.year;
|
|
|
|
month = r.month;
|
|
|
|
day = r.day;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
year: year,
|
|
|
|
month: month,
|
|
|
|
day: day,
|
|
|
|
hour: hour,
|
|
|
|
minute: minute,
|
|
|
|
second: second,
|
|
|
|
microsecond: microsecond
|
|
|
|
};
|
|
|
|
};
|
2012-11-23 07:59:38 +00:00
|
|
|
datetime.timedelta = py.type('timedelta', null, {
|
|
|
|
__init__: function () {
|
|
|
|
var args = py.PY_parseArgs(arguments, [
|
|
|
|
['days', zero], ['seconds', zero], ['microseconds', zero],
|
|
|
|
['milliseconds', zero], ['minutes', zero], ['hours', zero],
|
|
|
|
['weeks', zero]
|
|
|
|
]);
|
|
|
|
|
|
|
|
var d = 0, s = 0, m = 0;
|
|
|
|
var days = args.days.toJSON() + args.weeks.toJSON() * 7;
|
|
|
|
var seconds = args.seconds.toJSON()
|
|
|
|
+ args.minutes.toJSON() * 60
|
|
|
|
+ args.hours.toJSON() * 3600;
|
|
|
|
var microseconds = args.microseconds.toJSON()
|
|
|
|
+ args.milliseconds.toJSON() * 1000;
|
|
|
|
|
|
|
|
// Get rid of all fractions, and normalize s and us.
|
|
|
|
// Take a deep breath <wink>.
|
|
|
|
var daysecondsfrac = modf(days, function (dayfrac, days) {
|
|
|
|
d = days;
|
|
|
|
if (dayfrac) {
|
|
|
|
return modf(dayfrac * 24 * 3600, function (dsf, dsw) {
|
|
|
|
s = dsw;
|
|
|
|
return dsf;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
var secondsfrac = modf(seconds, function (sf, s) {
|
|
|
|
seconds = s;
|
|
|
|
return sf + daysecondsfrac;
|
|
|
|
});
|
|
|
|
divmod(seconds, 24*3600, function (days, seconds) {
|
|
|
|
d += days;
|
2013-07-25 10:33:01 +00:00
|
|
|
s += seconds;
|
2012-11-23 07:59:38 +00:00
|
|
|
});
|
|
|
|
// seconds isn't referenced again before redefinition
|
|
|
|
|
|
|
|
microseconds += secondsfrac * 1e6;
|
|
|
|
divmod(microseconds, 1000000, function (seconds, microseconds) {
|
|
|
|
divmod(seconds, 24*3600, function (days, seconds) {
|
|
|
|
d += days;
|
|
|
|
s += seconds;
|
|
|
|
m += Math.round(microseconds);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// Carrying still possible here?
|
|
|
|
|
|
|
|
this.days = d;
|
|
|
|
this.seconds = s;
|
|
|
|
this.microseconds = m;
|
|
|
|
},
|
|
|
|
__str__: function () {
|
|
|
|
var hh, mm, ss;
|
|
|
|
divmod(this.seconds, 60, function (m, s) {
|
|
|
|
divmod(m, 60, function (h, m) {
|
|
|
|
hh = h;
|
|
|
|
mm = m;
|
|
|
|
ss = s;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
var s = _.str.sprintf("%d:%02d:%02d", hh, mm, ss);
|
|
|
|
if (this.days) {
|
|
|
|
s = _.str.sprintf("%d day%s, %s",
|
|
|
|
this.days,
|
|
|
|
(this.days != 1 && this.days != -1) ? 's' : '',
|
|
|
|
s);
|
|
|
|
}
|
|
|
|
if (this.microseconds) {
|
|
|
|
s = _.str.sprintf("%s.%06d", s, this.microseconds);
|
|
|
|
}
|
|
|
|
return py.str.fromJSON(s);
|
|
|
|
},
|
|
|
|
__eq__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (!py.PY_isInstance(other, datetime.timedelta)) {
|
2012-11-23 07:59:38 +00:00
|
|
|
return py.False;
|
|
|
|
}
|
|
|
|
return (this.days === other.days
|
|
|
|
&& this.seconds === other.seconds
|
|
|
|
&& this.microseconds === other.microseconds)
|
|
|
|
? py.True : py.False;
|
|
|
|
},
|
|
|
|
__add__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (!py.PY_isInstance(other, datetime.timedelta)) {
|
2012-11-23 07:59:38 +00:00
|
|
|
return py.NotImplemented;
|
|
|
|
}
|
|
|
|
return py.PY_call(datetime.timedelta, [
|
|
|
|
py.float.fromJSON(this.days + other.days),
|
|
|
|
py.float.fromJSON(this.seconds + other.seconds),
|
|
|
|
py.float.fromJSON(this.microseconds + other.microseconds)
|
|
|
|
]);
|
|
|
|
},
|
|
|
|
__radd__: function (other) { return this.__add__(other); },
|
|
|
|
__sub__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (!py.PY_isInstance(other, datetime.timedelta)) {
|
2012-11-23 07:59:38 +00:00
|
|
|
return py.NotImplemented;
|
|
|
|
}
|
|
|
|
return py.PY_call(datetime.timedelta, [
|
|
|
|
py.float.fromJSON(this.days - other.days),
|
|
|
|
py.float.fromJSON(this.seconds - other.seconds),
|
|
|
|
py.float.fromJSON(this.microseconds - other.microseconds)
|
|
|
|
]);
|
|
|
|
},
|
|
|
|
__rsub__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (!py.PY_isInstance(other, datetime.timedelta)) {
|
2012-11-23 07:59:38 +00:00
|
|
|
return py.NotImplemented;
|
|
|
|
}
|
|
|
|
return this.__neg__().__add__(other);
|
|
|
|
},
|
|
|
|
__neg__: function () {
|
|
|
|
return py.PY_call(datetime.timedelta, [
|
|
|
|
py.float.fromJSON(-this.days),
|
|
|
|
py.float.fromJSON(-this.seconds),
|
|
|
|
py.float.fromJSON(-this.microseconds)
|
|
|
|
]);
|
|
|
|
},
|
|
|
|
__pos__: function () { return this; },
|
|
|
|
__mul__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (!py.PY_isInstance(other, py.float)) {
|
2012-11-23 07:59:38 +00:00
|
|
|
return py.NotImplemented;
|
|
|
|
}
|
|
|
|
var n = other.toJSON();
|
|
|
|
return py.PY_call(datetime.timedelta, [
|
|
|
|
py.float.fromJSON(this.days * n),
|
|
|
|
py.float.fromJSON(this.seconds * n),
|
|
|
|
py.float.fromJSON(this.microseconds * n)
|
|
|
|
]);
|
|
|
|
},
|
|
|
|
__rmul__: function (other) { return this.__mul__(other); },
|
|
|
|
__div__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (!py.PY_isInstance(other, py.float)) {
|
2012-11-23 07:59:38 +00:00
|
|
|
return py.NotImplemented;
|
|
|
|
}
|
|
|
|
var usec = ((this.days * 24 * 3600) + this.seconds) * 1000000
|
|
|
|
+ this.microseconds;
|
|
|
|
return py.PY_call(
|
|
|
|
datetime.timedelta, [
|
|
|
|
zero, zero, py.float.fromJSON(usec / other.toJSON())]);
|
|
|
|
},
|
|
|
|
__floordiv__: function (other) { return this.__div__(other); },
|
|
|
|
total_seconds: function () {
|
|
|
|
return py.float.fromJSON(
|
|
|
|
this.days * 86400
|
|
|
|
+ this.seconds
|
2013-07-25 10:33:01 +00:00
|
|
|
+ this.microseconds / 1000000);
|
2012-11-23 07:59:38 +00:00
|
|
|
},
|
|
|
|
__nonzero__: function () {
|
|
|
|
return (!!this.days || !!this.seconds || !!this.microseconds)
|
|
|
|
? py.True
|
|
|
|
: py.False;
|
|
|
|
}
|
|
|
|
});
|
2012-10-08 12:06:19 +00:00
|
|
|
datetime.datetime = py.type('datetime', null, {
|
|
|
|
__init__: function () {
|
|
|
|
var zero = py.float.fromJSON(0);
|
|
|
|
var args = py.PY_parseArgs(arguments, [
|
|
|
|
'year', 'month', 'day',
|
|
|
|
['hour', zero], ['minute', zero], ['second', zero],
|
|
|
|
['microsecond', zero], ['tzinfo', py.None]
|
|
|
|
]);
|
|
|
|
for(var key in args) {
|
|
|
|
if (!args.hasOwnProperty(key)) { continue; }
|
|
|
|
this[key] = asJS(args[key]);
|
|
|
|
}
|
|
|
|
},
|
2014-01-22 15:19:11 +00:00
|
|
|
replace: function () {
|
|
|
|
var args = py.PY_parseArgs(arguments, [
|
|
|
|
['year', py.None], ['month', py.None], ['day', py.None],
|
|
|
|
['hour', py.None], ['minute', py.None], ['second', py.None],
|
|
|
|
['microsecond', py.None] // FIXME: tzinfo, can't use None as valid input
|
|
|
|
]);
|
|
|
|
var params = {};
|
|
|
|
for(var key in args) {
|
|
|
|
if (!args.hasOwnProperty(key)) { continue; }
|
|
|
|
|
|
|
|
var arg = args[key];
|
|
|
|
params[key] = (arg === py.None ? this[key] : asJS(arg));
|
|
|
|
}
|
|
|
|
return py.PY_call(datetime.datetime, params);
|
|
|
|
},
|
2012-10-08 12:06:19 +00:00
|
|
|
strftime: function () {
|
|
|
|
var self = this;
|
|
|
|
var args = py.PY_parseArgs(arguments, 'format');
|
|
|
|
return py.str.fromJSON(args.format.toJSON()
|
|
|
|
.replace(/%([A-Za-z])/g, function (m, c) {
|
|
|
|
switch (c) {
|
2014-01-22 15:19:11 +00:00
|
|
|
case 'Y': return _.str.sprintf('%04d', self.year);
|
2012-10-08 12:06:19 +00:00
|
|
|
case 'm': return _.str.sprintf('%02d', self.month);
|
|
|
|
case 'd': return _.str.sprintf('%02d', self.day);
|
|
|
|
case 'H': return _.str.sprintf('%02d', self.hour);
|
|
|
|
case 'M': return _.str.sprintf('%02d', self.minute);
|
|
|
|
case 'S': return _.str.sprintf('%02d', self.second);
|
|
|
|
}
|
|
|
|
throw new Error('ValueError: No known conversion for ' + m);
|
|
|
|
}));
|
|
|
|
},
|
|
|
|
now: py.classmethod.fromJSON(function () {
|
2014-01-22 15:19:11 +00:00
|
|
|
var d = new Date;
|
|
|
|
return py.PY_call(datetime.datetime, [
|
|
|
|
d.getFullYear(), d.getMonth() + 1, d.getDate(),
|
|
|
|
d.getHours(), d.getMinutes(), d.getSeconds(),
|
|
|
|
d.getMilliseconds() * 1000]);
|
|
|
|
}),
|
|
|
|
today: py.classmethod.fromJSON(function () {
|
|
|
|
var dt_class = py.PY_getAttr(datetime, 'datetime');
|
|
|
|
return py.PY_call(py.PY_getAttr(dt_class, 'now'));
|
|
|
|
}),
|
|
|
|
utcnow: py.classmethod.fromJSON(function () {
|
2012-10-08 12:06:19 +00:00
|
|
|
var d = new Date();
|
|
|
|
return py.PY_call(datetime.datetime,
|
2012-11-29 15:36:01 +00:00
|
|
|
[d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate(),
|
|
|
|
d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(),
|
|
|
|
d.getUTCMilliseconds() * 1000]);
|
2012-10-08 12:06:19 +00:00
|
|
|
}),
|
|
|
|
combine: py.classmethod.fromJSON(function () {
|
|
|
|
var args = py.PY_parseArgs(arguments, 'date time');
|
|
|
|
return py.PY_call(datetime.datetime, [
|
2012-11-28 11:05:42 +00:00
|
|
|
py.PY_getAttr(args.date, 'year'),
|
|
|
|
py.PY_getAttr(args.date, 'month'),
|
|
|
|
py.PY_getAttr(args.date, 'day'),
|
|
|
|
py.PY_getAttr(args.time, 'hour'),
|
|
|
|
py.PY_getAttr(args.time, 'minute'),
|
|
|
|
py.PY_getAttr(args.time, 'second')
|
2012-10-08 12:06:19 +00:00
|
|
|
]);
|
2014-01-22 15:19:11 +00:00
|
|
|
}),
|
|
|
|
toJSON: function () {
|
|
|
|
return new Date(
|
|
|
|
this.year,
|
|
|
|
this.month - 1,
|
|
|
|
this.day,
|
|
|
|
this.hour,
|
|
|
|
this.minute,
|
|
|
|
this.second,
|
|
|
|
this.microsecond / 1000);
|
|
|
|
},
|
2012-08-06 09:46:27 +00:00
|
|
|
});
|
2012-10-08 12:06:19 +00:00
|
|
|
datetime.date = py.type('date', null, {
|
|
|
|
__init__: function () {
|
|
|
|
var args = py.PY_parseArgs(arguments, 'year month day');
|
|
|
|
this.year = asJS(args.year);
|
|
|
|
this.month = asJS(args.month);
|
|
|
|
this.day = asJS(args.day);
|
|
|
|
},
|
|
|
|
strftime: function () {
|
|
|
|
var self = this;
|
|
|
|
var args = py.PY_parseArgs(arguments, 'format');
|
|
|
|
return py.str.fromJSON(args.format.toJSON()
|
|
|
|
.replace(/%([A-Za-z])/g, function (m, c) {
|
|
|
|
switch (c) {
|
|
|
|
case 'Y': return self.year;
|
|
|
|
case 'm': return _.str.sprintf('%02d', self.month);
|
|
|
|
case 'd': return _.str.sprintf('%02d', self.day);
|
|
|
|
}
|
|
|
|
throw new Error('ValueError: No known conversion for ' + m);
|
|
|
|
}));
|
|
|
|
},
|
2012-11-23 09:52:06 +00:00
|
|
|
__eq__: function (other) {
|
|
|
|
return (this.year === other.year
|
|
|
|
&& this.month === other.month
|
|
|
|
&& this.day === other.day)
|
|
|
|
? py.True : py.False;
|
|
|
|
},
|
2014-10-05 20:25:08 +00:00
|
|
|
replace: function () {
|
|
|
|
var args = py.PY_parseArgs(arguments, [
|
|
|
|
['year', py.None], ['month', py.None], ['day', py.None]
|
|
|
|
]);
|
|
|
|
var params = {};
|
|
|
|
for(var key in args) {
|
|
|
|
if (!args.hasOwnProperty(key)) { continue; }
|
|
|
|
|
|
|
|
var arg = args[key];
|
|
|
|
params[key] = (arg === py.None ? this[key] : asJS(arg));
|
|
|
|
}
|
|
|
|
return py.PY_call(datetime.date, params);
|
|
|
|
},
|
2012-11-23 09:52:06 +00:00
|
|
|
__add__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (!py.PY_isInstance(other, datetime.timedelta)) {
|
2012-11-23 09:52:06 +00:00
|
|
|
return py.NotImplemented;
|
|
|
|
}
|
|
|
|
var s = tmxxx(this.year, this.month, this.day + other.days);
|
|
|
|
return datetime.date.fromJSON(s.year, s.month, s.day);
|
|
|
|
},
|
|
|
|
__radd__: function (other) { return this.__add__(other); },
|
|
|
|
__sub__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (py.PY_isInstance(other, datetime.timedelta)) {
|
2012-11-23 09:52:06 +00:00
|
|
|
return this.__add__(other.__neg__());
|
|
|
|
}
|
2012-11-28 11:05:42 +00:00
|
|
|
if (py.PY_isInstance(other, datetime.date)) {
|
2012-11-23 09:52:06 +00:00
|
|
|
// FIXME: getattr and sub API methods
|
|
|
|
return py.PY_call(datetime.timedelta, [
|
2012-11-28 11:05:42 +00:00
|
|
|
py.PY_subtract(
|
|
|
|
py.PY_call(py.PY_getAttr(this, 'toordinal')),
|
|
|
|
py.PY_call(py.PY_getAttr(other, 'toordinal')))
|
2012-11-23 09:52:06 +00:00
|
|
|
]);
|
|
|
|
}
|
|
|
|
return py.NotImplemented;
|
|
|
|
},
|
|
|
|
toordinal: function () {
|
|
|
|
return py.float.fromJSON(ymd2ord(this.year, this.month, this.day));
|
|
|
|
},
|
2015-01-16 09:26:09 +00:00
|
|
|
weekday: function () {
|
|
|
|
return py.float.fromJSON((this.toordinal().toJSON()+6)%7);
|
|
|
|
},
|
2012-11-23 09:52:06 +00:00
|
|
|
fromJSON: function (year, month, day) {
|
2013-07-25 10:33:01 +00:00
|
|
|
return py.PY_call(datetime.date, [year, month, day]);
|
2014-01-22 15:19:11 +00:00
|
|
|
},
|
|
|
|
today: py.classmethod.fromJSON(function () {
|
|
|
|
var d = new Date;
|
|
|
|
return py.PY_call(datetime.date, [
|
|
|
|
d.getFullYear(), d.getMonth() + 1, d.getDate()]);
|
|
|
|
}),
|
2012-08-06 09:46:27 +00:00
|
|
|
});
|
2012-11-29 16:07:46 +00:00
|
|
|
/**
|
|
|
|
Returns the current local date, which means the date on the client (which can be different
|
|
|
|
compared to the date of the server).
|
|
|
|
|
|
|
|
@return {datetime.date}
|
|
|
|
*/
|
|
|
|
var context_today = function() {
|
|
|
|
var d = new Date();
|
|
|
|
return py.PY_call(
|
|
|
|
datetime.date, [d.getFullYear(), d.getMonth() + 1, d.getDate()]);
|
|
|
|
};
|
2012-10-08 12:06:19 +00:00
|
|
|
datetime.time = py.type('time', null, {
|
|
|
|
__init__: function () {
|
|
|
|
var zero = py.float.fromJSON(0);
|
|
|
|
var args = py.PY_parseArgs(arguments, [
|
|
|
|
['hour', zero], ['minute', zero], ['second', zero], ['microsecond', zero],
|
|
|
|
['tzinfo', py.None]
|
|
|
|
]);
|
|
|
|
|
|
|
|
for(var k in args) {
|
|
|
|
if (!args.hasOwnProperty(k)) { continue; }
|
|
|
|
this[k] = asJS(args[k]);
|
|
|
|
}
|
2012-08-06 09:46:27 +00:00
|
|
|
}
|
|
|
|
});
|
2012-10-08 12:06:19 +00:00
|
|
|
var time = py.PY_call(py.object);
|
|
|
|
time.strftime = py.PY_def.fromJSON(function () {
|
2012-11-28 11:05:42 +00:00
|
|
|
var args = py.PY_parseArgs(arguments, 'format');
|
|
|
|
var dt_class = py.PY_getAttr(datetime, 'datetime');
|
2014-01-22 15:19:11 +00:00
|
|
|
var d = py.PY_call(py.PY_getAttr(dt_class, 'utcnow'));
|
2012-11-28 11:05:42 +00:00
|
|
|
return py.PY_call(py.PY_getAttr(d, 'strftime'), [args.format]);
|
2012-08-06 09:46:27 +00:00
|
|
|
});
|
|
|
|
|
2013-03-18 10:52:44 +00:00
|
|
|
var args = _.map(('year month day hour minute second microsecond '
|
|
|
|
+ 'years months weeks days hours minutes secondes microseconds '
|
|
|
|
+ 'weekday leakdays yearday nlyearday').split(' '), function (arg) {
|
2013-07-25 10:33:01 +00:00
|
|
|
return [arg, null];
|
2013-03-18 10:52:44 +00:00
|
|
|
});
|
|
|
|
args.unshift('*');
|
2012-10-08 12:06:19 +00:00
|
|
|
var relativedelta = py.type('relativedelta', null, {
|
|
|
|
__init__: function () {
|
2013-03-18 10:52:44 +00:00
|
|
|
this.ops = py.PY_parseArgs(arguments, args);
|
2012-10-08 12:06:19 +00:00
|
|
|
},
|
2012-08-06 09:46:27 +00:00
|
|
|
__add__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (!py.PY_isInstance(other, datetime.date)) {
|
2012-08-06 09:46:27 +00:00
|
|
|
return py.NotImplemented;
|
|
|
|
}
|
|
|
|
// TODO: test this whole mess
|
|
|
|
var year = asJS(this.ops.year) || asJS(other.year);
|
|
|
|
if (asJS(this.ops.years)) {
|
|
|
|
year += asJS(this.ops.years);
|
|
|
|
}
|
|
|
|
|
|
|
|
var month = asJS(this.ops.month) || asJS(other.month);
|
|
|
|
if (asJS(this.ops.months)) {
|
|
|
|
month += asJS(this.ops.months);
|
|
|
|
// FIXME: no divmod in JS?
|
|
|
|
while (month < 1) {
|
|
|
|
year -= 1;
|
|
|
|
month += 12;
|
|
|
|
}
|
|
|
|
while (month > 12) {
|
|
|
|
year += 1;
|
|
|
|
month -= 12;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var lastMonthDay = new Date(year, month, 0).getDate();
|
|
|
|
var day = asJS(this.ops.day) || asJS(other.day);
|
|
|
|
if (day > lastMonthDay) { day = lastMonthDay; }
|
|
|
|
var days_offset = ((asJS(this.ops.weeks) || 0) * 7) + (asJS(this.ops.days) || 0);
|
|
|
|
if (days_offset) {
|
|
|
|
day = new Date(year, month-1, day + days_offset).getDate();
|
|
|
|
}
|
|
|
|
// TODO: leapdays?
|
|
|
|
// TODO: hours, minutes, seconds? Not used in XML domains
|
|
|
|
// TODO: weekday?
|
2012-10-08 12:06:19 +00:00
|
|
|
// FIXME: use date.replace
|
|
|
|
return py.PY_call(datetime.date, [
|
|
|
|
py.float.fromJSON(year),
|
|
|
|
py.float.fromJSON(month),
|
|
|
|
py.float.fromJSON(day)
|
|
|
|
]);
|
2012-08-06 09:46:27 +00:00
|
|
|
},
|
|
|
|
__radd__: function (other) {
|
|
|
|
return this.__add__(other);
|
|
|
|
},
|
|
|
|
|
|
|
|
__sub__: function (other) {
|
2012-11-28 11:05:42 +00:00
|
|
|
if (!py.PY_isInstance(other, datetime.date)) {
|
2012-08-06 09:46:27 +00:00
|
|
|
return py.NotImplemented;
|
|
|
|
}
|
|
|
|
// TODO: test this whole mess
|
|
|
|
var year = asJS(this.ops.year) || asJS(other.year);
|
|
|
|
if (asJS(this.ops.years)) {
|
|
|
|
year -= asJS(this.ops.years);
|
|
|
|
}
|
|
|
|
|
|
|
|
var month = asJS(this.ops.month) || asJS(other.month);
|
|
|
|
if (asJS(this.ops.months)) {
|
|
|
|
month -= asJS(this.ops.months);
|
|
|
|
// FIXME: no divmod in JS?
|
|
|
|
while (month < 1) {
|
|
|
|
year -= 1;
|
|
|
|
month += 12;
|
|
|
|
}
|
|
|
|
while (month > 12) {
|
|
|
|
year += 1;
|
|
|
|
month -= 12;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var lastMonthDay = new Date(year, month, 0).getDate();
|
|
|
|
var day = asJS(this.ops.day) || asJS(other.day);
|
|
|
|
if (day > lastMonthDay) { day = lastMonthDay; }
|
|
|
|
var days_offset = ((asJS(this.ops.weeks) || 0) * 7) + (asJS(this.ops.days) || 0);
|
|
|
|
if (days_offset) {
|
|
|
|
day = new Date(year, month-1, day - days_offset).getDate();
|
|
|
|
}
|
|
|
|
// TODO: leapdays?
|
|
|
|
// TODO: hours, minutes, seconds? Not used in XML domains
|
|
|
|
// TODO: weekday?
|
2012-10-08 12:06:19 +00:00
|
|
|
return py.PY_call(datetime.date, [
|
|
|
|
py.float.fromJSON(year),
|
|
|
|
py.float.fromJSON(month),
|
|
|
|
py.float.fromJSON(day)
|
|
|
|
]);
|
2012-08-06 09:46:27 +00:00
|
|
|
},
|
|
|
|
__rsub__: function (other) {
|
|
|
|
return this.__sub__(other);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-06-24 05:59:29 +00:00
|
|
|
// recursively wraps JS objects passed into the context to attributedicts
|
|
|
|
// which jsonify back to JS objects
|
|
|
|
var wrap = function (value) {
|
|
|
|
if (value === null) { return py.None; }
|
|
|
|
|
|
|
|
switch (typeof value) {
|
|
|
|
case 'undefined': throw new Error("No conversion for undefined");
|
|
|
|
case 'boolean': return py.bool.fromJSON(value);
|
|
|
|
case 'number': return py.float.fromJSON(value);
|
|
|
|
case 'string': return py.str.fromJSON(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(value.constructor) {
|
|
|
|
case Object: return wrapping_dict.fromJSON(value);
|
|
|
|
case Array: return wrapping_list.fromJSON(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error("ValueError: unable to wrap " + value);
|
|
|
|
};
|
|
|
|
var wrapping_dict = py.type('wrapping_dict', null, {
|
|
|
|
__init__: function () {
|
|
|
|
this._store = {};
|
|
|
|
},
|
|
|
|
__getitem__: function (key) {
|
|
|
|
var k = key.toJSON();
|
|
|
|
if (!(k in this._store)) {
|
|
|
|
throw new Error("KeyError: '" + k + "'");
|
|
|
|
}
|
|
|
|
return wrap(this._store[k]);
|
|
|
|
},
|
|
|
|
__getattr__: function (key) {
|
|
|
|
return this.__getitem__(py.str.fromJSON(key));
|
|
|
|
},
|
|
|
|
get: function () {
|
|
|
|
var args = py.PY_parseArgs(arguments, ['k', ['d', py.None]]);
|
|
|
|
|
|
|
|
if (!(args.k.toJSON() in this._store)) { return args.d; }
|
|
|
|
return this.__getitem__(args.k);
|
|
|
|
},
|
|
|
|
fromJSON: function (d) {
|
|
|
|
var instance = py.PY_call(wrapping_dict);
|
|
|
|
instance._store = d;
|
|
|
|
return instance;
|
|
|
|
},
|
|
|
|
toJSON: function () {
|
|
|
|
return this._store;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
var wrapping_list = py.type('wrapping_list', null, {
|
|
|
|
__init__: function () {
|
|
|
|
this._store = [];
|
|
|
|
},
|
|
|
|
__getitem__: function (index) {
|
|
|
|
return wrap(this._store[index.toJSON()]);
|
|
|
|
},
|
|
|
|
fromJSON: function (ar) {
|
|
|
|
var instance = py.PY_call(wrapping_list);
|
|
|
|
instance._store = ar;
|
|
|
|
return instance;
|
|
|
|
},
|
|
|
|
toJSON: function () {
|
|
|
|
return this._store;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
var wrap_context = function (context) {
|
|
|
|
for (var k in context) {
|
|
|
|
if (!context.hasOwnProperty(k)) { continue; }
|
|
|
|
var val = context[k];
|
|
|
|
|
2013-07-02 14:16:00 +00:00
|
|
|
if (val === null) { continue; }
|
2013-06-24 05:59:29 +00:00
|
|
|
if (val.constructor === Array) {
|
|
|
|
context[k] = wrapping_list.fromJSON(val);
|
|
|
|
} else if (val.constructor === Object
|
|
|
|
&& !py.PY_isInstance(val, py.object)) {
|
|
|
|
context[k] = wrapping_dict.fromJSON(val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return context;
|
|
|
|
};
|
|
|
|
|
2012-08-06 09:46:27 +00:00
|
|
|
var eval_contexts = function (contexts, evaluation_context) {
|
2013-03-19 10:27:19 +00:00
|
|
|
evaluation_context = _.extend(instance.web.pyeval.context(), evaluation_context || {});
|
2012-08-06 09:46:27 +00:00
|
|
|
return _(contexts).reduce(function (result_context, ctx) {
|
|
|
|
// __eval_context evaluations can lead to some of `contexts`'s
|
|
|
|
// values being null, skip them as well as empty contexts
|
|
|
|
if (_.isEmpty(ctx)) { return result_context; }
|
2012-11-23 11:49:23 +00:00
|
|
|
if (_.isString(ctx)) {
|
|
|
|
// wrap raw strings in context
|
|
|
|
ctx = { __ref: 'context', __debug: ctx };
|
|
|
|
}
|
2012-08-06 09:46:27 +00:00
|
|
|
var evaluated = ctx;
|
|
|
|
switch(ctx.__ref) {
|
|
|
|
case 'context':
|
2013-06-24 05:59:29 +00:00
|
|
|
evaluation_context.context = evaluation_context;
|
|
|
|
evaluated = py.eval(ctx.__debug, wrap_context(evaluation_context));
|
2012-08-06 09:46:27 +00:00
|
|
|
break;
|
|
|
|
case 'compound_context':
|
|
|
|
var eval_context = eval_contexts([ctx.__eval_context]);
|
|
|
|
evaluated = eval_contexts(
|
|
|
|
ctx.__contexts, _.extend({}, evaluation_context, eval_context));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// add newly evaluated context to evaluation context for following
|
|
|
|
// siblings
|
|
|
|
_.extend(evaluation_context, evaluated);
|
|
|
|
return _.extend(result_context, evaluated);
|
2013-03-19 10:27:19 +00:00
|
|
|
}, {});
|
2012-08-06 09:46:27 +00:00
|
|
|
};
|
|
|
|
var eval_domains = function (domains, evaluation_context) {
|
2013-03-19 10:27:19 +00:00
|
|
|
evaluation_context = _.extend(instance.web.pyeval.context(), evaluation_context || {});
|
2012-08-06 09:46:27 +00:00
|
|
|
var result_domain = [];
|
|
|
|
_(domains).each(function (domain) {
|
2012-11-23 11:49:23 +00:00
|
|
|
if (_.isString(domain)) {
|
|
|
|
// wrap raw strings in domain
|
|
|
|
domain = { __ref: 'domain', __debug: domain };
|
|
|
|
}
|
2012-08-06 09:46:27 +00:00
|
|
|
switch(domain.__ref) {
|
|
|
|
case 'domain':
|
2013-06-24 05:59:29 +00:00
|
|
|
evaluation_context.context = evaluation_context;
|
2012-08-06 09:46:27 +00:00
|
|
|
result_domain.push.apply(
|
2013-06-24 05:59:29 +00:00
|
|
|
result_domain, py.eval(domain.__debug, wrap_context(evaluation_context)));
|
2012-08-06 09:46:27 +00:00
|
|
|
break;
|
|
|
|
case 'compound_domain':
|
|
|
|
var eval_context = eval_contexts([domain.__eval_context]);
|
|
|
|
result_domain.push.apply(
|
|
|
|
result_domain, eval_domains(
|
|
|
|
domain.__domains, _.extend(
|
|
|
|
{}, evaluation_context, eval_context)));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
result_domain.push.apply(result_domain, domain);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return result_domain;
|
|
|
|
};
|
|
|
|
var eval_groupbys = function (contexts, evaluation_context) {
|
2013-03-19 10:27:19 +00:00
|
|
|
evaluation_context = _.extend(instance.web.pyeval.context(), evaluation_context || {});
|
2012-08-06 09:46:27 +00:00
|
|
|
var result_group = [];
|
|
|
|
_(contexts).each(function (ctx) {
|
2012-11-23 11:49:23 +00:00
|
|
|
if (_.isString(ctx)) {
|
|
|
|
// wrap raw strings in context
|
|
|
|
ctx = { __ref: 'context', __debug: ctx };
|
|
|
|
}
|
2012-08-06 09:46:27 +00:00
|
|
|
var group;
|
|
|
|
var evaluated = ctx;
|
|
|
|
switch(ctx.__ref) {
|
|
|
|
case 'context':
|
2013-06-24 05:59:29 +00:00
|
|
|
evaluation_context.context = evaluation_context;
|
|
|
|
evaluated = py.eval(ctx.__debug, wrap_context(evaluation_context));
|
2012-08-06 09:46:27 +00:00
|
|
|
break;
|
|
|
|
case 'compound_context':
|
|
|
|
var eval_context = eval_contexts([ctx.__eval_context]);
|
|
|
|
evaluated = eval_contexts(
|
|
|
|
ctx.__contexts, _.extend({}, evaluation_context, eval_context));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
group = evaluated.group_by;
|
|
|
|
if (!group) { return; }
|
|
|
|
if (typeof group === 'string') {
|
|
|
|
result_group.push(group);
|
|
|
|
} else if (group instanceof Array) {
|
|
|
|
result_group.push.apply(result_group, group);
|
|
|
|
} else {
|
|
|
|
throw new Error('Got invalid groupby {{'
|
|
|
|
+ JSON.stringify(group) + '}}');
|
|
|
|
}
|
|
|
|
_.extend(evaluation_context, evaluated);
|
|
|
|
});
|
|
|
|
return result_group;
|
|
|
|
};
|
|
|
|
|
|
|
|
instance.web.pyeval.context = function () {
|
2013-01-28 13:06:40 +00:00
|
|
|
return _.extend({
|
2012-08-06 09:46:27 +00:00
|
|
|
datetime: datetime,
|
2012-11-29 16:07:46 +00:00
|
|
|
context_today: context_today,
|
2012-08-06 09:46:27 +00:00
|
|
|
time: time,
|
|
|
|
relativedelta: relativedelta,
|
2012-10-08 12:06:19 +00:00
|
|
|
current_date: py.PY_call(
|
|
|
|
time.strftime, [py.str.fromJSON('%Y-%m-%d')]),
|
2013-01-28 13:06:40 +00:00
|
|
|
}, instance.session.user_context);
|
2012-08-06 09:46:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {String} type "domains", "contexts" or "groupbys"
|
|
|
|
* @param {Array} object domains or contexts to evaluate
|
|
|
|
* @param {Object} [context] evaluation context
|
|
|
|
*/
|
2013-03-19 10:27:19 +00:00
|
|
|
instance.web.pyeval.eval = function (type, object, context, options) {
|
|
|
|
options = options || {};
|
2012-10-10 14:59:45 +00:00
|
|
|
context = _.extend(instance.web.pyeval.context(), context || {});
|
|
|
|
|
2012-11-26 08:42:29 +00:00
|
|
|
//noinspection FallthroughInSwitchStatementJS
|
2012-08-06 09:46:27 +00:00
|
|
|
switch(type) {
|
2013-07-25 10:07:49 +00:00
|
|
|
case 'context':
|
|
|
|
case 'contexts':
|
|
|
|
if (type === 'context')
|
|
|
|
object = [object];
|
|
|
|
return eval_contexts((options.no_user_context ? [] : [instance.session.user_context]).concat(object), context);
|
|
|
|
case 'domain':
|
|
|
|
case 'domains':
|
|
|
|
if (type === 'domain')
|
|
|
|
object = [object];
|
|
|
|
return eval_domains(object, context);
|
|
|
|
case 'groupbys':
|
|
|
|
return eval_groupbys(object, context);
|
2012-08-06 09:46:27 +00:00
|
|
|
}
|
2013-07-25 10:33:01 +00:00
|
|
|
throw new Error("Unknow evaluation type " + type);
|
2012-08-06 09:46:27 +00:00
|
|
|
};
|
2012-10-11 10:05:32 +00:00
|
|
|
|
|
|
|
var eval_arg = function (arg) {
|
|
|
|
if (typeof arg !== 'object' || !arg.__ref) { return arg; }
|
|
|
|
switch(arg.__ref) {
|
|
|
|
case 'domain': case 'compound_domain':
|
|
|
|
return instance.web.pyeval.eval('domains', [arg]);
|
|
|
|
case 'context': case 'compound_context':
|
|
|
|
return instance.web.pyeval.eval('contexts', [arg]);
|
|
|
|
default:
|
2012-11-22 12:41:30 +00:00
|
|
|
throw new Error(instance.web._t("Unknown nonliteral type " + arg.__ref));
|
2012-10-11 10:05:32 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* If args or kwargs are unevaluated contexts or domains (compound or not),
|
|
|
|
* evaluated them in-place.
|
|
|
|
*
|
|
|
|
* Potentially mutates both parameters.
|
|
|
|
*
|
|
|
|
* @param args
|
|
|
|
* @param kwargs
|
|
|
|
*/
|
|
|
|
instance.web.pyeval.ensure_evaluated = function (args, kwargs) {
|
|
|
|
for (var i=0; i<args.length; ++i) {
|
|
|
|
args[i] = eval_arg(args[i]);
|
|
|
|
}
|
|
|
|
for (var k in kwargs) {
|
|
|
|
if (!kwargs.hasOwnProperty(k)) { continue; }
|
|
|
|
kwargs[k] = eval_arg(kwargs[k]);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
instance.web.pyeval.eval_domains_and_contexts = function (source) {
|
2012-11-23 10:18:22 +00:00
|
|
|
return new $.Deferred(function (d) {setTimeout(function () {
|
2013-11-13 10:22:04 +00:00
|
|
|
var result;
|
2012-10-11 10:05:32 +00:00
|
|
|
try {
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
result = instance.web.pyeval.sync_eval_domains_and_contexts(source);
|
|
|
|
}
|
|
|
|
catch (e) {
|
2013-11-13 10:22:04 +00:00
|
|
|
result = { error: {
|
2012-10-11 10:05:32 +00:00
|
|
|
code: 400,
|
2012-11-22 12:41:30 +00:00
|
|
|
message: instance.web._t("Evaluation Error"),
|
2012-10-11 10:05:32 +00:00
|
|
|
data: {
|
|
|
|
type: 'local_exception',
|
|
|
|
debug: _.str.sprintf(
|
2012-11-22 12:41:30 +00:00
|
|
|
instance.web._t("Local evaluation failure\n%s\n\n%s"),
|
2012-10-11 10:05:32 +00:00
|
|
|
e.message, JSON.stringify(source))
|
|
|
|
}
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
}};
|
2012-10-11 10:05:32 +00:00
|
|
|
}
|
2013-11-13 10:22:04 +00:00
|
|
|
d.resolve(result);
|
2012-11-23 10:18:22 +00:00
|
|
|
}, 0); });
|
2013-07-25 10:33:01 +00:00
|
|
|
};
|
[FIX] web: fixes issues with custom filters and search view
The problem was that when the user manipulates the graph view (in pivot
table mode), the graph view resetted the group by facet in the search
view. It was not a problem unless a custom filter with a groupby was
already there, in which case, the group bys were duplicated.
The search view is now smarter, it only resets the additional groupbys
(and col_groupbys). Also, to prevent usability problems, the graph
view disable the '+/-' groupbys added by a custom filters.
Note that this fix is only temporary: a revamp of custom filters, facets,
search view is coming in the next months. (at least, that's the idea). Right
now, too much 'search logic' is in the graph view.
Another note: this fix is somewhat fragile: it makes some assumptions
about the search query (mainly that the custom filter is the first facet,
also, that no other filters add groupbys/col_groupbys)
2014-09-09 10:26:16 +00:00
|
|
|
instance.web.pyeval.sync_eval_domains_and_contexts = function (source) {
|
|
|
|
var contexts = ([instance.session.user_context] || []).concat(source.contexts);
|
|
|
|
// see Session.eval_context in Python
|
|
|
|
return {
|
|
|
|
context: instance.web.pyeval.eval('contexts', contexts),
|
|
|
|
domain: instance.web.pyeval.eval('domains', source.domains),
|
|
|
|
group_by: instance.web.pyeval.eval('groupbys', source.group_by_seq || [])
|
|
|
|
};
|
|
|
|
};
|
2013-08-06 12:50:22 +00:00
|
|
|
})();
|