Merge lp:~openerp-dev/openerp-web/7.0-today-filter-mat into lp:openerp-web/7.0

Proposed by Martin Trigaux (OpenERP)
Status: Needs review
Proposed branch: lp:~openerp-dev/openerp-web/7.0-today-filter-mat
Merge into: lp:openerp-web/7.0
Diff against target: 224 lines (+110/-8)
4 files modified
addons/web/static/src/js/corelib.js (+22/-2)
addons/web/static/src/js/data.js (+8/-2)
addons/web/static/src/js/pyeval.js (+45/-4)
addons/web/static/test/evals.js (+35/-0)
To merge this branch: bzr merge lp:~openerp-dev/openerp-web/7.0-today-filter-mat
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+203589@code.launchpad.net

Description of the change

backport of trunk revision 3919, not to be merged in 7.0

[MERGE] [ADD] ways to correctly handle local datetimes in e.g. domains

* Alter datetime.now(), generate a local datetime (add utcnow() which generates a UTC datetime)
* Implement datetime.replace() to manipulate local datetimes
* Implement date.today(), generates a local date
* Implement datetime.toJSON(), returns a javascript Date (assumes datetime attributes are local)
* Add conversion hook in JSON and JSONP handlers, automatically converts a Date object to a UTC datetime formatted according to server formats

