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.

200 lines
7.3 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  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. from odoo.osv import expression
  8. _logger = logging.getLogger(__name__)
  9. try:
  10. # We use a jinja2 sandboxed environment to render mako templates.
  11. # Note that the rendering does not cover all the mako syntax, in particular
  12. # arbitrary Python statements are not accepted, and not all expressions are
  13. # allowed: only "public" attributes (not starting with '_') of objects may
  14. # be accessed.
  15. # This is done on purpose: it prevents incidental or malicious execution of
  16. # Python code that may break the security of the server.
  17. from jinja2.sandbox import SandboxedEnvironment
  18. mako_template_env = SandboxedEnvironment(
  19. variable_start_string="${",
  20. variable_end_string="}",
  21. line_statement_prefix="%",
  22. trim_blocks=True, # do not output newline after blocks
  23. )
  24. mako_template_env.globals.update(
  25. {
  26. "str": str,
  27. "datetime": datetime,
  28. "len": len,
  29. "abs": abs,
  30. "min": min,
  31. "max": max,
  32. "sum": sum,
  33. "filter": filter,
  34. "map": map,
  35. "round": round,
  36. }
  37. )
  38. except ImportError:
  39. _logger.warning("jinja2 not available, templating features will not work!")
  40. class AttachmentSynchronizeTask(models.Model):
  41. _name = "attachment.synchronize.task"
  42. _description = "Attachment synchronize task"
  43. name = fields.Char(required=True)
  44. method_type = fields.Selection(
  45. [("import", "Import"), ("export", "Export")], required=True
  46. )
  47. pattern = fields.Char(
  48. help="Used to select the files to be imported. Import all the files if empty."
  49. )
  50. filepath = fields.Char(
  51. string="File Path", help="Path to imported/exported files in the Backend"
  52. )
  53. backend_id = fields.Many2one("storage.backend", string="Backend")
  54. attachment_ids = fields.One2many("attachment.queue", "task_id", string="Attachment")
  55. move_path = fields.Char(
  56. string="Move Path", help="Imported File will be moved to this path"
  57. )
  58. new_name = fields.Char(
  59. string="New Name",
  60. help="Imported File will be renamed to this name\n"
  61. "Name can use mako template where obj is an "
  62. "ir_attachement. template exemple : "
  63. " ${obj.name}-${obj.create_date}.csv",
  64. )
  65. after_import = fields.Selection(
  66. selection=[
  67. ("rename", "Rename"),
  68. ("move", "Move"),
  69. ("move_rename", "Move & Rename"),
  70. ("delete", "Delete"),
  71. ],
  72. help="Action after import a file",
  73. )
  74. file_type = fields.Selection(
  75. selection=[],
  76. string="File Type",
  77. help="The file type indicates what Odoo will do with the files once imported",
  78. )
  79. enabled = fields.Boolean("Enabled", default=True)
  80. avoid_duplicated_files = fields.Boolean(
  81. string="Avoid duplicated files importation",
  82. help="If checked, will avoid duplication file import",
  83. )
  84. emails = fields.Char(
  85. string="Notification Emails",
  86. help="These emails will receive a notification in case of the task failure",
  87. )
  88. def _prepare_attachment_vals(self, data, filename):
  89. self.ensure_one()
  90. vals = {
  91. "name": filename,
  92. "datas": data,
  93. "datas_fname": filename,
  94. "task_id": self.id,
  95. "file_type": self.file_type or False,
  96. }
  97. return vals
  98. @api.model
  99. def _template_render(self, template, record):
  100. try:
  101. template = mako_template_env.from_string(tools.ustr(template))
  102. except Exception:
  103. _logger.exception("Failed to load template %r", template)
  104. variables = {"obj": record}
  105. try:
  106. render_result = template.render(variables)
  107. except Exception:
  108. _logger.exception(
  109. "Failed to render template %r using values %r" % (template, variables)
  110. )
  111. render_result = u""
  112. if render_result == u"False":
  113. render_result = u""
  114. return render_result
  115. @api.model
  116. def run_task_import_scheduler(self, domain=None):
  117. if domain is None:
  118. domain = []
  119. domain = expression.AND(
  120. [domain, [("method_type", "=", "import"), ("enabled", "=", True)]]
  121. )
  122. for task in self.search(domain):
  123. task.run_import()
  124. def run_import(self):
  125. self.ensure_one()
  126. attach_obj = self.env["attachment.queue"]
  127. backend = self.backend_id
  128. filepath = self.filepath or ""
  129. filenames = backend._list(relative_path=filepath, pattern=self.pattern)
  130. if self.avoid_duplicated_files:
  131. filenames = self._file_to_import(filenames)
  132. total_import = 0
  133. for file_name in filenames:
  134. with api.Environment.manage():
  135. with odoo.registry(self.env.cr.dbname).cursor() as new_cr:
  136. new_env = api.Environment(new_cr, self.env.uid, self.env.context)
  137. try:
  138. full_absolute_path = os.path.join(filepath, file_name)
  139. data = backend._get_b64_data(full_absolute_path)
  140. attach_vals = self._prepare_attachment_vals(data, file_name)
  141. attachment = attach_obj.with_env(new_env).create(attach_vals)
  142. new_full_path = False
  143. if self.after_import == "rename":
  144. new_name = self._template_render(self.new_name, attachment)
  145. new_full_path = os.path.join(filepath, new_name)
  146. elif self.after_import == "move":
  147. new_full_path = os.path.join(self.move_path, file_name)
  148. elif self.after_import == "move_rename":
  149. new_name = self._template_render(self.new_name, attachment)
  150. new_full_path = os.path.join(self.move_path, new_name)
  151. if new_full_path:
  152. backend._add_b64_data(new_full_path, data)
  153. if self.after_import in (
  154. "delete",
  155. "rename",
  156. "move",
  157. "move_rename",
  158. ):
  159. backend._delete(full_absolute_path)
  160. total_import += 1
  161. except Exception as e:
  162. new_env.cr.rollback()
  163. raise e
  164. else:
  165. new_env.cr.commit()
  166. _logger.info("Run import complete! Imported {0} files".format(total_import))
  167. def _file_to_import(self, filenames):
  168. imported = (
  169. self.env["attachment.queue"]
  170. .search([("name", "in", filenames)])
  171. .mapped("name")
  172. )
  173. return list(set(filenames) - set(imported))
  174. def run_export(self):
  175. for task in self:
  176. task.attachment_ids.filtered(lambda a: a.state == "pending").run()
  177. def button_toogle_enabled(self):
  178. for rec in self:
  179. rec.enabled = not rec.enabled
  180. def button_duplicate_record(self):
  181. self.ensure_one()
  182. self.copy({"enabled": False})