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.

212 lines
8.4 KiB

  1. # coding: utf-8
  2. # @ 2015 Valentin CHEMIERE @ Akretion
  3. # © @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com>
  4. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  5. from openerp import models, fields, api
  6. import openerp
  7. from openerp import tools
  8. from base64 import b64encode
  9. import os
  10. import datetime
  11. import logging
  12. _logger = logging.getLogger(__name__)
  13. try:
  14. # We use a jinja2 sandboxed environment to render mako templates.
  15. # Note that the rendering does not cover all the mako syntax, in particular
  16. # arbitrary Python statements are not accepted, and not all expressions are
  17. # allowed: only "public" attributes (not starting with '_') of objects may
  18. # be accessed.
  19. # This is done on purpose: it prevents incidental or malicious execution of
  20. # Python code that may break the security of the server.
  21. from jinja2.sandbox import SandboxedEnvironment
  22. mako_template_env = SandboxedEnvironment(
  23. variable_start_string="${",
  24. variable_end_string="}",
  25. line_statement_prefix="%",
  26. trim_blocks=True, # do not output newline after blocks
  27. )
  28. mako_template_env.globals.update({
  29. 'str': str,
  30. 'datetime': datetime,
  31. 'len': len,
  32. 'abs': abs,
  33. 'min': min,
  34. 'max': max,
  35. 'sum': sum,
  36. 'filter': filter,
  37. 'reduce': reduce,
  38. 'map': map,
  39. 'round': round,
  40. })
  41. except ImportError:
  42. _logger.warning("jinja2 not available, templating features will not work!")
  43. class Task(models.Model):
  44. _name = 'external.file.task'
  45. _description = 'External file task'
  46. name = fields.Char(required=True)
  47. method_type = fields.Selection(
  48. [('import', 'Import'), ('export', 'Export')],
  49. required=True)
  50. filename = fields.Char(help='File name which is imported.'
  51. 'You can use file pattern like *.txt'
  52. 'to import all txt files')
  53. filepath = fields.Char(help='Path to imported/exported file')
  54. location_id = fields.Many2one('external.file.location', string='Location',
  55. required=True)
  56. attachment_ids = fields.One2many('ir.attachment.metadata', 'task_id',
  57. string='Attachment')
  58. move_path = fields.Char(string='Move Path',
  59. help='Imported File will be moved to this path')
  60. new_name = fields.Char(string='New Name',
  61. help='Imported File will be renamed to this name'
  62. 'Name can use mako template where obj is an '
  63. 'ir_attachement. template exemple : '
  64. ' ${obj.name}-${obj.create_date}.csv')
  65. md5_check = fields.Boolean(help='Control file integrity after import with'
  66. ' a md5 file')
  67. after_import = fields.Selection(selection='_get_action',
  68. help='Action after import a file')
  69. company_id = fields.Many2one(
  70. 'res.company', 'Company',
  71. default=lambda self: self.env['res.company']._company_default_get(
  72. 'external.file.task'))
  73. file_type = fields.Selection(
  74. selection=[],
  75. string="File Type",
  76. help="The file type determines an import method to be used "
  77. "to parse and transform data before their import in ERP")
  78. active = fields.Boolean(default=True)
  79. def _get_action(self):
  80. return [('rename', 'Rename'),
  81. ('move', 'Move'),
  82. ('move_rename', 'Move & Rename'),
  83. ('delete', 'Delete'),
  84. ]
  85. @api.multi
  86. def _prepare_attachment_vals(self, datas, filename, md5_datas):
  87. self.ensure_one()
  88. vals = {
  89. 'name': filename,
  90. 'datas': b64encode(datas),
  91. 'datas_fname': filename,
  92. 'task_id': self.id,
  93. 'external_hash': md5_datas,
  94. 'file_type': self.file_type or False,
  95. }
  96. return vals
  97. @api.model
  98. def _template_render(self, template, record):
  99. try:
  100. template = mako_template_env.from_string(tools.ustr(template))
  101. except Exception:
  102. _logger.exception("Failed to load template %r", template)
  103. variables = {'obj': record}
  104. try:
  105. render_result = template.render(variables)
  106. except Exception:
  107. _logger.exception(
  108. "Failed to render template %r using values %r" %
  109. (template, variables))
  110. render_result = u""
  111. if render_result == u"False":
  112. render_result = u""
  113. return render_result
  114. @api.model
  115. def run_task_scheduler(self, domain=None):
  116. if domain is None:
  117. domain = []
  118. tasks = self.env['external.file.task'].search(domain)
  119. for task in tasks:
  120. if task.method_type == 'import':
  121. task.run_import()
  122. elif task.method_type == 'export':
  123. task.run_export()
  124. @api.multi
  125. def run_import(self):
  126. self.ensure_one()
  127. protocols = self.env['external.file.location']._get_classes()
  128. cls = protocols.get(self.location_id.protocol)[1]
  129. attach_obj = self.env['ir.attachment.metadata']
  130. with cls.connect(self.location_id) as conn:
  131. md5_datas = ''
  132. for file_name in conn.listdir(path=self.filepath,
  133. wildcard=self.filename or '',
  134. files_only=True):
  135. with api.Environment.manage():
  136. with openerp.registry(
  137. self.env.cr.dbname).cursor() as new_cr:
  138. new_env = api.Environment(new_cr, self.env.uid,
  139. self.env.context)
  140. try:
  141. full_path = os.path.join(self.filepath, file_name)
  142. file_data = conn.open(full_path, 'rb')
  143. datas = file_data.read()
  144. if self.md5_check:
  145. md5_file = conn.open(full_path + '.md5', 'rb')
  146. md5_datas = md5_file.read().rstrip('\r\n')
  147. attach_vals = self._prepare_attachment_vals(
  148. datas, file_name, md5_datas)
  149. attachment = attach_obj.with_env(new_env).create(
  150. attach_vals)
  151. new_full_path = False
  152. if self.after_import == 'rename':
  153. new_name = self._template_render(
  154. self.new_name, attachment)
  155. new_full_path = os.path.join(
  156. self.filepath, new_name)
  157. elif self.after_import == 'move':
  158. new_full_path = os.path.join(
  159. self.move_path, file_name)
  160. elif self.after_import == 'move_rename':
  161. new_name = self._template_render(
  162. self.new_name, attachment)
  163. new_full_path = os.path.join(
  164. self.move_path, new_name)
  165. if new_full_path:
  166. conn.rename(full_path, new_full_path)
  167. if self.md5_check:
  168. conn.rename(
  169. full_path + '.md5',
  170. new_full_path + '/md5')
  171. if self.after_import == 'delete':
  172. conn.remove(full_path)
  173. if self.md5_check:
  174. conn.remove(full_path + '.md5')
  175. except Exception, e:
  176. new_env.cr.rollback()
  177. raise e
  178. else:
  179. new_env.cr.commit()
  180. @api.multi
  181. def run_export(self):
  182. self.ensure_one()
  183. attachment_obj = self.env['ir.attachment.metadata']
  184. attachments = attachment_obj.search(
  185. [('task_id', '=', self.id), ('state', '!=', 'done')])
  186. for attachment in attachments:
  187. attachment.run()