Should allow the generation of correctly working (from the end-user's POV) [Today] filters, amongst other things.
Eg: a local expression in a domain for 'Today 00:00:00' would now be expressed as 'datetime.datetime.now().replace(hour=0, minute=0, second=0)' (no .strftime) and will be converted to UTC when sent to the server

To post a comment you must log in.
4129. By Martin Trigaux (OpenERP)

[ADD] backport part of trunk rev 3918.3923: allow to use filtres defined with field:day

4130. By Martin Trigaux (OpenERP)

[fix] grouping_field can be undefined

4131. By Martin Trigaux (OpenERP)

[MERGE] sync with 7.0 up to rev 4159

Unmerged revisions

4131. By Martin Trigaux (OpenERP)

[MERGE] sync with 7.0 up to rev 4159

4130. By Martin Trigaux (OpenERP)

[fix] grouping_field can be undefined

4129. By Martin Trigaux (OpenERP)

[ADD] backport part of trunk rev 3918.3923: allow to use filtres defined with field:day

4128. By Martin Trigaux (OpenERP)

[ADD] filters: backport of trunk rev 3919

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'addons/web/static/src/js/corelib.js'
2--- addons/web/static/src/js/corelib.js 2013-03-01 10:22:23 +0000
3+++ addons/web/static/src/js/corelib.js 2014-03-13 15:59:47 +0000
4@@ -950,6 +950,26 @@
5 }
6 });
7
8+/**
9+ * Replacer function for JSON.stringify, serializes Date objects to UTC
10+ * datetime in the OpenERP Server format.
11+ *
12+ * However, if a serialized value has a toJSON method that method is called
13+ * *before* the replacer is invoked. Date#toJSON exists, and thus the value
14+ * passed to the replacer is a string, the original Date has to be fetched
15+ * on the parent object (which is provided as the replacer's context).
16+ *
17+ * @param {String} k
18+ * @param {Object} v
19+ * @returns {Object}
20+ */
21+function date_to_utc(k, v) {
22+ var value = this[k];
23+ if (!(value instanceof Date)) { return v; }
24+
25+ return instance.web.datetime_to_str(value);
26+}
27+
28 instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
29 triggers: {
30 'request': 'Request sent',
31@@ -1055,7 +1075,7 @@
32 type: "POST",
33 dataType: 'json',
34 contentType: 'application/json',
35- data: JSON.stringify(payload),
36+ data: JSON.stringify(payload, date_to_utc),
37 processData: false
38 }, url);
39 if (this.synch)
40@@ -1088,7 +1108,7 @@
41 }, url);
42 if (this.synch)
43 ajax.async = false;
44- var payload_str = JSON.stringify(payload);
45+ var payload_str = JSON.stringify(payload, date_to_utc);
46 var payload_url = $.param({r:payload_str});
47 if(payload_url.length < 2000) {
48 // Direct jsonp request
49
50=== modified file 'addons/web/static/src/js/data.js'
51--- addons/web/static/src/js/data.js 2014-03-12 15:04:35 +0000
52+++ addons/web/static/src/js/data.js 2014-03-13 15:59:47 +0000
53@@ -127,10 +127,14 @@
54 return null;
55 }
56
57+ var raw_fields = _.map(grouping.concat(this._fields || []), function (field) {
58+ return field.split(':')[0];
59+ });
60+
61 var self = this;
62 return this._model.call('read_group', {
63 groupby: grouping,
64- fields: _.uniq(grouping.concat(this._fields || [])),
65+ fields: _.uniq(raw_fields),
66 domain: this._model.domain(this._filter),
67 context: ctx,
68 offset: this._offset,
69@@ -230,12 +234,14 @@
70
71 var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0;
72 var leaf_group = fixed_group.__context.group_by.length === 0;
73+ var raw_field = grouping_field && grouping_field.split(':')[0];
74+
75 this.attributes = {
76 folded: !!(fixed_group.__fold),
77 grouped_on: grouping_field,
78 // if terminal group (or no group) and group_by_no_leaf => use group.__count
79 length: group_size,
80- value: fixed_group[grouping_field],
81+ value: fixed_group[raw_field],
82 // A group is open-able if it's not a leaf in group_by_no_leaf mode
83 has_children: !(leaf_group && fixed_group.__context['group_by_no_leaf']),
84
85
86=== modified file 'addons/web/static/src/js/pyeval.js'
87--- addons/web/static/src/js/pyeval.js 2013-07-02 14:16:00 +0000
88+++ addons/web/static/src/js/pyeval.js 2014-03-13 15:59:47 +0000
89@@ -379,13 +379,28 @@
90 this[key] = asJS(args[key]);
91 }
92 },
93+ replace: function () {
94+ var args = py.PY_parseArgs(arguments, [
95+ ['year', py.None], ['month', py.None], ['day', py.None],
96+ ['hour', py.None], ['minute', py.None], ['second', py.None],
97+ ['microsecond', py.None] // FIXME: tzinfo, can't use None as valid input
98+ ]);
99+ var params = {};
100+ for(var key in args) {
101+ if (!args.hasOwnProperty(key)) { continue; }
102+
103+ var arg = args[key];
104+ params[key] = (arg === py.None ? this[key] : asJS(arg));
105+ }
106+ return py.PY_call(datetime.datetime, params);
107+ },
108 strftime: function () {
109 var self = this;
110 var args = py.PY_parseArgs(arguments, 'format');
111 return py.str.fromJSON(args.format.toJSON()
112 .replace(/%([A-Za-z])/g, function (m, c) {
113 switch (c) {
114- case 'Y': return self.year;
115+ case 'Y': return _.str.sprintf('%04d', self.year);
116 case 'm': return _.str.sprintf('%02d', self.month);
117 case 'd': return _.str.sprintf('%02d', self.day);
118 case 'H': return _.str.sprintf('%02d', self.hour);
119@@ -396,6 +411,17 @@
120 }));
121 },
122 now: py.classmethod.fromJSON(function () {
123+ var d = new Date;
124+ return py.PY_call(datetime.datetime, [
125+ d.getFullYear(), d.getMonth() + 1, d.getDate(),
126+ d.getHours(), d.getMinutes(), d.getSeconds(),
127+ d.getMilliseconds() * 1000]);
128+ }),
129+ today: py.classmethod.fromJSON(function () {
130+ var dt_class = py.PY_getAttr(datetime, 'datetime');
131+ return py.PY_call(py.PY_getAttr(dt_class, 'now'));
132+ }),
133+ utcnow: py.classmethod.fromJSON(function () {
134 var d = new Date();
135 return py.PY_call(datetime.datetime,
136 [d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate(),
137@@ -412,7 +438,17 @@
138 py.PY_getAttr(args.time, 'minute'),
139 py.PY_getAttr(args.time, 'second')
140 ]);
141- })
142+ }),
143+ toJSON: function () {
144+ return new Date(
145+ this.year,
146+ this.month - 1,
147+ this.day,
148+ this.hour,
149+ this.minute,
150+ this.second,
151+ this.microsecond / 1000);
152+ },
153 });
154 datetime.date = py.type('date', null, {
155 __init__: function () {
156@@ -467,7 +503,12 @@
157 },
158 fromJSON: function (year, month, day) {
159 return py.PY_call(datetime.date, [year, month, day])
160- }
161+ },
162+ today: py.classmethod.fromJSON(function () {
163+ var d = new Date;
164+ return py.PY_call(datetime.date, [
165+ d.getFullYear(), d.getMonth() + 1, d.getDate()]);
166+ }),
167 });
168 /**
169 Returns the current local date, which means the date on the client (which can be different
170@@ -498,7 +539,7 @@
171 time.strftime = py.PY_def.fromJSON(function () {
172 var args = py.PY_parseArgs(arguments, 'format');
173 var dt_class = py.PY_getAttr(datetime, 'datetime');
174- var d = py.PY_call(py.PY_getAttr(dt_class, 'now'));
175+ var d = py.PY_call(py.PY_getAttr(dt_class, 'utcnow'));
176 return py.PY_call(py.PY_getAttr(d, 'strftime'), [args.format]);
177 });
178
179
180=== modified file 'addons/web/static/test/evals.js'
181--- addons/web/static/test/evals.js 2013-06-24 05:59:29 +0000
182+++ addons/web/static/test/evals.js 2014-03-13 15:59:47 +0000
183@@ -418,6 +418,41 @@
184 });
185 });
186 });
187+ test('datetime.tojson', function (instance) {
188+ var result = py.eval(
189+ 'datetime.datetime(2012, 2, 15, 1, 7, 31)',
190+ instance.web.pyeval.context());
191+ ok(result instanceof Date);
192+ equal(result.getFullYear(), 2012);
193+ equal(result.getMonth(), 1);
194+ equal(result.getDate(), 15);
195+ equal(result.getHours(), 1);
196+ equal(result.getMinutes(), 7);
197+ equal(result.getSeconds(), 31);
198+ });
199+ test('datetime.combine', function (instance) {
200+ var result = py.eval(
201+ 'datetime.datetime.combine(datetime.date(2012, 2, 15),' +
202+ ' datetime.time(1, 7, 13))' +
203+ ' .strftime("%Y-%m-%d %H:%M:%S")',
204+ instance.web.pyeval.context());
205+ equal(result, "2012-02-15 01:07:13");
206+
207+ result = py.eval(
208+ 'datetime.datetime.combine(datetime.date(2012, 2, 15),' +
209+ ' datetime.time())' +
210+ ' .strftime("%Y-%m-%d %H:%M:%S")',
211+ instance.web.pyeval.context());
212+ equal(result, '2012-02-15 00:00:00');
213+ });
214+ test('datetime.replace', function (instance) {
215+ var result = py.eval(
216+ 'datetime.datetime(2012, 2, 15, 1, 7, 13)' +
217+ ' .replace(hour=0, minute=0, second=0)' +
218+ ' .strftime("%Y-%m-%d %H:%M:%S")',
219+ instance.web.pyeval.context());
220+ equal(result, "2012-02-15 00:00:00");
221+ });
222 });
223 openerp.testing.section('eval.edc.nonliterals', {
224 dependencies: ['web.data'],