Browse Source
Port external_file_location to v9
Port external_file_location to v9
Add company_id on external location and external taskpull/516/head
26 changed files with 1364 additions and 747 deletions
-
47external_file_location/README.rst
-
12external_file_location/__openerp__.py
-
4external_file_location/data/cron.xml
-
99external_file_location/demo/task_demo.xml
-
347external_file_location/i18n/external_file_location.pot
-
347external_file_location/i18n/fr.po
-
30external_file_location/models/attachment.py
-
84external_file_location/models/helper.py
-
71external_file_location/models/location.py
-
235external_file_location/models/task.py
-
1external_file_location/tasks/__init__.py
-
196external_file_location/tasks/abstract_fs.py
-
28external_file_location/tasks/abstract_task.py
-
50external_file_location/tasks/filestore.py
-
55external_file_location/tasks/ftp.py
-
55external_file_location/tasks/sftp.py
-
2external_file_location/tests/__init__.py
-
32external_file_location/tests/common.py
-
19external_file_location/tests/mock_server.py
-
50external_file_location/tests/test_filestore.py
-
86external_file_location/tests/test_ftp.py
-
213external_file_location/tests/test_sftp.py
-
28external_file_location/views/attachment_view.xml
-
7external_file_location/views/location_view.xml
-
12external_file_location/views/task_view.xml
-
1requirements.txt
@ -0,0 +1,99 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<openerp> |
||||
|
<data> |
||||
|
|
||||
|
<record id="location_ftp" model="external.file.location"> |
||||
|
<field name="name">TEST FTP</field> |
||||
|
<field name="protocol">ftp</field> |
||||
|
<field name="address">my-ftp-address</field> |
||||
|
<field name="login">my-ftp-user</field> |
||||
|
<field name="password">my-ftp-password</field> |
||||
|
<field name="port">21</field> |
||||
|
</record> |
||||
|
<record id="location_sftp" model="external.file.location"> |
||||
|
<field name="name">TEST SFTP</field> |
||||
|
<field name="protocol">sftp</field> |
||||
|
<field name="address">my-sftp-address</field> |
||||
|
<field name="login">my-sftp-user</field> |
||||
|
<field name="password">my-sftp-password</field> |
||||
|
<field name="port">22</field> |
||||
|
</record> |
||||
|
<record id="location_filestore" model="external.file.location"> |
||||
|
<field name="name">TEST File Store</field> |
||||
|
<field name="protocol">file_store</field> |
||||
|
<field name="filestore_rootpath">/</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="ftp_import_task" model="external.file.task"> |
||||
|
<field name="method_type">import</field> |
||||
|
<field name="location_id" eval="ref('external_file_location.location_ftp')"/> |
||||
|
<field name="filename">test-import-ftp.txt</field> |
||||
|
<field name="filepath">/home/user/test</field> |
||||
|
<field name="name">Import FTP Task</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="ftp_export_task" model="external.file.task"> |
||||
|
<field name="method_type">export</field> |
||||
|
<field name="location_id" eval="ref('external_file_location.location_ftp')"/> |
||||
|
<field name="filepath">/home/user/test</field> |
||||
|
<field name="name">Export FTP Task</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="sftp_import_task" model="external.file.task"> |
||||
|
<field name="method_type">import</field> |
||||
|
<field name="location_id" eval="ref('external_file_location.location_sftp')"/> |
||||
|
<field name="filename">test-import-sftp.txt</field> |
||||
|
<field name="filepath">/home/user/test</field> |
||||
|
<field name="name">Import SFTP Task</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="sftp_export_task" model="external.file.task"> |
||||
|
<field name="method_type">export</field> |
||||
|
<field name="location_id" eval="ref('external_file_location.location_sftp')"/> |
||||
|
<field name="filepath">/home/user/test</field> |
||||
|
<field name="name">Export SFTP Task</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="filestore_import_task" model="external.file.task"> |
||||
|
<field name="method_type">import</field> |
||||
|
<field name="location_id" eval="ref('external_file_location.location_filestore')"/> |
||||
|
<field name="filename">test-import-filestore.txt</field> |
||||
|
<field name="filepath">/home/user/test</field> |
||||
|
<field name="name">Import filestore Task</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="filestore_export_task" model="external.file.task"> |
||||
|
<field name="method_type">export</field> |
||||
|
<field name="location_id" eval="ref('external_file_location.location_filestore')"/> |
||||
|
<field name="filepath">/home/user/test</field> |
||||
|
<field name="name">Export filestore Task</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="ir_attachment_export_file_sftp" model="ir.attachment.metadata"> |
||||
|
<field name="name">Sftp text export file</field> |
||||
|
<field name="datas">dGVzdCBzZnRwIGZpbGUgZXhwb3J0</field> |
||||
|
<field name="datas_fname">sftp_test_export.txt</field> |
||||
|
<field name="task_id" eval="ref('external_file_location.sftp_export_task')"/> |
||||
|
<field name="file_type">export_external_location</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="ir_attachment_export_file_ftp" model="ir.attachment.metadata"> |
||||
|
<field name="name">ftp text export file</field> |
||||
|
<field name="datas">dGVzdCBmdHAgZmlsZSBleHBvcnQ=</field> |
||||
|
<field name="datas_fname">ftp_test_export.txt</field> |
||||
|
<field name="task_id" eval="ref('external_file_location.ftp_export_task')"/> |
||||
|
<field name="file_type">export_external_location</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="ir_attachment_export_file_filestore" model="ir.attachment.metadata"> |
||||
|
<field name="name">filestore text export file</field> |
||||
|
<field name="datas">dGVzdCBmaWxlc3RvcmUgZmlsZSBleHBvcnQ=</field> |
||||
|
<field name="datas_fname">filestore_test_export.txt</field> |
||||
|
<field name="task_id" eval="ref('external_file_location.filestore_export_task')"/> |
||||
|
<field name="file_type">export_external_location</field> |
||||
|
</record> |
||||
|
|
||||
|
|
||||
|
|
||||
|
</data> |
||||
|
</openerp> |
@ -0,0 +1,347 @@ |
|||||
|
# Translation of Odoo Server. |
||||
|
# This file contains the translation of the following modules: |
||||
|
# * external_file_location |
||||
|
# |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: Odoo Server 9.0c\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2016-08-10 16:51+0000\n" |
||||
|
"PO-Revision-Date: 2016-08-10 16:51+0000\n" |
||||
|
"Last-Translator: <>\n" |
||||
|
"Language-Team: \n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: \n" |
||||
|
"Plural-Forms: \n" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_needaction |
||||
|
msgid "Action Needed" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_after_import |
||||
|
msgid "Action after import a file" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_address |
||||
|
msgid "Address" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_after_import |
||||
|
msgid "After import" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_attachment_ids |
||||
|
msgid "Attachment" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_md5_check |
||||
|
msgid "Control file integrity after import with a md5 file" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_create_uid |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_create_uid |
||||
|
msgid "Created by" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_create_date |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_create_date |
||||
|
msgid "Created on" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_form |
||||
|
msgid "Data importation setting" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_last_post |
||||
|
msgid "Date of the last message posted on the record." |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model,name:external_file_location.model_external_file_location |
||||
|
msgid "Description" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_display_name |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_display_name |
||||
|
msgid "Display Name" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: selection:external.file.task,method_type:0 |
||||
|
msgid "Export" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model,name:external_file_location.model_external_file_task |
||||
|
msgid "External file task" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_location_tree |
||||
|
msgid "File Location" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.actions.act_window,name:external_file_location.action_location |
||||
|
#: model:ir.ui.menu,name:external_file_location.menu_ir_location |
||||
|
msgid "File Locations" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.menu,name:external_file_location.menu_file_exchange |
||||
|
msgid "File exchange" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_filename |
||||
|
msgid "File name which is imported.You can use file pattern like *.txtto import all txt files" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_file_type |
||||
|
msgid "File type" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_filestore_rootpath |
||||
|
msgid "FileStore Root Path" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_filename |
||||
|
msgid "Filename" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_filepath |
||||
|
msgid "Filepath" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_follower_ids |
||||
|
msgid "Followers" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_channel_ids |
||||
|
msgid "Followers (Channels)" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_partner_ids |
||||
|
msgid "Followers (Partners)" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_hide_login |
||||
|
msgid "Hide login" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_hide_password |
||||
|
msgid "Hide password" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_hide_port |
||||
|
msgid "Hide port" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_id |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_id |
||||
|
msgid "ID" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_unread |
||||
|
msgid "If checked new messages require your attention." |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_needaction |
||||
|
msgid "If checked, new messages require your attention." |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: selection:external.file.task,method_type:0 |
||||
|
msgid "Import" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_move_path |
||||
|
msgid "Imported File will be moved to this path" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_new_name |
||||
|
msgid "Imported File will be renamed to this nameName can use mako template where obj is an ir_attachement. template exemple : ${obj.name}-${obj.create_date}.csv" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_is_follower |
||||
|
msgid "Is Follower" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_last_post |
||||
|
msgid "Last Message Date" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location___last_update |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task___last_update |
||||
|
msgid "Last Modified on" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_write_uid |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_write_uid |
||||
|
msgid "Last Updated by" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_write_date |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_write_date |
||||
|
msgid "Last Updated on" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_location_id |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_location_id |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_location_form |
||||
|
msgid "Location" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_login |
||||
|
msgid "Login" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_md5_check |
||||
|
msgid "Md5 check" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_ids |
||||
|
msgid "Messages" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_method_type |
||||
|
msgid "Method type" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_move_path |
||||
|
msgid "Move path" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_name |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_name |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_location_form |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_form |
||||
|
msgid "Name" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_new_name |
||||
|
msgid "New name" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_needaction_counter |
||||
|
msgid "Number of Actions" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_needaction_counter |
||||
|
msgid "Number of messages which requires an action" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_unread_counter |
||||
|
msgid "Number of unread messages" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_password |
||||
|
msgid "Password" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_filepath |
||||
|
msgid "Path to imported/exported file" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_port |
||||
|
msgid "Port" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_protocol |
||||
|
msgid "Protocol" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_form |
||||
|
msgid "Run" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_location_filestore_rootpath |
||||
|
msgid "Server's root path" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_task_id |
||||
|
msgid "Task" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_task_ids |
||||
|
msgid "Task ids" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_location_form |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_form |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_tree |
||||
|
msgid "Tasks" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_file_type |
||||
|
msgid "The file type determines an import method to be used to parse and transform data before their import in ERP" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_unread |
||||
|
msgid "Unread Messages" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_unread_counter |
||||
|
msgid "Unread Messages Counter" |
||||
|
msgstr "" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model,name:external_file_location.model_ir_attachment_metadata |
||||
|
msgid "ir.attachment.metadata" |
||||
|
msgstr "" |
||||
|
|
@ -0,0 +1,347 @@ |
|||||
|
# Translation of Odoo Server. |
||||
|
# This file contains the translation of the following modules: |
||||
|
# * external_file_location |
||||
|
# |
||||
|
msgid "" |
||||
|
msgstr "" |
||||
|
"Project-Id-Version: Odoo Server 9.0c\n" |
||||
|
"Report-Msgid-Bugs-To: \n" |
||||
|
"POT-Creation-Date: 2016-08-10 16:51+0000\n" |
||||
|
"PO-Revision-Date: 2016-08-10 16:51+0000\n" |
||||
|
"Last-Translator: <>\n" |
||||
|
"Language-Team: \n" |
||||
|
"MIME-Version: 1.0\n" |
||||
|
"Content-Type: text/plain; charset=UTF-8\n" |
||||
|
"Content-Transfer-Encoding: \n" |
||||
|
"Plural-Forms: \n" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_needaction |
||||
|
msgid "Action Needed" |
||||
|
msgstr "A besoin d'une action" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_after_import |
||||
|
msgid "Action after import a file" |
||||
|
msgstr "Action après l'import du fichier" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_address |
||||
|
msgid "Address" |
||||
|
msgstr "Addresse" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_after_import |
||||
|
msgid "After import" |
||||
|
msgstr "Après import" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_attachment_ids |
||||
|
msgid "Attachment" |
||||
|
msgstr "Pièce jointe" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_md5_check |
||||
|
msgid "Control file integrity after import with a md5 file" |
||||
|
msgstr "Contrôle l'intégrité du fichier après l'import avec un fichier md5" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_create_uid |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_create_uid |
||||
|
msgid "Created by" |
||||
|
msgstr "Créé par" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_create_date |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_create_date |
||||
|
msgid "Created on" |
||||
|
msgstr "Créé le" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_form |
||||
|
msgid "Data importation setting" |
||||
|
msgstr "Data importation setting" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_last_post |
||||
|
msgid "Date of the last message posted on the record." |
||||
|
msgstr "Date du dernier message publié sur cet enregistrement" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model,name:external_file_location.model_external_file_location |
||||
|
msgid "Description" |
||||
|
msgstr "Description" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_display_name |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_display_name |
||||
|
msgid "Display Name" |
||||
|
msgstr "Afficher le nom" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: selection:external.file.task,method_type:0 |
||||
|
msgid "Export" |
||||
|
msgstr "Export" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model,name:external_file_location.model_external_file_task |
||||
|
msgid "External file task" |
||||
|
msgstr "Tache" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_location_tree |
||||
|
msgid "File Location" |
||||
|
msgstr "Emplacement fichier" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.actions.act_window,name:external_file_location.action_location |
||||
|
#: model:ir.ui.menu,name:external_file_location.menu_ir_location |
||||
|
msgid "File Locations" |
||||
|
msgstr "Emplacements fichiers" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.menu,name:external_file_location.menu_file_exchange |
||||
|
msgid "File exchange" |
||||
|
msgstr "Echange de fichier" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_filename |
||||
|
msgid "File name which is imported.You can use file pattern like *.txtto import all txt files" |
||||
|
msgstr "Nom du fichier importé. Vous pouvez utiliser une expression comme *.txt pour importer tous les fichiers txt" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_file_type |
||||
|
msgid "File type" |
||||
|
msgstr "Type de fichier" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_filestore_rootpath |
||||
|
msgid "FileStore Root Path" |
||||
|
msgstr "Emplacement racine" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_filename |
||||
|
msgid "Filename" |
||||
|
msgstr "Nom du fichier" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_filepath |
||||
|
msgid "Filepath" |
||||
|
msgstr "Chemin" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_follower_ids |
||||
|
msgid "Followers" |
||||
|
msgstr "Abonnés" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_channel_ids |
||||
|
msgid "Followers (Channels)" |
||||
|
msgstr "Abonnés (Canaux)" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_partner_ids |
||||
|
msgid "Followers (Partners)" |
||||
|
msgstr "Abonnés (Partenaires)" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_hide_login |
||||
|
msgid "Hide login" |
||||
|
msgstr "Cacher le login" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_hide_password |
||||
|
msgid "Hide password" |
||||
|
msgstr "Cacher le mot de passe" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_hide_port |
||||
|
msgid "Hide port" |
||||
|
msgstr "Cacher le port" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_id |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_id |
||||
|
msgid "ID" |
||||
|
msgstr "Identifiant" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_unread |
||||
|
msgid "If checked new messages require your attention." |
||||
|
msgstr "Si coché, de nouveaux messages demandent votre attention." |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_needaction |
||||
|
msgid "If checked, new messages require your attention." |
||||
|
msgstr "si elle est cochée, de nouveaux messages requièrent votre attention." |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: selection:external.file.task,method_type:0 |
||||
|
msgid "Import" |
||||
|
msgstr "Import" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_move_path |
||||
|
msgid "Imported File will be moved to this path" |
||||
|
msgstr "Le fichier importé sera déplacé dans cet emplacement" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_new_name |
||||
|
msgid "Imported File will be renamed to this nameName can use mako template where obj is an ir_attachement. template exemple : ${obj.name}-${obj.create_date}.csv" |
||||
|
msgstr "Imported File will be renamed to this nameName can use mako template where obj is an ir_attachement. template exemple : ${obj.name}-${obj.create_date}.csv" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_is_follower |
||||
|
msgid "Is Follower" |
||||
|
msgstr "Est un abonné" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_last_post |
||||
|
msgid "Last Message Date" |
||||
|
msgstr "Date du dernier message" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location___last_update |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task___last_update |
||||
|
msgid "Last Modified on" |
||||
|
msgstr "Dernière modification le" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_write_uid |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_write_uid |
||||
|
msgid "Last Updated by" |
||||
|
msgstr "Dernière mise à jour par" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_write_date |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_write_date |
||||
|
msgid "Last Updated on" |
||||
|
msgstr "Dernière mise à jour le" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_location_id |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_location_id |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_location_form |
||||
|
msgid "Location" |
||||
|
msgstr "Emplacement" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_login |
||||
|
msgid "Login" |
||||
|
msgstr "Identifiant" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_md5_check |
||||
|
msgid "Md5 check" |
||||
|
msgstr "Md5 check" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_ids |
||||
|
msgid "Messages" |
||||
|
msgstr "Messages" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_method_type |
||||
|
msgid "Method type" |
||||
|
msgstr "Type de méthode" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_move_path |
||||
|
msgid "Move path" |
||||
|
msgstr "chemin des archives" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_name |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_name |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_location_form |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_form |
||||
|
msgid "Name" |
||||
|
msgstr "Nom" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_task_new_name |
||||
|
msgid "New name" |
||||
|
msgstr "Nouveau nom" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_needaction_counter |
||||
|
msgid "Number of Actions" |
||||
|
msgstr "Nombre d'Actions" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_needaction_counter |
||||
|
msgid "Number of messages which requires an action" |
||||
|
msgstr "Nombre de messages demandant une action" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_ir_attachment_metadata_message_unread_counter |
||||
|
msgid "Number of unread messages" |
||||
|
msgstr "Nombre de messages non lus" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_password |
||||
|
msgid "Password" |
||||
|
msgstr "Mot de passe" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_filepath |
||||
|
msgid "Path to imported/exported file" |
||||
|
msgstr "Path to imported/exported file" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_port |
||||
|
msgid "Port" |
||||
|
msgstr "Port" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_protocol |
||||
|
msgid "Protocol" |
||||
|
msgstr "Protocole" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_form |
||||
|
msgid "Run" |
||||
|
msgstr "Run" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_location_filestore_rootpath |
||||
|
msgid "Server's root path" |
||||
|
msgstr "Chemin racine du serveur" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_task_id |
||||
|
msgid "Task" |
||||
|
msgstr "Tache" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_external_file_location_task_ids |
||||
|
msgid "Task ids" |
||||
|
msgstr "Task ids" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_location_form |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_form |
||||
|
#: model:ir.ui.view,arch_db:external_file_location.view_task_tree |
||||
|
msgid "Tasks" |
||||
|
msgstr "Taches" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,help:external_file_location.field_external_file_task_file_type |
||||
|
msgid "The file type determines an import method to be used to parse and transform data before their import in ERP" |
||||
|
msgstr "Le type de fichier détermine la méthode d'import utilisée pour parser le fichier et transformer les données avant l'import dans l'ERP" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_unread |
||||
|
msgid "Unread Messages" |
||||
|
msgstr "Messages non lus" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model.fields,field_description:external_file_location.field_ir_attachment_metadata_message_unread_counter |
||||
|
msgid "Unread Messages Counter" |
||||
|
msgstr "Compteur de messages non lus" |
||||
|
|
||||
|
#. module: external_file_location |
||||
|
#: model:ir.model,name:external_file_location.model_ir_attachment_metadata |
||||
|
msgid "ir.attachment.metadata" |
||||
|
msgstr "ir.attachment.metadata" |
||||
|
|
@ -1,21 +1,33 @@ |
|||||
# coding: utf-8 |
# coding: utf-8 |
||||
# @ 2015 Valentin CHEMIERE @ Akretion |
|
||||
|
# @ 2016 Florian DA COSTA @ Akretion |
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
from openerp import models, fields |
|
||||
|
from openerp import models, fields, api |
||||
|
import base64 |
||||
|
import os |
||||
|
|
||||
|
|
||||
class IrAttachmentMetadata(models.Model): |
class IrAttachmentMetadata(models.Model): |
||||
_inherit = 'ir.attachment.metadata' |
_inherit = 'ir.attachment.metadata' |
||||
|
|
||||
sync_date = fields.Datetime() |
|
||||
state = fields.Selection([ |
|
||||
('pending', 'Pending'), |
|
||||
('failed', 'Failed'), |
|
||||
('done', 'Done'), |
|
||||
], readonly=False, required=True, default='pending') |
|
||||
state_message = fields.Text() |
|
||||
task_id = fields.Many2one('external.file.task', string='Task') |
task_id = fields.Many2one('external.file.task', string='Task') |
||||
location_id = fields.Many2one( |
location_id = fields.Many2one( |
||||
'external.file.location', string='Location', |
'external.file.location', string='Location', |
||||
related='task_id.location_id', store=True) |
related='task_id.location_id', store=True) |
||||
|
file_type = fields.Selection( |
||||
|
selection_add=[ |
||||
|
('export_external_location', |
||||
|
'Export File (External location)') |
||||
|
]) |
||||
|
|
||||
|
@api.multi |
||||
|
def _run(self): |
||||
|
super(IrAttachmentMetadata, self)._run() |
||||
|
if self.file_type == 'export_external_location': |
||||
|
protocols = self.env['external.file.location']._get_classes() |
||||
|
location = self.location_id |
||||
|
cls = protocols.get(location.protocol)[1] |
||||
|
path = os.path.join(self.task_id.filepath, self.datas_fname) |
||||
|
with cls.connect(location) as conn: |
||||
|
datas = base64.decodestring(self.datas) |
||||
|
conn.setcontents(path, data=datas) |
@ -1,84 +0,0 @@ |
|||||
# coding: utf-8 |
|
||||
# Author: Joel Grand-Guillaume |
|
||||
# Copyright 2011-2012 Camptocamp SA |
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
||||
|
|
||||
|
|
||||
def itersubclasses(cls, _seen=None): |
|
||||
""" |
|
||||
itersubclasses(cls) |
|
||||
Generator over all subclasses of a given class, in depth first order. |
|
||||
>>> list(itersubclasses(int)) == [bool] |
|
||||
True |
|
||||
>>> class A(object): pass |
|
||||
>>> class B(A): pass |
|
||||
>>> class C(A): pass |
|
||||
>>> class D(B,C): pass |
|
||||
>>> class E(D): pass |
|
||||
>>> |
|
||||
>>> for cls in itersubclasses(A): |
|
||||
... print(cls.__name__) |
|
||||
B |
|
||||
D |
|
||||
E |
|
||||
C |
|
||||
>>> # get ALL (new-style) classes currently defined |
|
||||
>>> [cls.__name__ for cls in itersubclasses(object)] #doctest: +ELLIPSIS |
|
||||
['type', ...'tuple', ...] |
|
||||
""" |
|
||||
if not isinstance(cls, type): |
|
||||
raise TypeError('itersubclasses must be called with ' |
|
||||
'new-style classes, not %.100r' % cls |
|
||||
) |
|
||||
if _seen is None: |
|
||||
_seen = set() |
|
||||
try: |
|
||||
subs = cls.__subclasses__() |
|
||||
except TypeError: # fails only when cls is type |
|
||||
subs = cls.__subclasses__(cls) |
|
||||
for sub in subs: |
|
||||
if sub not in _seen: |
|
||||
_seen.add(sub) |
|
||||
yield sub |
|
||||
for sub in itersubclasses(sub, _seen): |
|
||||
yield sub |
|
||||
|
|
||||
|
|
||||
def _get_erp_module_name(module_path): |
|
||||
# see this PR for v9 https://github.com/odoo/odoo/pull/11084 |
|
||||
""" Extract the name of the Odoo module from the path of the |
|
||||
Python module. |
|
||||
|
|
||||
Taken from Odoo server: ``openerp.models.MetaModel`` |
|
||||
|
|
||||
The (Odoo) module name can be in the ``openerp.addons`` namespace |
|
||||
or not. For instance module ``sale`` can be imported as |
|
||||
``openerp.addons.sale`` (the good way) or ``sale`` (for backward |
|
||||
compatibility). |
|
||||
""" |
|
||||
module_parts = module_path.split('.') |
|
||||
if len(module_parts) > 2 and module_parts[:2] == ['openerp', 'addons']: |
|
||||
module_name = module_parts[2] |
|
||||
else: |
|
||||
module_name = module_parts[0] |
|
||||
return module_name |
|
||||
|
|
||||
|
|
||||
def is_module_installed(env, module_name): |
|
||||
""" Check if an Odoo addon is installed. |
|
||||
|
|
||||
:param module_name: name of the addon |
|
||||
""" |
|
||||
# the registry maintains a set of fully loaded modules so we can |
|
||||
# lookup for our module there |
|
||||
return module_name in env.registry._init_modules |
|
||||
|
|
||||
|
|
||||
def get_erp_module(cls_or_func): |
|
||||
""" For a top level function or class, returns the |
|
||||
name of the Odoo module where it lives. |
|
||||
|
|
||||
So we will be able to filter them according to the modules |
|
||||
installation state. |
|
||||
""" |
|
||||
return _get_erp_module_name(cls_or_func.__module__) |
|
@ -1,4 +1,3 @@ |
|||||
from . import abstract_fs |
|
||||
from . import ftp |
from . import ftp |
||||
from . import sftp |
from . import sftp |
||||
from . import filestore |
from . import filestore |
@ -1,196 +0,0 @@ |
|||||
# coding: utf-8 |
|
||||
# Copyright (C) 2014 initOS GmbH & Co. KG (<http://www.initos.com>). |
|
||||
# @ 2015 Valentin CHEMIERE @ Akretion |
|
||||
# ©2016 @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> |
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
||||
import logging |
|
||||
import os |
|
||||
import fnmatch |
|
||||
import datetime |
|
||||
|
|
||||
from openerp import tools |
|
||||
|
|
||||
from .abstract_task import AbstractTask |
|
||||
|
|
||||
_logger = logging.getLogger(__name__) |
|
||||
|
|
||||
|
|
||||
try: |
|
||||
# We use a jinja2 sandboxed environment to render mako templates. |
|
||||
# Note that the rendering does not cover all the mako syntax, in particular |
|
||||
# arbitrary Python statements are not accepted, and not all expressions are |
|
||||
# allowed: only "public" attributes (not starting with '_') of objects may |
|
||||
# be accessed. |
|
||||
# This is done on purpose: it prevents incidental or malicious execution of |
|
||||
# Python code that may break the security of the server. |
|
||||
from jinja2.sandbox import SandboxedEnvironment |
|
||||
mako_template_env = SandboxedEnvironment( |
|
||||
variable_start_string="${", |
|
||||
variable_end_string="}", |
|
||||
line_statement_prefix="%", |
|
||||
trim_blocks=True, # do not output newline after blocks |
|
||||
) |
|
||||
mako_template_env.globals.update({ |
|
||||
'str': str, |
|
||||
'datetime': datetime, |
|
||||
'len': len, |
|
||||
'abs': abs, |
|
||||
'min': min, |
|
||||
'max': max, |
|
||||
'sum': sum, |
|
||||
'filter': filter, |
|
||||
'reduce': reduce, |
|
||||
'map': map, |
|
||||
'round': round, |
|
||||
}) |
|
||||
except ImportError: |
|
||||
_logger.warning("jinja2 not available, templating features will not work!") |
|
||||
|
|
||||
|
|
||||
class AbstractFSTask(AbstractTask): |
|
||||
|
|
||||
_name = None |
|
||||
_key = None |
|
||||
_synchronize_type = None |
|
||||
_default_port = None |
|
||||
|
|
||||
def __init__(self, env, config): |
|
||||
self.env = env |
|
||||
self.host = config.get('host', '') |
|
||||
self.user = config.get('user', '') |
|
||||
self.pwd = config.get('pwd', '') |
|
||||
self.port = config.get('port', '') |
|
||||
self.allow_dir_creation = config.get('allow_dir_creation', '') |
|
||||
self.file_name = config.get('file_name', '') |
|
||||
self.path = config.get('path') or '.' |
|
||||
self.move_path = config.get('move_path', '') |
|
||||
self.new_name = config.get('new_name', '') |
|
||||
self.after_import = config.get('after_import', False) |
|
||||
self.file_type = config.get('file_type', False) |
|
||||
self.attachment_ids = config.get('attachment_ids', False) |
|
||||
self.task = config.get('task', False) |
|
||||
self.ext_hash = False |
|
||||
self.md5_check = config.get('md5_check', False) |
|
||||
|
|
||||
def _handle_new_source(self, fs_conn, download_directory, file_name, |
|
||||
move_directory): |
|
||||
"""open and read given file into create_file method, |
|
||||
move file if move_directory is given""" |
|
||||
with fs_conn.open(self._source_name(download_directory, file_name), |
|
||||
"rb") as fileobj: |
|
||||
data = fileobj.read() |
|
||||
return self.create_file(file_name, data) |
|
||||
|
|
||||
def _source_name(self, download_directory, file_name): |
|
||||
"""helper to get the full name""" |
|
||||
return os.path.join(download_directory, file_name) |
|
||||
|
|
||||
def _move_file(self, fs_conn, source, target): |
|
||||
"""Moves a file on the server""" |
|
||||
_logger.info('Moving file %s %s' % (source, target)) |
|
||||
fs_conn.rename(source, target) |
|
||||
if self.md5_check: |
|
||||
fs_conn.rename(source + '.md5', target + '.md5') |
|
||||
|
|
||||
def _delete_file(self, fs_conn, source): |
|
||||
"""Deletes a file from the server""" |
|
||||
_logger.info('Deleting file %s' % source) |
|
||||
fs_conn.remove(source) |
|
||||
if self.md5_check: |
|
||||
fs_conn.remove(source + '.md5') |
|
||||
|
|
||||
def _get_hash(self, file_name, fs_conn): |
|
||||
hash_file_name = file_name + '.md5' |
|
||||
with fs_conn.open(hash_file_name, 'rb') as f: |
|
||||
return f.read().rstrip('\r\n') |
|
||||
|
|
||||
def _get_files(self, conn, path): |
|
||||
process_files = [] |
|
||||
files_list = conn.listdir(path) |
|
||||
pattern = self.file_name |
|
||||
for file_name in fnmatch.filter(files_list, pattern): |
|
||||
source_name = self._source_name(self.path, file_name) |
|
||||
process_files.append((file_name, source_name)) |
|
||||
return process_files |
|
||||
|
|
||||
def _template_render(self, template, record): |
|
||||
try: |
|
||||
template = mako_template_env.from_string(tools.ustr(template)) |
|
||||
except Exception: |
|
||||
_logger.exception("Failed to load template %r", template) |
|
||||
|
|
||||
variables = {'obj': record} |
|
||||
try: |
|
||||
render_result = template.render(variables) |
|
||||
except Exception: |
|
||||
_logger.exception( |
|
||||
"Failed to render template %r using values %r" % |
|
||||
(template, variables)) |
|
||||
render_result = u"" |
|
||||
if render_result == u"False": |
|
||||
render_result = u"" |
|
||||
return render_result |
|
||||
|
|
||||
def _process_file(self, conn, file_to_process): |
|
||||
if self.md5_check: |
|
||||
self.ext_hash = self._get_hash(file_to_process[1], conn) |
|
||||
att_id = self._handle_new_source( |
|
||||
conn, |
|
||||
self.path, |
|
||||
self.file_name, |
|
||||
self.move_path) |
|
||||
move = False |
|
||||
rename = False |
|
||||
if self.after_import: |
|
||||
move = 'move' in self.after_import |
|
||||
rename = 'rename' in self.after_import |
|
||||
|
|
||||
# Move/rename/delete files only after all |
|
||||
# files have been processed. |
|
||||
if self.after_import == 'delete': |
|
||||
self._delete_file(conn, file_to_process[1]) |
|
||||
elif rename or move: |
|
||||
new_name = file_to_process[0] |
|
||||
if rename and self.new_name: |
|
||||
new_name_render = self._template_render( |
|
||||
self.new_name, att_id) |
|
||||
if new_name_render: |
|
||||
# Avoid space in file name |
|
||||
new_name = new_name_render.replace(' ', '_') |
|
||||
if self.move_path and not conn.exists(self.move_path): |
|
||||
conn.makedir(self.move_path) |
|
||||
move_path = self.move_path if self.move_path else self.path |
|
||||
self._move_file( |
|
||||
conn, |
|
||||
file_to_process[1], |
|
||||
self._source_name(move_path, new_name)) |
|
||||
return att_id |
|
||||
|
|
||||
def _handle_existing_target(self, fs_conn, target_name, filedata): |
|
||||
raise Exception("%s already exists" % target_name) |
|
||||
|
|
||||
def _handle_new_target(self, fs_conn, target_name, filedata): |
|
||||
try: |
|
||||
with fs_conn.open(target_name, mode='wb') as fileobj: |
|
||||
fileobj.write(filedata) |
|
||||
_logger.info('wrote %s, size %d', target_name, len(filedata)) |
|
||||
self.attachment_id.state = 'done' |
|
||||
self.attachment_id.state_message = '' |
|
||||
except IOError: |
|
||||
self.attachment_id.state = 'failed' |
|
||||
self.attachment_id.state_message = ( |
|
||||
'The directory doesn\'t exist or had insufficient rights') |
|
||||
|
|
||||
def _target_name(self, fs_conn, upload_directory, filename): |
|
||||
return os.path.join(upload_directory, filename) |
|
||||
|
|
||||
def _upload_file(self, conn, host, port, user, pwd, |
|
||||
path, filename, filedata): |
|
||||
upload_directory = path or '.' |
|
||||
target_name = self._target_name(conn, |
|
||||
upload_directory, |
|
||||
filename) |
|
||||
if conn.isfile(target_name): |
|
||||
self._handle_existing_target(conn, target_name, filedata) |
|
||||
else: |
|
||||
self._handle_new_target(conn, target_name, filedata) |
|
@ -1,28 +0,0 @@ |
|||||
# coding: utf-8 |
|
||||
# @ 2015 Valentin CHEMIERE @ Akretion |
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
||||
|
|
||||
from base64 import b64encode |
|
||||
|
|
||||
|
|
||||
class AbstractTask(object): |
|
||||
|
|
||||
_name = None |
|
||||
_key = None |
|
||||
_synchronize_type = None |
|
||||
_default_port = None |
|
||||
_hide_login = False |
|
||||
_hide_password = False |
|
||||
_hide_port = False |
|
||||
|
|
||||
def create_file(self, filename, data): |
|
||||
ir_attachment_id = self.env['ir.attachment.metadata'].create({ |
|
||||
'name': filename, |
|
||||
'datas': b64encode(data), |
|
||||
'datas_fname': filename, |
|
||||
'task_id': self.task and self.task.id or False, |
|
||||
'location_id': self.task and self.task.location_id.id or False, |
|
||||
'external_hash': self.ext_hash, |
|
||||
'file_type': self.file_type, |
|
||||
}) |
|
||||
return ir_attachment_id |
|
@ -1,53 +1,27 @@ |
|||||
# coding: utf-8 |
# coding: utf-8 |
||||
# Copyright (C) 2014 initOS GmbH & Co. KG (<http://www.initos.com>). |
|
||||
# @ 2015 Valentin CHEMIERE @ Akretion |
|
||||
|
# @ 2016 Florian DA COSTA @ Akretion |
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
from .abstract_fs import AbstractFSTask |
|
||||
from base64 import b64decode |
|
||||
from fs import osfs |
|
||||
import logging |
import logging |
||||
_logger = logging.getLogger(__name__) |
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
try: |
||||
|
from fs import osfs |
||||
|
except ImportError: |
||||
|
_logger.debug('Cannot `import fs`.') |
||||
|
|
||||
class FileStoreTask(AbstractFSTask): |
|
||||
|
|
||||
|
class FileStoreTask(osfs.OSFS): |
||||
|
|
||||
_key = 'filestore' |
_key = 'filestore' |
||||
_name = 'File Store' |
_name = 'File Store' |
||||
_synchronize_type = None |
|
||||
_default_port = None |
_default_port = None |
||||
_hide_login = True |
_hide_login = True |
||||
_hide_password = True |
_hide_password = True |
||||
_hide_port = True |
_hide_port = True |
||||
|
|
||||
|
|
||||
class FileStoreImportTask(FileStoreTask): |
|
||||
|
|
||||
_synchronize_type = 'import' |
|
||||
|
|
||||
def run(self): |
|
||||
att_ids = [] |
|
||||
with osfs.OSFS(self.host) as fs_conn: |
|
||||
files_to_process = self._get_files(fs_conn, self.path) |
|
||||
for file_to_process in files_to_process: |
|
||||
att_ids.append(self._process_file(fs_conn, file_to_process)) |
|
||||
return att_ids |
|
||||
|
|
||||
|
|
||||
class FileStoreExportTask(FileStoreTask): |
|
||||
|
|
||||
_synchronize_type = 'export' |
|
||||
|
|
||||
def run(self, async=True): |
|
||||
for attachment in self.attachment_ids: |
|
||||
if attachment.state in ('pending', 'failed'): |
|
||||
self.attachment_id = attachment |
|
||||
with osfs.OSFS(self.host) as fs_conn: |
|
||||
self._upload_file(fs_conn, |
|
||||
self.host, |
|
||||
self.port, |
|
||||
self.user, |
|
||||
self.pwd, |
|
||||
self.path, |
|
||||
attachment.datas_fname, |
|
||||
b64decode(attachment.datas)) |
|
||||
|
@staticmethod |
||||
|
def connect(location): |
||||
|
rootpath = location.filestore_rootpath or '/' |
||||
|
conn = FileStoreTask(rootpath) |
||||
|
return conn |
@ -1,51 +1,30 @@ |
|||||
# coding: utf-8 |
# coding: utf-8 |
||||
# Copyright (C) 2014 initOS GmbH & Co. KG (<http://www.initos.com>). |
|
||||
# @ 2015 Valentin CHEMIERE @ Akretion |
|
||||
|
# @ 2016 Florian DA COSTA @ Akretion |
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
|
||||
from .abstract_fs import AbstractFSTask |
|
||||
from base64 import b64decode |
|
||||
from fs import ftpfs |
|
||||
import logging |
import logging |
||||
_logger = logging.getLogger(__name__) |
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
try: |
||||
|
from fs import ftpfs |
||||
|
except ImportError: |
||||
|
_logger.debug('Cannot `import fs`.') |
||||
|
|
||||
class FtpTask(AbstractFSTask): |
|
||||
|
|
||||
_key = 'ftp' |
|
||||
_name = 'FTP' |
|
||||
|
class FtpTask(ftpfs.FTPFS): |
||||
|
|
||||
|
_key = 'sftp' |
||||
|
_name = 'SFTP' |
||||
_synchronize_type = None |
_synchronize_type = None |
||||
_default_port = 21 |
|
||||
|
_default_port = 22 |
||||
_hide_login = False |
_hide_login = False |
||||
_hide_password = False |
_hide_password = False |
||||
_hide_port = False |
_hide_port = False |
||||
|
|
||||
|
|
||||
class FtpImportTask(FtpTask): |
|
||||
|
|
||||
_synchronize_type = 'import' |
|
||||
|
|
||||
def run(self): |
|
||||
att_ids = [] |
|
||||
with ftpfs.FTPFS( |
|
||||
self.host, self.user, self.pwd, port=self.port) as ftp_conn: |
|
||||
files_to_process = self._get_files(ftp_conn, self.path) |
|
||||
for file_to_process in files_to_process: |
|
||||
att_ids.append(self._process_file(ftp_conn, file_to_process)) |
|
||||
return att_ids |
|
||||
|
|
||||
|
|
||||
class FtpExportTask(FtpTask): |
|
||||
|
|
||||
_synchronize_type = 'export' |
|
||||
|
|
||||
def run(self, async=True): |
|
||||
for attachment in self.attachment_ids: |
|
||||
if attachment.state in ('pending', 'failed'): |
|
||||
self.attachment_id = attachment |
|
||||
with ftpfs.FTPFS(self.host, self.user, self.pwd, |
|
||||
port=self.port) as ftp_conn: |
|
||||
self._upload_file(ftp_conn, self.host, self.port, |
|
||||
self.user, self.pwd, self.path, |
|
||||
attachment.datas_fname, |
|
||||
b64decode(attachment.datas)) |
|
||||
|
@staticmethod |
||||
|
def connect(location): |
||||
|
conn = FtpTask(location.address, |
||||
|
location.login, |
||||
|
location.password, |
||||
|
location.port) |
||||
|
return conn |
@ -1,2 +1,4 @@ |
|||||
from . import mock_server |
from . import mock_server |
||||
|
from . import test_ftp |
||||
from . import test_sftp |
from . import test_sftp |
||||
|
from . import test_filestore |
@ -0,0 +1,32 @@ |
|||||
|
# coding: utf-8 |
||||
|
# @ 2016 Florian da Costa @ Akretion |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
import openerp.tests.common as common |
||||
|
from openerp import api |
||||
|
from StringIO import StringIO |
||||
|
|
||||
|
|
||||
|
class ContextualStringIO(StringIO): |
||||
|
""" |
||||
|
snippet from http://bit.ly/1HfH6uW (stackoverflow) |
||||
|
""" |
||||
|
|
||||
|
def __enter__(self): |
||||
|
return self |
||||
|
|
||||
|
def __exit__(self, *args): |
||||
|
self.close() |
||||
|
return False |
||||
|
|
||||
|
|
||||
|
class TestConnection(common.TransactionCase): |
||||
|
|
||||
|
def setUp(self): |
||||
|
super(TestConnection, self).setUp() |
||||
|
self.registry.enter_test_mode() |
||||
|
self.env = api.Environment(self.registry.test_cr, self.env.uid, |
||||
|
self.env.context) |
||||
|
|
||||
|
def tearDown(self): |
||||
|
self.registry.leave_test_mode() |
||||
|
super(TestConnection, self).tearDown() |
@ -0,0 +1,50 @@ |
|||||
|
# coding: utf-8 |
||||
|
# @ 2015 Valentin CHEMIERE @ Akretion |
||||
|
# ©2016 @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
import logging |
||||
|
from base64 import b64decode |
||||
|
from .common import TestConnection, ContextualStringIO |
||||
|
from .mock_server import server_mock_filestore |
||||
|
|
||||
|
|
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
class TestfilestoreConnection(TestConnection): |
||||
|
|
||||
|
def setUp(self): |
||||
|
super(TestfilestoreConnection, self).setUp() |
||||
|
self.test_file_filestore = ContextualStringIO() |
||||
|
self.test_file_filestore.write('import filestore') |
||||
|
self.test_file_filestore.seek(0) |
||||
|
|
||||
|
def test_00_filestore_import(self): |
||||
|
self.task = self.env.ref( |
||||
|
'external_file_location.filestore_import_task') |
||||
|
with server_mock_filestore( |
||||
|
{'open': self.test_file_filestore, |
||||
|
'listdir': ['test-import-filestore.txt']}): |
||||
|
self.task.run_import() |
||||
|
search_file = self.env['ir.attachment.metadata'].search( |
||||
|
[('name', '=', 'test-import-filestore.txt')]) |
||||
|
self.assertEqual(len(search_file), 1) |
||||
|
self.assertEqual(b64decode(search_file[0].datas), 'import filestore') |
||||
|
|
||||
|
def test_01_filestore_export(self): |
||||
|
self.task = self.env.ref( |
||||
|
'external_file_location.filestore_export_task') |
||||
|
self.filestore_attachment = self.env.ref( |
||||
|
'external_file_location.ir_attachment_export_file_filestore') |
||||
|
with server_mock_filestore( |
||||
|
{'setcontents': ''}) as Fakefilestore: |
||||
|
self.task.run_export() |
||||
|
if Fakefilestore: |
||||
|
self.assertEqual('setcontents', Fakefilestore[-1]['method']) |
||||
|
self.assertEqual('done', self.filestore_attachment.state) |
||||
|
self.assertEqual( |
||||
|
'/home/user/test/filestore_test_export.txt', |
||||
|
Fakefilestore[-1]['args'][0]) |
||||
|
self.assertEqual( |
||||
|
'test filestore file export', |
||||
|
Fakefilestore[-1]['kwargs']['data']) |
@ -0,0 +1,86 @@ |
|||||
|
# coding: utf-8 |
||||
|
# @ 2015 Valentin CHEMIERE @ Akretion |
||||
|
# ©2016 @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> |
||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
||||
|
import logging |
||||
|
from base64 import b64decode |
||||
|
import hashlib |
||||
|
from .common import TestConnection, ContextualStringIO |
||||
|
from .mock_server import server_mock_ftp |
||||
|
from .mock_server import MultiResponse |
||||
|
from openerp.exceptions import UserError |
||||
|
|
||||
|
|
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
|
||||
|
class TestFtpConnection(TestConnection): |
||||
|
|
||||
|
def setUp(self): |
||||
|
super(TestFtpConnection, self).setUp() |
||||
|
self.test_file_ftp = ContextualStringIO() |
||||
|
self.test_file_ftp.write('import ftp') |
||||
|
self.test_file_ftp.seek(0) |
||||
|
|
||||
|
def test_00_ftp_import(self): |
||||
|
self.task = self.env.ref('external_file_location.ftp_import_task') |
||||
|
with server_mock_ftp( |
||||
|
{'open': self.test_file_ftp, |
||||
|
'listdir': ['test-import-ftp.txt']}): |
||||
|
self.task.run_import() |
||||
|
search_file = self.env['ir.attachment.metadata'].search( |
||||
|
[('name', '=', 'test-import-ftp.txt')]) |
||||
|
self.assertEqual(len(search_file), 1) |
||||
|
self.assertEqual(b64decode(search_file[0].datas), 'import ftp') |
||||
|
|
||||
|
def test_01_ftp_export(self): |
||||
|
self.task = self.env.ref('external_file_location.ftp_export_task') |
||||
|
self.ftp_attachment = self.env.ref( |
||||
|
'external_file_location.ir_attachment_export_file_ftp') |
||||
|
with server_mock_ftp( |
||||
|
{'setcontents': ''}) as FakeFTP: |
||||
|
self.task.run_export() |
||||
|
if FakeFTP: |
||||
|
self.assertEqual('setcontents', FakeFTP[-1]['method']) |
||||
|
self.assertEqual('done', self.ftp_attachment.state) |
||||
|
self.assertEqual( |
||||
|
'/home/user/test/ftp_test_export.txt', |
||||
|
FakeFTP[-1]['args'][0]) |
||||
|
self.assertEqual( |
||||
|
'test ftp file export', |
||||
|
FakeFTP[-1]['kwargs']['data']) |
||||
|
|
||||
|
def test_02_ftp_import_md5(self): |
||||
|
md5_file = ContextualStringIO() |
||||
|
md5_file.write(hashlib.md5('import ftp').hexdigest()) |
||||
|
md5_file.seek(0) |
||||
|
task = self.env.ref('external_file_location.ftp_import_task') |
||||
|
task.md5_check = True |
||||
|
with server_mock_ftp( |
||||
|
{'open': MultiResponse({ |
||||
|
1: md5_file, |
||||
|
0: self.test_file_ftp}), |
||||
|
'listdir': [task.filename]}) as Fakeftp: |
||||
|
task.run_import() |
||||
|
search_file = self.env['ir.attachment.metadata'].search( |
||||
|
(('name', '=', task.filename),)) |
||||
|
self.assertEqual(len(search_file), 1) |
||||
|
self.assertEqual(b64decode(search_file[0].datas), |
||||
|
'import ftp') |
||||
|
self.assertEqual('open', Fakeftp[-1]['method']) |
||||
|
self.assertEqual(hashlib.md5('import ftp').hexdigest(), |
||||
|
search_file.external_hash) |
||||
|
|
||||
|
def test_03_ftp_import_md5_corrupt_file(self): |
||||
|
md5_file = ContextualStringIO() |
||||
|
md5_file.write(hashlib.md5('import test ftp corrupted').hexdigest()) |
||||
|
md5_file.seek(0) |
||||
|
task = self.env.ref('external_file_location.ftp_import_task') |
||||
|
task.md5_check = True |
||||
|
with server_mock_ftp( |
||||
|
{'open': MultiResponse({ |
||||
|
1: md5_file, |
||||
|
0: self.test_file_ftp}), |
||||
|
'listdir': [task.filename]}): |
||||
|
with self.assertRaises(UserError): |
||||
|
task.run_import() |
Write
Preview
Loading…
Cancel
Save
Reference in new issue