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.

102 lines
3.8 KiB

  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, YEARLY
  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.Model):
  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 __exit__(self):
  54. pass
  55. def __nonzero__(self):
  56. return bool(self._rrule)
  57. def __repr__(self):
  58. return ', '.join(str(a) for a in self)
  59. def __getitem__(self, key):
  60. return rruleset.__getitem__(self(), key)
  61. def __getslice__(self, i, j):
  62. return rruleset.__getitem__(self(), slice(i, j))
  63. class FieldRRule(fields.Serialized):
  64. def convert_to_cache(self, value, record, validate=True):
  65. result = SerializableRRuleSet()
  66. if not value:
  67. return result
  68. if isinstance(value, SerializableRRuleSet):
  69. return value
  70. assert isinstance(value, list), 'An RRULE\'s content must be a list'
  71. for data in value:
  72. assert isinstance(data, dict), 'The list must contain dictionaries'
  73. assert 'type' in data, 'The dictionary must contain a type'
  74. data_type = data['type']
  75. data = {
  76. key: fields.Datetime.from_string(value)
  77. if '_%s' % key in _DATETIME_FIELDS
  78. else map(int, value)
  79. if value and '_%s' % key not in _SCALAR_FIELDS
  80. else int(value) if value
  81. else None if not value and '_%s' % key not in _ZERO_IS_NOT_NONE
  82. else value
  83. for key, value in data.iteritems()
  84. if key != 'type'
  85. }
  86. if data_type == 'rrule':
  87. result.rrule(rrule(**data))
  88. # TODO: implement rdate, exrule, exdate
  89. else:
  90. raise ValueError('Unknown type given')
  91. return result