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.

329 lines
11 KiB

  1. ###################################################################################
  2. #
  3. # Copyright (C) 2018 MuK IT GmbH
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU Affero General Public License as
  7. # published by the Free Software Foundation, either version 3 of the
  8. # License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU Affero General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Affero General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #
  18. ###################################################################################
  19. import time
  20. import base64
  21. import logging
  22. import datetime
  23. import dateutil
  24. from pytz import timezone
  25. from odoo import _
  26. from odoo import models, api, fields
  27. from odoo.exceptions import ValidationError, Warning
  28. from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
  29. from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
  30. from odoo.tools.safe_eval import safe_eval, test_python_expr
  31. _logger = logging.getLogger(__name__)
  32. class AutoVacuumRules(models.Model):
  33. _name = 'muk_autovacuum.rules'
  34. _description = "Auto Vacuum Rules"
  35. _order = "sequence asc"
  36. #----------------------------------------------------------
  37. # Defaults
  38. #----------------------------------------------------------
  39. def _default_sequence(self):
  40. record = self.sudo().search([], order='sequence desc', limit=1)
  41. if record.exists():
  42. return record.sequence + 1
  43. else:
  44. return 1
  45. #----------------------------------------------------------
  46. # Database
  47. #----------------------------------------------------------
  48. name = fields.Char(
  49. string='Name',
  50. required=True)
  51. active = fields.Boolean(
  52. string='Active',
  53. default=True)
  54. state = fields.Selection(
  55. selection=[
  56. ('time', 'Time Based'),
  57. ('size', 'Size Based'),
  58. ('domain', 'Domain Based'),
  59. ('code', 'Code Based')],
  60. string='Rule Type',
  61. default='time',
  62. required=True)
  63. sequence = fields.Integer(
  64. string='Sequence',
  65. default=_default_sequence,
  66. required=True)
  67. model = fields.Many2one(
  68. comodel_name='ir.model',
  69. string="Model",
  70. required=True,
  71. ondelete='cascade',
  72. help="Model on which the rule is applied.")
  73. model_name = fields.Char(
  74. related='model.model',
  75. string="Model Name",
  76. readonly=True,
  77. store=True)
  78. time_field = fields.Many2one(
  79. comodel_name='ir.model.fields',
  80. domain="[('model_id', '=', model), ('ttype', '=', 'datetime')]",
  81. string='Time Field',
  82. ondelete='cascade',
  83. states={
  84. 'time': [('required', True)],
  85. 'size': [('invisible', True)],
  86. 'domain': [('invisible', True)],
  87. 'code': [('invisible', True)]})
  88. time_type = fields.Selection(
  89. selection=[
  90. ('minutes', 'Minutes'),
  91. ('hours', 'Hours'),
  92. ('days', 'Days'),
  93. ('weeks', 'Weeks'),
  94. ('months', 'Months'),
  95. ('years', 'Years')],
  96. string='Time Unit',
  97. default='months',
  98. states={
  99. 'time': [('required', True)],
  100. 'size': [('invisible', True)],
  101. 'domain': [('invisible', True)],
  102. 'code': [('invisible', True)]})
  103. time = fields.Integer(
  104. string='Time',
  105. default=1,
  106. states={
  107. 'time': [('required', True)],
  108. 'size': [('invisible', True)],
  109. 'domain': [('invisible', True)],
  110. 'code': [('invisible', True)]},
  111. help="Delete older data than x.")
  112. size_type = fields.Selection(
  113. selection=[
  114. ('fixed', 'Fixed Value'),
  115. ('parameter', 'System Parameter')],
  116. string='Size Type',
  117. default='fixed',
  118. states={
  119. 'time': [('invisible', True)],
  120. 'size': [('required', True)],
  121. 'domain': [('invisible', True)],
  122. 'code': [('invisible', True)]})
  123. size_parameter = fields.Many2one(
  124. comodel_name='ir.config_parameter',
  125. string='System Parameter',
  126. ondelete='cascade',
  127. states={
  128. 'time': [('invisible', True)],
  129. 'size': [('required', True)],
  130. 'domain': [('invisible', True)],
  131. 'code': [('invisible', True)]})
  132. size_parameter_value = fields.Integer(
  133. compute='_compute_size_parameter_value',
  134. string='Size Value',
  135. states={
  136. 'time': [('invisible', True)],
  137. 'size': [('readonly', True)],
  138. 'domain': [('invisible', True)],
  139. 'code': [('invisible', True)]},
  140. help="Delete records with am index greater than x.")
  141. size_order = fields.Char(
  142. string='Size Order',
  143. default='create_date desc',
  144. states={
  145. 'time': [('invisible', True)],
  146. 'size': [('required', True)],
  147. 'domain': [('invisible', True)],
  148. 'code': [('invisible', True)]},
  149. help="Order by which the index is defined.")
  150. size = fields.Integer(
  151. string='Size',
  152. default=200,
  153. states={
  154. 'time': [('invisible', True)],
  155. 'size': [('required', True)],
  156. 'domain': [('invisible', True)],
  157. 'code': [('invisible', True)]},
  158. help="Delete records with am index greater than x.")
  159. domain = fields.Char(
  160. string='Domain',
  161. states={
  162. 'time': [('invisible', True)],
  163. 'size': [('invisible', True)],
  164. 'domain': [('required', True)],
  165. 'code': [('invisible', True)]},
  166. help="Delete all records which match the domain.")
  167. code = fields.Text(
  168. string='Code',
  169. states={
  170. 'time': [('invisible', True)],
  171. 'size': [('invisible', True)],
  172. 'domain': [('invisible', True)] ,
  173. 'code': [('required', True)]},
  174. default="# Enter Python code here. Help about Python expression is available in the help tab of this document.",
  175. help="Code which will be executed during the clean up.")
  176. protect_starred = fields.Boolean(
  177. string='Protect Starred',
  178. default=True,
  179. states={
  180. 'time': [('invisible', False)],
  181. 'size': [('invisible', True)],
  182. 'domain': [('invisible', True)],
  183. 'code': [('invisible', True)]},
  184. help="""Do not delete starred records.
  185. Checks for the following fields:
  186. - starred
  187. - favorite
  188. - is_starred
  189. - is_favorite""")
  190. only_inactive = fields.Boolean(
  191. string='Only Archived',
  192. default=False,
  193. states={
  194. 'time': [('invisible', False)],
  195. 'size': [('invisible', True)],
  196. 'domain': [('invisible', True)],
  197. 'code': [('invisible', True)]},
  198. help="Only delete archived records.")
  199. only_attachments = fields.Boolean(
  200. string='Only Attachments',
  201. default=False,
  202. states={
  203. 'time': [('invisible', False)],
  204. 'size': [('invisible', False)],
  205. 'domain': [('invisible', False)],
  206. 'code': [('invisible', True)]},
  207. help="Only delete record attachments.")
  208. #----------------------------------------------------------
  209. # Functions
  210. #----------------------------------------------------------
  211. @api.model
  212. def _get_eval_domain_context(self):
  213. return {
  214. 'datetime': datetime,
  215. 'dateutil': dateutil,
  216. 'timezone': timezone,
  217. 'time': time,
  218. 'uid': self.env.uid,
  219. 'user': self.env.user
  220. }
  221. @api.model
  222. def _get_eval_code_context(self, rule):
  223. return {
  224. 'env': self.env,
  225. 'model': self.env[rule.model_name],
  226. 'uid': self.env.user.id,
  227. 'user': self.env.user,
  228. 'time': time,
  229. 'datetime': datetime,
  230. 'dateutil': dateutil,
  231. 'timezone': timezone,
  232. 'b64encode': base64.b64encode,
  233. 'b64decode': base64.b64decode,
  234. 'date_format': DEFAULT_SERVER_DATE_FORMAT,
  235. 'datetime_format': DEFAULT_SERVER_DATETIME_FORMAT,
  236. 'Warning': Warning,
  237. 'logger': logging.getLogger("%s (%s)" % (__name__, rule.name)),
  238. }
  239. #----------------------------------------------------------
  240. # View
  241. #----------------------------------------------------------
  242. @api.onchange('model')
  243. def _onchange_model(self):
  244. field_domain = [
  245. ('model_id', '=', self.model.id),
  246. ('ttype', '=', 'datetime'),
  247. ('name', '=', 'create_date')]
  248. record = self.env['ir.model.fields'].sudo().search(field_domain, limit=1)
  249. if record.exists():
  250. self.time_field = record
  251. else:
  252. return None
  253. @api.model
  254. def get_import_templates(self):
  255. return [{
  256. 'label': _('Import Template for Auto Vacuum Rules'),
  257. 'template': '/muk_autovacuum/static/xls/muk_autovacuum_rules.xls'
  258. }]
  259. #----------------------------------------------------------
  260. # Read
  261. #----------------------------------------------------------
  262. @api.depends('size_parameter')
  263. def _compute_size_parameter_value(self):
  264. for record in self:
  265. try:
  266. record.size_parameter_value = int(record.size_parameter.value)
  267. except ValueError:
  268. record.size_parameter_value = None
  269. #----------------------------------------------------------
  270. # Create, Update, Delete
  271. #----------------------------------------------------------
  272. @api.constrains('code')
  273. def _check_code(self):
  274. for record in self.sudo().filtered('code'):
  275. message = test_python_expr(expr=record.code.strip(), mode="exec")
  276. if message:
  277. raise ValidationError(message)
  278. @api.constrains(
  279. 'state', 'model', 'domain', 'code',
  280. 'time_field', 'time_type', 'time',
  281. 'size_type', 'size_parameter', 'size_order', 'size')
  282. def _validate(self):
  283. validators = {
  284. 'time': lambda rec: rec.time_field and rec.time_type and rec.time,
  285. 'size': lambda rec: rec.size_order and (rec.size_parameter or rec.size),
  286. 'domain': lambda rec: rec.domain,
  287. 'code': lambda rec: rec.code,
  288. }
  289. for record in self:
  290. if not validators[record.state](record):
  291. raise ValidationError(_("Rule validation has failed!"))