Merge lp:~openerp-dev/openobject-server/6.0-bug-747746-xrg into lp:openobject-server/6.0

Proposed by xrg
Status: Rejected
Rejected by: Vo Minh Thu
Proposed branch: lp:~openerp-dev/openobject-server/6.0-bug-747746-xrg
Merge into: lp:openobject-server/6.0
Diff against target: 137 lines (+101/-1)
2 files modified
bin/tools/date_eval.py (+86/-0)
bin/tools/yaml_tag.py (+15/-1)
To merge this branch: bzr merge lp:~openerp-dev/openobject-server/6.0-bug-747746-xrg
Reviewer Review Type Date Requested Status
Vo Minh Thu (community) Disapprove
Review via email: mp+56001@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Vo Minh Thu (thu) wrote :

I'm not sure such 'utility' function has its place in the code just to write tests, especially that it has no documentation, and no associated tests.

review: Disapprove
Revision history for this message
Vo Minh Thu (thu) wrote :

The code might prove useful in multiple places. Before merging and using it, we would need to make sure this is what we want. A few things to think about:

- is there anything already doing something similar, either in python or as concrete syntax (to not reinvent the wheel)?
- is it possible to directly write something like this in yaml
    date: today + 2 days
  or does it need a new !bang syntax or use of !python?
- have a solid semantic regarding date/datetime conversions
- things like
    in 2 days, 2 days ago, monday in 1 month, next monday,...
  ?

Revision history for this message
Vo Minh Thu (thu) wrote :

Anyway, this is not much important. This is mostly a end-user experience thing, we don't really care for the developers. And nothing has been decided for the user interface about this.

I will reject this proposal even if we might want to include it later for user experience reason.

Revision history for this message
xrg (xrg) wrote :

On Thursday 12 May 2011, you wrote:
> The code might prove useful in multiple places. Before merging and using
> it, we would need to make sure this is what we want. A few things to think
> about:
>
> - is there anything already doing something similar, either in python or as
> concrete syntax (to not reinvent the wheel)?
The only concrete function is in PHP. For python, there is a wrapper that
calls the PHP strtotime(), but I imagine that's not what we would ever use.

> - is it possible to directly write something like this in yaml
> date: today + 2 days
> or does it need a new !bang syntax or use of !python?
It is not possible, since the values of fields are passed as "scalar
expressions", which means the string is pushed as-is to the var. Thus, it is
cleaner and safer to use an explicit operator. I thought "!date" and
"!datetime" are easy to guess/use.

> - have a solid semantic regarding date/datetime conversions
> - things like
> in 2 days, 2 days ago, monday in 1 month, next monday,...
> ?
I have a stash for this enhancement. However, while trying to implement that,
I discovered that the English text (like ones you propose) is ambiguous in
several cases. I wanted a little more time + discussion with community, to
standardize that.
I'd be happy to show you these half-implemented extensions and have your
opinion on the ambiguity.

>Anyway, this is not much important. This is mostly a end-user experience
thing..

