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.

176 lines
7.1 KiB

  1. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  2. import datetime
  3. import logging
  4. import os
  5. import odoo
  6. from odoo import api, fields, models, tools
  7. _logger = logging.getLogger(__name__)
  8. try:
  9. # We use a jinja2 sandboxed environment to render mako templates.
  10. # Note that the rendering does not cover all the mako syntax, in particular
  11. # arbitrary Python statements are not accepted, and not all expressions are
  12. # allowed: only "public" attributes (not starting with '_') of objects may
  13. # be accessed.
  14. # This is done on purpose: it prevents incidental or malicious execution of
  15. # Python code that may break the security of the server.
  16. from jinja2.sandbox import SandboxedEnvironment
  17. mako_template_env = SandboxedEnvironment(
  18. variable_start_string="${",
  19. variable_end_string="}",
  20. line_statement_prefix="%",
  21. trim_blocks=True, # do not output newline after blocks
  22. )
  23. mako_template_env.globals.update({
  24. 'str': str,
  25. 'datetime': datetime,
  26. 'len': len,
  27. 'abs': abs,
  28. 'min': min,
  29. 'max': max,
  30. 'sum': sum,
  31. 'filter': filter,
  32. 'map': map,
  33. 'round': round,
  34. })
  35. except ImportError:
  36. _logger.warning("jinja2 not available, templating features will not work!")
  37. class StorageTask(models.Model):
  38. _name = 'storage.backend.task'
  39. _description = 'Storage Backend task'
  40. name = fields.Char(required=True)
  41. method_type = fields.Selection(
  42. [('import', 'Import'), ('export', 'Export')],
  43. required=True)
  44. pattern = fields.Char(help='File name which is imported.'
  45. 'The system will check if the remote file at '
  46. 'least contains the pattern in its name. '
  47. 'Leave it empty to import all files')
  48. filepath = fields.Char(help='Path to imported/exported file')
  49. backend_id = fields.Many2one(
  50. 'storage.backend', string='Backend', required=True)
  51. attachment_ids = fields.One2many('ir.attachment.metadata', 'task_id',
  52. string='Attachment')
  53. move_path = fields.Char(string='Move Path',
  54. help='Imported File will be moved to this path')
  55. new_name = fields.Char(string='New Name',
  56. help='Imported File will be renamed to this name\n'
  57. 'Name can use mako template where obj is an '
  58. 'ir_attachement. template exemple : '
  59. ' ${obj.name}-${obj.create_date}.csv')
  60. after_import = fields.Selection(
  61. selection=[
  62. ('rename', 'Rename'),
  63. ('move', 'Move'),
  64. ('move_rename', 'Move & Rename'),
  65. ('delete', 'Delete'),
  66. ], help='Action after import a file')
  67. file_type = fields.Selection(
  68. selection=[],
  69. string="File Type",
  70. help="The file type determines an import method to be used "
  71. "to parse and transform data before their import in ERP")
  72. enabled = fields.Boolean('Enabled', default=True)
  73. check_duplicated_files = fields.Boolean(
  74. string='Check duplicated files',
  75. help='If checked, will avoid duplication file import')
  76. @api.multi
  77. def _prepare_attachment_vals(self, datas, filename):
  78. self.ensure_one()
  79. vals = {
  80. 'name': filename,
  81. 'datas': datas,
  82. 'datas_fname': filename,
  83. 'task_id': self.id,
  84. 'file_type': self.file_type or False,
  85. }
  86. return vals
  87. @api.model
  88. def _template_render(self, template, record):
  89. try:
  90. template = mako_template_env.from_string(tools.ustr(template))
  91. except Exception:
  92. _logger.exception("Failed to load template %r", template)
  93. variables = {'obj': record}
  94. try:
  95. render_result = template.render(variables)
  96. except Exception:
  97. _logger.exception(
  98. "Failed to render template %r using values %r" %
  99. (template, variables))
  100. render_result = u""
  101. if render_result == u"False":
  102. render_result = u""
  103. return render_result
  104. @api.model
  105. def run_task_scheduler(self, domain=list()):
  106. if ('method_type', '=', 'import') not in domain:
  107. domain.append([('method_type', '=', 'import')])
  108. domain.append([('enabled', '=', True)])
  109. tasks = self.env['storage.backend'].search(domain)
  110. for task in tasks:
  111. task.run_import()
  112. @api.multi
  113. def run_import(self):
  114. self.ensure_one()
  115. attach_obj = self.env['ir.attachment.metadata']
  116. backend = self.backend_id
  117. filenames = backend._list(
  118. relative_path=self.filepath, pattern=self.pattern)
  119. if self.check_duplicated_files:
  120. filenames = self._file_to_import(filenames)
  121. for file_name in filenames:
  122. with api.Environment.manage():
  123. with odoo.registry(
  124. self.env.cr.dbname).cursor() as new_cr:
  125. new_env = api.Environment(new_cr, self.env.uid,
  126. self.env.context)
  127. try:
  128. full_absolute_path = os.path.join(
  129. self.filepath, file_name)
  130. datas = backend._get_b64_data(full_absolute_path)
  131. attach_vals = self._prepare_attachment_vals(
  132. datas, file_name)
  133. attachment = attach_obj.with_env(new_env).create(
  134. attach_vals)
  135. new_full_path = False
  136. if self.after_import == 'rename':
  137. new_name = self._template_render(
  138. self.new_name, attachment)
  139. new_full_path = os.path.join(
  140. self.filepath, new_name)
  141. elif self.after_import == 'move':
  142. new_full_path = os.path.join(
  143. self.move_path, file_name)
  144. elif self.after_import == 'move_rename':
  145. new_name = self._template_render(
  146. self.new_name, attachment)
  147. new_full_path = os.path.join(
  148. self.move_path, new_name)
  149. if new_full_path:
  150. backend._add_b64_data(new_full_path, datas)
  151. if self.after_import in (
  152. 'delete', 'rename', 'move', 'move_rename'
  153. ):
  154. backend._delete(full_absolute_path)
  155. except Exception as e:
  156. new_env.cr.rollback()
  157. raise e
  158. else:
  159. new_env.cr.commit()
  160. def _file_to_import(self, filenames):
  161. imported = self.attachment_ids.search([('name', 'in', [n for n in filenames])]).mapped('name')
  162. return list(set(filenames) - set(imported))