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