In some cases, it makes our life much easier. We have had a lot of yaml test
either failing or doing wrong computations because of hard-coded (or poor
relative) dates/times. Apart from that, if we could agree[1] on an
implementation (wrt. syntax of the expressions), we could extend their usage
in other fields. For example, we could allow them to appear in xml files, in
formulae (imagine the production scheduler to be able to be set "now +3days at
14:00") etc.

[1] I never claim this code is perfect, feel free to propose a better
solution.

Unmerged revisions

3389. By xrg

tools: 'date_eval' utility for relative dates in YAML

Every end of the month, we face the problem that some YAML tests fail
because of naive date calculations (without curry).
Provide a utility function that will parse dates/times like "now+2days".

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'bin/tools/date_eval.py'
2--- bin/tools/date_eval.py 1970-01-01 00:00:00 +0000
3+++ bin/tools/date_eval.py 2011-04-01 20:43:38 +0000
4@@ -0,0 +1,86 @@
5+#!/usr/bin/python
6+# -*- coding: utf-8 -*-
7+
8+import re
9+import time
10+import datetime
11+from dateutil.relativedelta import relativedelta
12+
13+re_abstimes = { 'now': datetime.datetime.now,
14+ 'today': datetime.date.today,
15+ 'tomorrow': lambda: (datetime.date.today() + datetime.timedelta(1)),
16+ 'yesterday': lambda: (datetime.date.today() - datetime.timedelta(1)),
17+ }
18+
19+rel_units = { 'yr' : 'Y',
20+ 'year': 'Y',
21+ 'years': 'Y',
22+ 'month': 'M',
23+ 'months': 'M',
24+ 'mo': 'M',
25+ 'd': 'D',
26+ 'day': 'D',
27+ 'days': 'D',
28+ 'h': 3600,
29+ 'hr': 3600,
30+ 'hour': 3600,
31+ 'hours': 3600,
32+ 'm': 60,
33+ 'min': 60,
34+ 'minute': 60,
35+ 'minutes': 60,
36+ 's': 1,
37+ 'sec' : 1,
38+ 'second': 1,
39+ 'seconds': 1,
40+ }
41+
42+re_dateeval = re.compile(r"(?P<abs>" + '|'.join(re_abstimes) +")"
43+ r"|(?:(?P<rel>(?:\+|-)[0-9]+)(?P<rel_unit>" + '|'.join(rel_units)+ "))"
44+ r"|(?: ?\bon ?(?P<date>[0-9]{1,2}(?:/[0-9]{1,2}(?:/[0-9]{2,4})?)?))"
45+ r"|(?: ?\bat ?(?P<time>[0-9]{1,2}(?::[0-9]{2}(?::[0-9]{2})?)?))"
46+ r"| +", re.I)
47+
48+def date_eval(rstr):
49+ cur_time = datetime.datetime.now()
50+ for m in re_dateeval.finditer(rstr):
51+ if m.group('abs'):
52+ cur_time = re_abstimes[m.group('abs')]()
53+ if not isinstance(cur_time, datetime.datetime):
54+ cur_time = datetime.datetime.fromordinal(cur_time.toordinal())
55+
56+ elif m.group('rel'):
57+ mrel = int(m.group('rel')[1:])
58+ if m.group('rel')[0] == '-':
59+ mrel = 0 - mrel
60+ mun = rel_units[m.group('rel_unit')]
61+ if mun == 'Y':
62+ drel = relativedelta(years=mrel)
63+ elif mun == 'M':
64+ drel = relativedelta(months=mrel)
65+ elif mun == 'D':
66+ drel = datetime.timedelta(days=mrel)
67+ else:
68+ drel = mrel * datetime.timedelta(seconds=mun)
69+
70+ cur_time = cur_time + drel
71+ elif m.group('date'):
72+ dli = map(int, m.group('date').split('/'))
73+ if len(dli) == 2:
74+ dli += [cur_time.year,]
75+ elif len(dli) == 1:
76+ dli += [cur_time.month, cur_time.year]
77+ cur_time = datetime.datetime.combine(datetime.date(dli[2],dli[1],dli[0]), cur_time.time())
78+ elif m.group('time'):
79+ dli = map(int, m.group('time').split(':'))
80+ if len(dli) == 2:
81+ dli += [cur_time.second,]
82+ elif len(dli) == 1:
83+ dli += [cur_time.minute, cur_time.second]
84+ cur_time = datetime.datetime.combine(cur_time.date(), datetime.time(dli[0],dli[1],dli[2]))
85+ else:
86+ pass
87+
88+ return cur_time
89+
90+#eof
91
92=== modified file 'bin/tools/yaml_tag.py'
93--- bin/tools/yaml_tag.py 2010-12-03 14:20:35 +0000
94+++ bin/tools/yaml_tag.py 2011-04-01 20:43:38 +0000
95@@ -1,5 +1,7 @@
96 import yaml
97 import logging
98+from date_eval import date_eval
99+from misc import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
100
101 class YamlTag(object):
102 """
103@@ -90,7 +92,7 @@
104 super(Eval, self).__init__()
105 def __str__(self):
106 return '!eval %s' % str(self.expression)
107-
108+
109 class Ref(YamlTag):
110 def __init__(self, expr="False", *args, **kwargs):
111 self.expr = expr
112@@ -150,6 +152,16 @@
113 expression = loader.construct_scalar(node)
114 return Eval(expression)
115
116+def date_constructor(loader, node):
117+ expression = loader.construct_scalar(node)
118+ # TODO return a datetime.date object, not string
119+ return date_eval(expression).strftime(DEFAULT_SERVER_DATE_FORMAT)
120+
121+def datetime_constructor(loader, node):
122+ expression = loader.construct_scalar(node)
123+ # TODO return a datetime.datetime object
124+ return date_eval(expression).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
125+
126 def ref_constructor(loader, tag_suffix, node):
127 if tag_suffix == "id":
128 kwargs = {"id": loader.construct_scalar(node)}
129@@ -177,6 +189,8 @@
130 yaml.add_constructor(u"!delete", delete_constructor)
131 yaml.add_constructor(u"!url", url_constructor)
132 yaml.add_constructor(u"!eval", eval_constructor)
133+ yaml.add_constructor(u"!date", date_constructor)
134+ yaml.add_constructor(u"!datetime", datetime_constructor)
135 yaml.add_multi_constructor(u"!ref", ref_constructor)
136 yaml.add_constructor(u"!ir_set", ir_set_constructor)
137 add_constructors()