You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

117 lines
4.1 KiB

8 years ago
  1. # -*- coding: utf-8 -*-
  2. # © 2016 Therp BV <http://therp.nl>
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. from dateutil.rrule import rrule, rruleset
  5. from openerp import fields, models
  6. _DATETIME_FIELDS = ['_until', '_dtstart']
  7. _SCALAR_FIELDS = [
  8. '_wkst', '_cache', '_until', '_dtstart', '_count', '_freq', '_interval',
  9. ]
  10. _ZERO_IS_NOT_NONE = ['_freq']
  11. class SerializableRRuleSet(rruleset, list):
  12. """Getting our rule set json stringified is tricky, because we can't
  13. just inject our own json encoder. Now we pretend our set is a list,
  14. then json.dumps will try to iterate, which is why we can do our specific
  15. stuff in __iter__"""
  16. def __init__(self, *args):
  17. self._rrule = []
  18. super(SerializableRRuleSet, self).__init__(self)
  19. for arg in args:
  20. self.rrule(arg)
  21. def __iter__(self):
  22. for rule in self._rrule:
  23. yield dict(type='rrule', **{
  24. key[1:]:
  25. fields.Datetime.to_string(getattr(rule, key))
  26. if key in _DATETIME_FIELDS
  27. else
  28. [] if getattr(rule, key) is None and key not in _SCALAR_FIELDS
  29. else
  30. list(getattr(rule, key)) if key not in _SCALAR_FIELDS
  31. else getattr(rule, key)
  32. for key in [
  33. '_byhour', '_wkst', '_bysecond', '_bymonthday',
  34. '_byweekno', '_bysetpos', '_cache', '_bymonth',
  35. '_byyearday', '_byweekday', '_byminute',
  36. '_until', '_dtstart', '_count', '_freq', '_interval',
  37. '_byeaster',
  38. ]
  39. })
  40. # TODO: implement rdate, exrule, exdate
  41. def __call__(self, default_self=None):
  42. """convert self to a proper rruleset for iteration.
  43. If we use defaults on our field, this will be called too with
  44. and empty recordset as parameter. In this case, we need self"""
  45. if isinstance(default_self, models.BaseModel):
  46. return self
  47. result = rruleset()
  48. result._rrule = self._rrule
  49. result._rdate = self._rdate
  50. result._exrule = self._exrule
  51. result._exdate = self._exdate
  52. return result
  53. def __nonzero__(self):
  54. return bool(self._rrule)
  55. def __repr__(self):
  56. return ', '.join(str(a) for a in self)
  57. def __getitem__(self, key):
  58. return rruleset.__getitem__(self(), key)
  59. def __getslice__(self, i, j):
  60. return rruleset.__getitem__(self(), slice(i, j))
  61. def __ne__(self, o):
  62. return not self.__eq__(o)
  63. def __eq__(self, o):
  64. return self.__repr__() == o.__repr__()
  65. def between(self, after, before, inc=False):
  66. return self().between(after, before, inc=inc)
  67. def after(self, dt, inc=False):
  68. return self().after(dt, inc=inc)
  69. def before(self, dt, inc=False):
  70. return self().before(dt, inc=inc)
  71. def count(self):
  72. return self().count()
  73. class FieldRRule(fields.Serialized):
  74. def convert_to_cache(self, value, record, validate=True):
  75. result = SerializableRRuleSet()
  76. if not value:
  77. return result
  78. if isinstance(value, SerializableRRuleSet):
  79. return value
  80. assert isinstance(value, list), 'An RRULE\'s content must be a list'
  81. for data in value:
  82. assert isinstance(data, dict), 'The list must contain dictionaries'
  83. assert 'type' in data, 'The dictionary must contain a type'
  84. data_type = data['type']
  85. data = {
  86. key: fields.Datetime.from_string(value)
  87. if '_%s' % key in _DATETIME_FIELDS
  88. else map(int, value)
  89. if value and '_%s' % key not in _SCALAR_FIELDS
  90. else int(value) if value
  91. else None if not value and '_%s' % key not in _ZERO_IS_NOT_NONE
  92. else value
  93. for key, value in data.iteritems()
  94. if key != 'type'
  95. }
  96. if data_type == 'rrule':
  97. result.rrule(rrule(**data))
  98. # TODO: implement rdate, exrule, exdate
  99. else:
  100. raise ValueError('Unknown type given')
  101. return result