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
=== added file 'bin/tools/date_eval.py'
--- bin/tools/date_eval.py 1970-01-01 00:00:00 +0000
+++ bin/tools/date_eval.py 2011-04-01 20:43:38 +0000
@@ -0,0 +1,86 @@
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4import re
5import time
6import datetime
7from dateutil.relativedelta import relativedelta
8
9re_abstimes = { 'now': datetime.datetime.now,
10 'today': datetime.date.today,
11 'tomorrow': lambda: (datetime.date.today() + datetime.timedelta(1)),
12 'yesterday': lambda: (datetime.date.today() - datetime.timedelta(1)),
13 }
14
15rel_units = { 'yr' : 'Y',
16 'year': 'Y',
17 'years': 'Y',
18 'month': 'M',
19 'months': 'M',
20 'mo': 'M',
21 'd': 'D',
22 'day': 'D',
23 'days': 'D',
24 'h': 3600,
25 'hr': 3600,
26 'hour': 3600,
27 'hours': 3600,
28 'm': 60,
29 'min': 60,
30 'minute': 60,
31 'minutes': 60,
32 's': 1,
33 'sec' : 1,
34 'second': 1,
35 'seconds': 1,
36 }
37
38re_dateeval = re.compile(r"(?P<abs>" + '|'.join(re_abstimes) +")"
39 r"|(?:(?P<rel>(?:\+|-)[0-9]+)(?P<rel_unit>" + '|'.join(rel_units)+ "))"
40 r"|(?: ?\bon ?(?P<date>[0-9]{1,2}(?:/[0-9]{1,2}(?:/[0-9]{2,4})?)?))"
41 r"|(?: ?\bat ?(?P<time>[0-9]{1,2}(?::[0-9]{2}(?::[0-9]{2})?)?))"
42 r"| +", re.I)
43
44def date_eval(rstr):
45 cur_time = datetime.datetime.now()
46 for m in re_dateeval.finditer(rstr):
47 if m.group('abs'):
48 cur_time = re_abstimes[m.group('abs')]()
49 if not isinstance(cur_time, datetime.datetime):
50 cur_time = datetime.datetime.fromordinal(cur_time.toordinal())
51
52 elif m.group('rel'):
53 mrel = int(m.group('rel')[1:])
54 if m.group('rel')[0] == '-':
55 mrel = 0 - mrel
56 mun = rel_units[m.group('rel_unit')]
57 if mun == 'Y':
58 drel = relativedelta(years=mrel)
59 elif mun == 'M':
60 drel = relativedelta(months=mrel)
61 elif mun == 'D':
62 drel = datetime.timedelta(days=mrel)
63 else:
64 drel = mrel * datetime.timedelta(seconds=mun)
65
66 cur_time = cur_time + drel
67 elif m.group('date'):
68 dli = map(int, m.group('date').split('/'))
69 if len(dli) == 2:
70 dli += [cur_time.year,]
71 elif len(dli) == 1:
72 dli += [cur_time.month, cur_time.year]
73 cur_time = datetime.datetime.combine(datetime.date(dli[2],dli[1],dli[0]), cur_time.time())
74 elif m.group('time'):
75 dli = map(int, m.group('time').split(':'))
76 if len(dli) == 2:
77 dli += [cur_time.second,]
78 elif len(dli) == 1:
79 dli += [cur_time.minute, cur_time.second]
80 cur_time = datetime.datetime.combine(cur_time.date(), datetime.time(dli[0],dli[1],dli[2]))
81 else:
82 pass
83
84 return cur_time
85
86#eof
087
=== modified file 'bin/tools/yaml_tag.py'
--- bin/tools/yaml_tag.py 2010-12-03 14:20:35 +0000
+++ bin/tools/yaml_tag.py 2011-04-01 20:43:38 +0000
@@ -1,5 +1,7 @@
1import yaml1import yaml
2import logging2import logging
3from date_eval import date_eval
4from misc import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
35
4class YamlTag(object):6class YamlTag(object):
5 """7 """
@@ -90,7 +92,7 @@
90 super(Eval, self).__init__()92 super(Eval, self).__init__()
91 def __str__(self):93 def __str__(self):
92 return '!eval %s' % str(self.expression)94 return '!eval %s' % str(self.expression)
93 95
94class Ref(YamlTag):96class Ref(YamlTag):
95 def __init__(self, expr="False", *args, **kwargs):97 def __init__(self, expr="False", *args, **kwargs):
96 self.expr = expr98 self.expr = expr
@@ -150,6 +152,16 @@
150 expression = loader.construct_scalar(node)152 expression = loader.construct_scalar(node)
151 return Eval(expression)153 return Eval(expression)
152 154
155def date_constructor(loader, node):
156 expression = loader.construct_scalar(node)
157 # TODO return a datetime.date object, not string
158 return date_eval(expression).strftime(DEFAULT_SERVER_DATE_FORMAT)
159
160def datetime_constructor(loader, node):
161 expression = loader.construct_scalar(node)
162 # TODO return a datetime.datetime object
163 return date_eval(expression).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
164
153def ref_constructor(loader, tag_suffix, node):165def ref_constructor(loader, tag_suffix, node):
154 if tag_suffix == "id":166 if tag_suffix == "id":
155 kwargs = {"id": loader.construct_scalar(node)}167 kwargs = {"id": loader.construct_scalar(node)}
@@ -177,6 +189,8 @@
177 yaml.add_constructor(u"!delete", delete_constructor)189 yaml.add_constructor(u"!delete", delete_constructor)
178 yaml.add_constructor(u"!url", url_constructor)190 yaml.add_constructor(u"!url", url_constructor)
179 yaml.add_constructor(u"!eval", eval_constructor)191 yaml.add_constructor(u"!eval", eval_constructor)
192 yaml.add_constructor(u"!date", date_constructor)
193 yaml.add_constructor(u"!datetime", datetime_constructor)
180 yaml.add_multi_constructor(u"!ref", ref_constructor)194 yaml.add_multi_constructor(u"!ref", ref_constructor)
181 yaml.add_constructor(u"!ir_set", ir_set_constructor)195 yaml.add_constructor(u"!ir_set", ir_set_constructor)
182add_constructors()196add_constructors()