-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
8 add module external_file_location #377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
b95fb37
Add file import/export
2e47b90
POC external_file_location integration in connector_flow
136a29d
[FIX] encode to utf8 user/password
bealdav 4075ae2
[FIX] prevent not installed class been evaluated
bealdav 85ff3cc
[FIX] python header in akretion modules
bealdav 4247a7f
[FIX] check if class is in installed module, README
bealdav c9a0449
[FIX] travis for dependency on a PR
bealdav 6ea558f
[WIP] Use pattern in file name & add move and rename file option
mourad-ehm 3a453b5
[WIP] use jinja to render new file name based on simple template
mourad-ehm fcd3cff
[FIX] bug move and rename file option
mourad-ehm 763c8db
[ADD] add file type on task
mourad-ehm d7d3702
[FIX] add file type in attachemnt create method
mourad-ehm 3425043
[WIP] test rename file
mourad-ehm e00298e
[WIP] inherit view from attachment_metadata
mourad-ehm c4d4ce7
[FIX] bug of inherit view and menu from attachment_metadata
mourad-ehm 9fcf565
[IMP] add move & rename test to test_sftp
mourad-ehm b216db2
[IMP] move file location menu inside automation menu and rename it
mourad-ehm 272e566
[IMP] add ir.model.access manager rule
mourad-ehm 61e2727
[FIX] typing mistake
mourad-ehm 21d9136
[FIX] access file store without login/password
mourad-ehm 46ec646
[IMP] reorganize task form view
mourad-ehm db6dfd0
[FIX] api.multi in task run method
mourad-ehm c7668b9
[FIX] OCA guidelines
mourad-ehm 20fee96
[FIX] add authors and contributors
mourad-ehm 4d9c30d
[FIX] fix pylint
mourad-ehm 489fe45
[FIX] add oca_dependencies & fixe oca version format
mourad-ehm 6c29cec
[FIX] fix pylint & oca_dependencies
mourad-ehm 58ba998
[FIX] python fs lib to travis
mourad-ehm ef7c158
[FIX] fix views path
mourad-ehm 0bea727
[FIX] set application to False
mourad-ehm d88b06a
[FIX] Move fs lib from travis.yml to requirements.txt
mourad-ehm f99e8c6
[FIX] delete akretion branch from travis.yml
mourad-ehm 9890697
[FIX] Remove dependency to Akretion repository in oca_dependencies.txt
mourad-ehm 54a4a4f
[FIX] fs lib name
mourad-ehm 1a57029
[FIX] fs lib name
mourad-ehm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg | ||
| :alt: License | ||
|
|
||
| External File Location | ||
| ====================== | ||
|
|
||
| This module was written to extend the functionality of ir.attachment to support remote communication and allow you to import/export file to a remote server | ||
|
|
||
| Installation | ||
| ============ | ||
|
|
||
| To install this module, you need to: | ||
|
|
||
| * fs python module | ||
| * Paramiko python module | ||
|
|
||
| Usage | ||
| ===== | ||
|
|
||
| To use this module, you need to: | ||
|
|
||
| * Add a location with your server infos | ||
| * Create a task with your file info and remote communication method | ||
| * A cron task will trigger each task | ||
|
|
||
| For further information, please visit: | ||
|
|
||
| * https://www.odoo.com/forum/help-1 | ||
|
|
||
| Known issues / Roadmap | ||
| ====================== | ||
|
|
||
|
|
||
| Credits | ||
| ======= | ||
|
|
||
| * Joel Grand-Guillaume Camptocamp | ||
| * initOS <http://initos.com> | ||
| * Valentin CHEMIERE <valentin.chemiere@akretion.com> | ||
| * Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> | ||
|
|
||
|
|
||
| Contributors | ||
| ------------ | ||
|
|
||
| * Sebastien BEAU <sebastian.beau@akretion.com> | ||
| * David BEAL <david.beal@akretion.com> | ||
|
|
||
| Maintainer | ||
| ---------- | ||
|
|
||
| .. image:: http://odoo-community.org/logo.png | ||
| :alt: Odoo Community Association | ||
| :target: http://odoo-community.org | ||
|
|
||
| This module is maintained by the OCA. | ||
|
|
||
| OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. | ||
|
|
||
| To contribute to this module, please visit http://odoo-community.org. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| from . import models | ||
| from . import tasks | ||
| from . import tests |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # 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). | ||
|
|
||
| { | ||
| 'name': 'external_file_location', | ||
| 'version': '8.0.1.0.0', | ||
| 'author': 'Akretion,Odoo Community Association (OCA)', | ||
| 'website': 'www.akretion.com', | ||
| 'license': 'AGPL-3', | ||
| 'category': 'Generic Modules', | ||
| 'depends': [ | ||
| 'attachment_metadata', | ||
| ], | ||
| 'external_dependencies': { | ||
| 'python': [ | ||
| 'fs', | ||
| 'paramiko', | ||
| ], | ||
| }, | ||
| 'data': [ | ||
| 'views/menu.xml', | ||
| 'views/attachment_view.xml', | ||
| 'views/location_view.xml', | ||
| 'views/task_view.xml', | ||
| 'data/cron.xml', | ||
| 'security/ir.model.access.csv', | ||
| ], | ||
| 'installable': True, | ||
| 'application': False, | ||
| 'images': [], | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <openerp> | ||
| <data> | ||
|
|
||
| <record model="ir.cron" id="cronjob_run_exchange_tasks"> | ||
| <field name='name'>Run file exchange tasks</field> | ||
| <field name='interval_number'>30</field> | ||
| <field name='interval_type'>minutes</field> | ||
| <field name="numbercall">-1</field> | ||
| <field name="active">True</field> | ||
| <field name="doall" eval="False" /> | ||
| <field name="model">external.file.task</field> | ||
| <field name="function">_run</field> | ||
| <field name="args">([])</field> | ||
| </record> | ||
|
|
||
| </data> | ||
| </openerp> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| from . import attachment | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. license? |
||
| from . import location | ||
| from . import task | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # coding: utf-8 | ||
| # @ 2015 Valentin CHEMIERE @ Akretion | ||
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
|
||
| from openerp import models, fields | ||
|
|
||
|
|
||
| class IrAttachmentMetadata(models.Model): | ||
| _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') | ||
| location_id = fields.Many2one( | ||
| 'external.file.location', string='Location', | ||
| related='task_id.location_id', store=True) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| # 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__) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # coding: utf-8 | ||
| # @ 2015 Valentin CHEMIERE @ Akretion | ||
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
|
||
| from openerp import models, fields, api | ||
| from ..tasks.abstract_task import AbstractTask | ||
| from .helper import itersubclasses | ||
|
|
||
|
|
||
| class Location(models.Model): | ||
| _name = 'external.file.location' | ||
| _description = 'Description' | ||
|
|
||
| name = fields.Char(string='Name', required=True) | ||
| protocol = fields.Selection(selection='_get_protocol', required=True) | ||
| address = fields.Char(string='Address', required=True) | ||
| port = fields.Integer() | ||
| login = fields.Char() | ||
| password = fields.Char() | ||
| task_ids = fields.One2many('external.file.task', 'location_id') | ||
| hide_login = fields.Boolean() | ||
| hide_password = fields.Boolean() | ||
| hide_port = fields.Boolean() | ||
|
|
||
| def _get_protocol(self): | ||
| res = [] | ||
| for cls in itersubclasses(AbstractTask): | ||
| if not cls._synchronize_type: | ||
| cls_info = (cls._key, cls._name) | ||
| res.append(cls_info) | ||
| elif not cls._synchronize_type and cls._key and cls._name: | ||
| pass | ||
| return res | ||
|
|
||
| @api.onchange('protocol') | ||
| def onchange_protocol(self): | ||
| for cls in itersubclasses(AbstractTask): | ||
| if cls._key == self.protocol: | ||
| self.port = cls._default_port | ||
| if cls._hide_login: | ||
| self.hide_login = True | ||
| else: | ||
| self.hide_login = False | ||
| if cls._hide_password: | ||
| self.hide_password = True | ||
| else: | ||
| self.hide_password = False | ||
| if cls._hide_port: | ||
| self.hide_port = True | ||
| else: | ||
| self.hide_port = False |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| # coding: utf-8 | ||
| # @ 2015 Valentin CHEMIERE @ Akretion | ||
| # © @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> | ||
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
|
||
| from openerp import models, fields, api | ||
| from .helper import itersubclasses, get_erp_module, is_module_installed | ||
| from ..tasks.abstract_task import AbstractTask | ||
|
|
||
|
|
||
| class Task(models.Model): | ||
| _name = 'external.file.task' | ||
| _description = 'External file task' | ||
|
|
||
| name = fields.Char(required=True) | ||
| method = fields.Selection(selection='_get_method', required=True, | ||
| help='procotol and trasmitting info') | ||
| method_type = fields.Char() | ||
| filename = fields.Char(help='File name which is imported.' | ||
| 'You can use file pattern like *.txt' | ||
| 'to import all txt files') | ||
| filepath = fields.Char(help='Path to imported file') | ||
| location_id = fields.Many2one('external.file.location', string='Location', | ||
| required=True) | ||
| attachment_ids = fields.One2many('ir.attachment.metadata', 'task_id', | ||
| string='Attachment') | ||
| move_path = fields.Char(string='Move path', | ||
| help='Imported File will be moved to this path') | ||
| new_name = fields.Char(string='New name', | ||
| help='Imported File will be renamed to this name' | ||
| 'Name can use mako template where obj is an ' | ||
| 'ir_attachement. template exemple : ' | ||
| ' ${obj.name}-${obj.create_date}.csv') | ||
| md5_check = fields.Boolean(help='Control file integrity after import with' | ||
| ' a md5 file') | ||
| after_import = fields.Selection(selection='_get_action', | ||
| help='Action after import a file') | ||
| file_type = fields.Selection( | ||
| selection="_get_file_type", | ||
| string="File type", | ||
| help="The file type determines an import method to be used " | ||
| "to parse and transform data before their import in ERP") | ||
|
|
||
| def _get_action(self): | ||
| return [('rename', 'Rename'), | ||
| ('move', 'Move'), | ||
| ('move_rename', 'Move & Rename'), | ||
| ('delete', 'Delete'), | ||
| ] | ||
|
|
||
| def _get_file_type(self): | ||
| """This is the method to be inherited for adding file types | ||
| The basic import do not apply any parsing or transform of the file. | ||
| The file is just added as an attachement | ||
| """ | ||
| return [('basic_import', 'Basic import')] | ||
|
|
||
| def _get_method(self): | ||
| res = [] | ||
| for cls in itersubclasses(AbstractTask): | ||
| if not is_module_installed(self.env, get_erp_module(cls)): | ||
| continue | ||
| if cls._synchronize_type and ( | ||
| 'protocol' not in self._context or | ||
| cls._key == self._context['protocol']): | ||
| cls_info = (cls._key + '_' + cls._synchronize_type, | ||
| cls._name + ' ' + cls._synchronize_type) | ||
| res.append(cls_info) | ||
| return res | ||
|
|
||
| @api.onchange('method') | ||
| def onchange_method(self): | ||
| if self.method: | ||
| if 'import' in self.method: | ||
| self.method_type = 'import' | ||
| elif 'export' in self.method: | ||
| self.method_type = 'export' | ||
|
|
||
| @api.model | ||
| def _run(self, domain=None): | ||
| if not domain: | ||
| domain = [] | ||
| tasks = self.env['external.file.task'].search(domain) | ||
| tasks.run() | ||
|
|
||
| @api.multi | ||
| def run(self): | ||
| for tsk in self: | ||
| for cls in itersubclasses(AbstractTask): | ||
| if not is_module_installed(self.env, get_erp_module(cls)): | ||
| continue | ||
| cls_build = '%s_%s' % (cls._key, cls._synchronize_type) | ||
| if cls._synchronize_type and cls_build == tsk.method: | ||
| method_class = cls | ||
| config = { | ||
| 'host': tsk.location_id.address, | ||
| # ftplib does not support unicode | ||
| 'user': tsk.location_id.login and\ | ||
| tsk.location_id.login.encode('utf-8'), | ||
| 'pwd': tsk.location_id.password and \ | ||
| tsk.location_id.password.encode('utf-8'), | ||
| 'port': tsk.location_id.port, | ||
| 'allow_dir_creation': False, | ||
| 'file_name': tsk.filename, | ||
| 'path': tsk.filepath, | ||
| 'attachment_ids': tsk.attachment_ids, | ||
| 'task': tsk, | ||
| 'move_path': tsk.move_path, | ||
| 'new_name': tsk.new_name, | ||
| 'after_import': tsk.after_import, | ||
| 'file_type': tsk.file_type, | ||
| 'md5_check': tsk.md5_check, | ||
| } | ||
| conn = method_class(self.env, config) | ||
| conn.run() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add
paramikoto requirements.txt too?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see it being used in the code actually. Looks like the depends are now
ftplibandpysftp